package at.procon.eventhub.importing.persistence;

import at.procon.eventhub.dto.AcquisitionStrategy;
import at.procon.eventhub.dto.EventFamily;
import at.procon.eventhub.dto.ImportCursorStateDto;
import at.procon.eventhub.importing.ExtractionBatchResult;
import java.util.UUID;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class ImportCursorRepository {

    private final JdbcTemplate jdbcTemplate;

    public ImportCursorRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public ImportCursorStateDto findCursor(
            String tenantKey,
            int eventSourceId,
            String scopeHash,
            EventFamily eventFamily,
            String sourceKind,
            AcquisitionStrategy strategy
    ) {
        return jdbcTemplate.query(
                """
                select last_source_package_imported_at,
                       last_source_package_id,
                       last_source_row_updated_at,
                       last_occurred_to
                from eventhub.import_cursor
                where tenant_key = ?
                  and event_source_id = ?
                  and scope_hash = ?
                  and event_family = ?
                  and source_kind = ?
                  and cursor_type = ?
                """,
                rs -> rs.next()
                        ? new ImportCursorStateDto(
                                rs.getObject("last_source_package_imported_at", java.time.OffsetDateTime.class),
                                rs.getString("last_source_package_id"),
                                rs.getObject("last_source_row_updated_at", java.time.OffsetDateTime.class),
                                rs.getObject("last_occurred_to", java.time.OffsetDateTime.class)
                        )
                        : null,
                tenantKey,
                eventSourceId,
                scopeHash,
                eventFamily.name(),
                sourceKind,
                strategy.name()
        );
    }

    public ImportCursorStateDto findLatestCursor(
            String tenantKey,
            int eventSourceId,
            String scopeHash,
            EventFamily eventFamily,
            String sourceKind
    ) {
        return jdbcTemplate.query(
                """
                select last_source_package_imported_at,
                       last_source_package_id,
                       last_source_row_updated_at,
                       last_occurred_to
                from eventhub.import_cursor
                where tenant_key = ?
                  and event_source_id = ?
                  and scope_hash = ?
                  and event_family = ?
                  and source_kind = ?
                order by coalesce(last_occurred_to, last_source_row_updated_at, last_source_package_imported_at) desc nulls last,
                         updated_at desc
                limit 1
                """,
                rs -> rs.next()
                        ? new ImportCursorStateDto(
                                rs.getObject("last_source_package_imported_at", java.time.OffsetDateTime.class),
                                rs.getString("last_source_package_id"),
                                rs.getObject("last_source_row_updated_at", java.time.OffsetDateTime.class),
                                rs.getObject("last_occurred_to", java.time.OffsetDateTime.class)
                        )
                        : null,
                tenantKey,
                eventSourceId,
                scopeHash,
                eventFamily.name(),
                sourceKind
        );
    }

    public void advanceCursor(
            String tenantKey,
            int eventSourceId,
            String scopeHash,
            EventFamily eventFamily,
            String sourceKind,
            AcquisitionStrategy strategy,
            ExtractionBatchResult result
    ) {
        jdbcTemplate.update(
                """
                insert into eventhub.import_cursor(
                    id, tenant_key, event_source_id, scope_hash, event_family, source_kind, cursor_type,
                    last_source_package_imported_at, last_source_package_id,
                    last_source_row_updated_at, last_occurred_to, updated_at
                ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now())
                on conflict (tenant_key, event_source_id, scope_hash, event_family, source_kind, cursor_type)
                do update set
                    last_source_package_imported_at = coalesce(excluded.last_source_package_imported_at, eventhub.import_cursor.last_source_package_imported_at),
                    last_source_package_id = coalesce(excluded.last_source_package_id, eventhub.import_cursor.last_source_package_id),
                    last_source_row_updated_at = coalesce(excluded.last_source_row_updated_at, eventhub.import_cursor.last_source_row_updated_at),
                    last_occurred_to = coalesce(excluded.last_occurred_to, eventhub.import_cursor.last_occurred_to),
                    updated_at = now()
                """,
                UUID.randomUUID(),
                tenantKey,
                eventSourceId,
                scopeHash,
                eventFamily.name(),
                sourceKind,
                strategy.name(),
                result.lastSourcePackageImportedAt(),
                result.lastSourcePackageId(),
                result.lastSourceRowUpdatedAt(),
                result.lastOccurredTo()
        );
    }
}
