package at.procon.eventhub.importing.persistence;

import at.procon.eventhub.dto.ImportRunStatus;
import at.procon.eventhub.dto.ImportScopeDto;
import at.procon.eventhub.dto.SourceGroupRefDto;
import at.procon.eventhub.importing.ImportRunRequest;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.sql.Array;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
import java.util.UUID;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class ImportRunRepository {

    private final JdbcTemplate jdbcTemplate;
    private final ObjectMapper objectMapper;

    public ImportRunRepository(JdbcTemplate jdbcTemplate, ObjectMapper objectMapper) {
        this.jdbcTemplate = jdbcTemplate;
        this.objectMapper = objectMapper;
    }

    public UUID createPlannedRun(int eventSourceId, ImportRunRequest request, Map<String, Object> metadata) {
        UUID id = UUID.randomUUID();
        SourceGroupRefDto sourceGroup = request.sourceGroup();
        ImportScopeDto importScope = request.importScope();
        SourceGroupRefDto rootOrg = importScope == null ? null : importScope.rootSourceOrganisation();

        jdbcTemplate.update(con -> {
            var ps = con.prepareStatement("""
                    insert into eventhub.import_run(
                        id, tenant_key, event_source_id, mode, status, refresh_master_data_first,
                        source_group_type, source_group_entity_id, source_group_code, source_group_name,
                        import_scope_type, root_source_org_entity_id, root_source_org_code, root_source_org_name,
                        include_children, occurred_from, occurred_to,
                        requested_event_families, acquisition_strategy, metadata, planned_package_count
                    ) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?::jsonb, 0)
                    """);
            ps.setObject(1, id);
            ps.setString(2, request.tenantKey());
            ps.setInt(3, eventSourceId);
            ps.setString(4, request.mode().name());
            ps.setString(5, ImportRunStatus.PLANNED.name());
            ps.setBoolean(6, request.refreshMasterDataFirst());
            ps.setString(7, sourceGroup == null || sourceGroup.type() == null ? null : sourceGroup.type().name());
            ps.setString(8, sourceGroup == null ? null : sourceGroup.sourceEntityId());
            ps.setString(9, sourceGroup == null ? null : sourceGroup.code());
            ps.setString(10, sourceGroup == null ? null : sourceGroup.name());
            ps.setString(11, importScope == null || importScope.type() == null ? null : importScope.type().name());
            ps.setString(12, rootOrg == null ? null : rootOrg.sourceEntityId());
            ps.setString(13, rootOrg == null ? null : rootOrg.code());
            ps.setString(14, rootOrg == null ? null : rootOrg.name());
            ps.setBoolean(15, importScope != null && importScope.includeChildren());
            ps.setObject(16, importScope == null ? null : importScope.occurredFrom());
            ps.setObject(17, importScope == null ? null : importScope.occurredTo());
            ps.setArray(18, eventFamilyArray(con, request));
            ps.setString(19, request.acquisitionStrategy().name());
            ps.setString(20, toJson(metadata == null ? Map.of() : metadata));
            return ps;
        });
        return id;
    }

    public void markRunning(UUID id) {
        jdbcTemplate.update("update eventhub.import_run set status = ? where id = ?", ImportRunStatus.RUNNING.name(), id);
    }

    public void markPlannedPackages(UUID id, int plannedPackageCount) {
        jdbcTemplate.update("update eventhub.import_run set planned_package_count = ? where id = ?", plannedPackageCount, id);
    }

    public void markCompleted(UUID id) {
        jdbcTemplate.update("update eventhub.import_run set status = ?, finished_at = now() where id = ?", ImportRunStatus.COMPLETED.name(), id);
    }

    public void markFailed(UUID id, String errorMessage) {
        jdbcTemplate.update("update eventhub.import_run set status = ?, error_message = ?, finished_at = now() where id = ?", ImportRunStatus.FAILED.name(), errorMessage, id);
    }

    private Array eventFamilyArray(Connection con, ImportRunRequest request) throws SQLException {
        String[] values = request.eventFamilies().stream().map(Enum::name).toArray(String[]::new);
        return con.createArrayOf("text", values);
    }

    private String toJson(Map<String, Object> value) {
        try {
            return objectMapper.writeValueAsString(value == null ? Map.of() : value);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("Cannot serialize import run metadata", e);
        }
    }
}
