package at.procon.eventhub.persistence;

import at.procon.eventhub.dto.DriverCardRefDto;
import at.procon.eventhub.dto.DriverRefDto;
import at.procon.eventhub.reference.DriverCardNumberNormalizer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
public class DriverIdentityRepository {

    private final JdbcTemplate jdbcTemplate;
    private final ObjectMapper objectMapper;

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

    public ResolvedDriverReference resolveOrCreateDriverReference(
            String tenantKey,
            int eventSourceId,
            DriverRefDto driverRef
    ) {
        if (driverRef == null || !driverRef.hasAnyReference()) {
            return ResolvedDriverReference.empty();
        }

        String normalizedTenantKey = normalizeRequired(tenantKey, "tenantKey");
        String sourceDriverEntityId = normalizeNullable(driverRef.sourceEntityId());
        DriverCardRefDto driverCard = driverRef.driverCard();
        String cardNation = driverCard == null ? null : normalizeNullable(driverCard.nation());
        Integer cardNationNumericCode = driverCard == null ? null : driverCard.nationNumericCode();
        String cardNumber = driverCard == null ? null : normalizeDriverCardNumber(driverCard.number());

        UUID driverId = findBySourceDriverEntityId(normalizedTenantKey, eventSourceId, sourceDriverEntityId);
        UUID driverCardId = resolveOrCreateDriverCardId(cardNation, cardNationNumericCode, cardNumber, driverId);

        if (driverId == null && driverCardId != null) {
            driverId = findDriverIdByCardId(driverCardId);
        }
        if (driverId == null && sourceDriverEntityId != null) {
            Map<String, Object> payload = new LinkedHashMap<>();
            put(payload, "source", "event");
            put(payload, "source_driver_entity_id", sourceDriverEntityId);
            driverId = createDriver(
                    normalizedTenantKey,
                    eventSourceId,
                    sourceDriverEntityId,
                    null,
                    null,
                    null,
                    null,
                    payload
            );
        }

        if (driverId != null && sourceDriverEntityId != null) {
            upsertSourceDriverIdentity(
                    normalizedTenantKey,
                    eventSourceId,
                    sourceDriverEntityId,
                    driverId,
                    null,
                    Map.of("source", "event")
            );
        }
        if (driverCardId != null && driverId != null) {
            linkDriverCard(driverCardId, driverId);
        }
        return new ResolvedDriverReference(driverId, driverCardId);
    }

    @Transactional
    public int reconcileFromMasterData(String tenantKey, int eventSourceId) {
        String normalizedTenantKey = normalizeRequired(tenantKey, "tenantKey");
        int updates = reconcileDriversFromMasterData(normalizedTenantKey, eventSourceId);
        updates += reconcileDriverCardsFromMasterData(normalizedTenantKey, eventSourceId);
        updates += projectDriverCardLinksFromMasterData(normalizedTenantKey, eventSourceId);
        return updates;
    }

    private int reconcileDriversFromMasterData(String tenantKey, int eventSourceId) {
        int insertedDrivers;
        if (driverUsesLegacySchema()) {
            insertedDrivers = jdbcTemplate.update(
                    compatibleSourcesCte() + """
                    , master_drivers as (
                        select distinct on (nullif(trim(source_entity_id), ''))
                               event_source_id,
                               nullif(trim(source_entity_id), '') as source_driver_entity_id,
                               nullif(trim(payload ->> 'first_names'), '') as first_names,
                               coalesce(
                                   nullif(trim(payload ->> 'last_name'), ''),
                                   nullif(trim(payload ->> 'surname'), '')
                               ) as last_name,
                               cast(nullif(trim(payload ->> 'birth_date'), '') as date) as birth_date,
                               source_updated_at,
                               payload
                        from eventhub.source_master_entity
                        where tenant_key = ?
                          and event_source_id in (select id from compatible_sources)
                          and entity_type = 'DRIVER'
                          and nullif(trim(source_entity_id), '') is not null
                        order by nullif(trim(source_entity_id), ''), updated_at desc
                    ),
                    resolved_drivers as (
                        select master.event_source_id,
                               master.source_driver_entity_id,
                               master.first_names,
                               master.last_name,
                               master.birth_date,
                               master.source_updated_at,
                               master.payload,
                               coalesce(identity.driver_id, gen_random_uuid()) as driver_id
                        from master_drivers master
                        left join lateral (
                            select identity.driver_id
                            from eventhub.source_driver_identity identity
                            where identity.tenant_key = ?
                              and identity.event_source_id in (select id from compatible_sources)
                              and identity.source_driver_entity_id = master.source_driver_entity_id
                            order by identity.updated_at desc, identity.id desc
                            limit 1
                        ) identity on true
                    )
                    insert into eventhub.driver(
                        id, tenant_key, event_source_id, source_driver_entity_id,
                        first_names, last_name, birth_date, source_updated_at, payload, updated_at
                    )
                    select distinct on (resolved.driver_id)
                           resolved.driver_id,
                           ?,
                           resolved.event_source_id,
                           resolved.source_driver_entity_id,
                           resolved.first_names,
                           resolved.last_name,
                           resolved.birth_date,
                           resolved.source_updated_at,
                           resolved.payload,
                           now()
                    from resolved_drivers resolved
                    where not exists (
                        select 1
                        from eventhub.driver existing
                        where existing.id = resolved.driver_id
                    )
                    """,
                    eventSourceId,
                    tenantKey,
                    tenantKey,
                    tenantKey
            );
        } else {
            insertedDrivers = jdbcTemplate.update(
                    compatibleSourcesCte() + """
                    , master_drivers as (
                        select distinct on (nullif(trim(source_entity_id), ''))
                               event_source_id,
                               nullif(trim(source_entity_id), '') as source_driver_entity_id,
                               nullif(trim(payload ->> 'first_names'), '') as first_names,
                               coalesce(
                                   nullif(trim(payload ->> 'last_name'), ''),
                                   nullif(trim(payload ->> 'surname'), '')
                               ) as last_name,
                               cast(nullif(trim(payload ->> 'birth_date'), '') as date) as birth_date,
                               source_updated_at,
                               payload
                        from eventhub.source_master_entity
                        where tenant_key = ?
                          and event_source_id in (select id from compatible_sources)
                          and entity_type = 'DRIVER'
                          and nullif(trim(source_entity_id), '') is not null
                        order by nullif(trim(source_entity_id), ''), updated_at desc
                    ),
                    resolved_drivers as (
                        select master.event_source_id,
                               master.source_driver_entity_id,
                               master.first_names,
                               master.last_name,
                               master.birth_date,
                               master.source_updated_at,
                               master.payload,
                               coalesce(identity.driver_id, gen_random_uuid()) as driver_id
                        from master_drivers master
                        left join lateral (
                            select identity.driver_id
                            from eventhub.source_driver_identity identity
                            where identity.tenant_key = ?
                              and identity.event_source_id in (select id from compatible_sources)
                              and identity.source_driver_entity_id = master.source_driver_entity_id
                            order by identity.updated_at desc, identity.id desc
                            limit 1
                        ) identity on true
                    )
                    insert into eventhub.driver(
                        id, first_names, last_name, birth_date, source_updated_at, payload, updated_at
                    )
                    select distinct on (resolved.driver_id)
                           resolved.driver_id,
                           resolved.first_names,
                           resolved.last_name,
                           resolved.birth_date,
                           resolved.source_updated_at,
                           resolved.payload,
                           now()
                    from resolved_drivers resolved
                    where not exists (
                        select 1
                        from eventhub.driver existing
                        where existing.id = resolved.driver_id
                    )
                    """,
                    eventSourceId,
                    tenantKey,
                    tenantKey
            );
        }

        int updatedDrivers = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_drivers as (
                    select distinct on (nullif(trim(source_entity_id), ''))
                           nullif(trim(source_entity_id), '') as source_driver_entity_id,
                           nullif(trim(payload ->> 'first_names'), '') as first_names,
                           coalesce(
                               nullif(trim(payload ->> 'last_name'), ''),
                               nullif(trim(payload ->> 'surname'), '')
                           ) as last_name,
                           cast(nullif(trim(payload ->> 'birth_date'), '') as date) as birth_date,
                           source_updated_at,
                           payload
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'DRIVER'
                      and nullif(trim(source_entity_id), '') is not null
                    order by nullif(trim(source_entity_id), ''), updated_at desc
                )
                update eventhub.driver driver
                set first_names = coalesce(master.first_names, driver.first_names),
                    last_name = coalesce(master.last_name, driver.last_name),
                    birth_date = coalesce(master.birth_date, driver.birth_date),
                    source_updated_at = coalesce(master.source_updated_at, driver.source_updated_at),
                    payload = driver.payload || master.payload,
                    updated_at = now()
                from master_drivers master
                join lateral (
                    select identity.driver_id
                    from eventhub.source_driver_identity identity
                    where identity.tenant_key = ?
                      and identity.event_source_id in (select id from compatible_sources)
                      and identity.source_driver_entity_id = master.source_driver_entity_id
                    order by identity.updated_at desc, identity.id desc
                    limit 1
                ) identity on true
                where identity.driver_id = driver.id
                """,
                eventSourceId,
                tenantKey,
                tenantKey
        );

        int linkedDrivers = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_drivers as (
                    select distinct on (nullif(trim(source_entity_id), ''))
                           event_source_id,
                           nullif(trim(source_entity_id), '') as source_driver_entity_id,
                           source_updated_at,
                           payload
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'DRIVER'
                      and nullif(trim(source_entity_id), '') is not null
                    order by nullif(trim(source_entity_id), ''), updated_at desc
                ),
                resolved_driver_ids as (
                    select master.event_source_id,
                           master.source_driver_entity_id,
                           master.source_updated_at,
                           master.payload,
                           coalesce(identity.driver_id, (
                               select created.id
                               from eventhub.driver created
                               where created.payload = master.payload
                               order by created.updated_at desc
                               limit 1
                           )) as driver_id
                    from master_drivers master
                    left join lateral (
                        select identity.driver_id
                        from eventhub.source_driver_identity identity
                        where identity.tenant_key = ?
                          and identity.event_source_id in (select id from compatible_sources)
                          and identity.source_driver_entity_id = master.source_driver_entity_id
                        order by identity.updated_at desc, identity.id desc
                        limit 1
                    ) identity on true
                )
                insert into eventhub.source_driver_identity(
                    id, tenant_key, event_source_id, source_driver_entity_id,
                    driver_id, source_updated_at, payload, updated_at
                )
                select gen_random_uuid(),
                       ?,
                       resolved.event_source_id,
                       resolved.source_driver_entity_id,
                       resolved.driver_id,
                       resolved.source_updated_at,
                       resolved.payload,
                       now()
                from resolved_driver_ids resolved
                where resolved.driver_id is not null
                on conflict (tenant_key, event_source_id, source_driver_entity_id)
                do update set
                    driver_id = excluded.driver_id,
                    source_updated_at = coalesce(excluded.source_updated_at, eventhub.source_driver_identity.source_updated_at),
                    payload = eventhub.source_driver_identity.payload || excluded.payload,
                    updated_at = now()
                """,
                eventSourceId,
                tenantKey,
                tenantKey,
                tenantKey
        );

        return insertedDrivers + updatedDrivers + linkedDrivers;
    }

    private int reconcileDriverCardsFromMasterData(String tenantKey, int eventSourceId) {
        int insertedCards = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_driver_cards as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        nullif(trim(payload ->> 'card_nation'), ''),
                        nullif(trim(payload ->> 'card_number'), '')
                    )
                           event_source_id,
                           nullif(trim(source_entity_id), '') as source_driver_card_entity_id,
                           nullif(trim(payload ->> 'card_nation'), '') as card_nation,
                           nullif(trim(payload ->> 'card_number'), '') as card_number,
                           source_updated_at,
                           payload
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'DRIVER_CARD'
                      and nullif(trim(payload ->> 'card_nation'), '') is not null
                      and nullif(trim(payload ->> 'card_number'), '') is not null
                    order by nullif(trim(source_entity_id), ''),
                             nullif(trim(payload ->> 'card_nation'), ''),
                             nullif(trim(payload ->> 'card_number'), ''),
                             updated_at desc
                ),
                canonical_driver_cards as (
                    select distinct on (master.card_nation, master.card_number)
                           master.card_nation,
                           master.card_number,
                           master.source_updated_at,
                           master.payload
                    from master_driver_cards master
                    order by master.card_nation,
                             master.card_number,
                             case when master.source_driver_card_entity_id is null then 1 else 0 end,
                             master.source_updated_at desc,
                             master.source_driver_card_entity_id
                ),
                existing_driver_cards as (
                    select distinct on (existing.nation, existing.card_number)
                           existing.id,
                           existing.nation,
                           existing.card_number
                    from eventhub.driver_card existing
                    order by existing.nation,
                             existing.card_number,
                             case when existing.driver_id is null then 1 else 0 end,
                             existing.updated_at desc,
                             existing.created_at desc,
                             existing.id
                ),
                resolved_cards as (
                    select canonical.card_nation,
                           canonical.card_number,
                           canonical.source_updated_at,
                           canonical.payload,
                           coalesce(existing.id, gen_random_uuid()) as driver_card_id
                    from canonical_driver_cards canonical
                    left join existing_driver_cards existing
                      on existing.nation = canonical.card_nation
                     and existing.card_number = canonical.card_number
                )
                insert into eventhub.driver_card(
                    id, driver_id, nation, card_number, source_updated_at, payload, updated_at
                )
                select distinct on (resolved.driver_card_id)
                       resolved.driver_card_id,
                       null,
                       resolved.card_nation,
                       resolved.card_number,
                       resolved.source_updated_at,
                       resolved.payload,
                       now()
                from resolved_cards resolved
                where not exists (
                    select 1
                    from eventhub.driver_card existing
                    where existing.id = resolved.driver_card_id
                )
                """,
                eventSourceId,
                tenantKey
        );

        int updatedCards = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_driver_cards as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        nullif(trim(payload ->> 'card_nation'), ''),
                        nullif(trim(payload ->> 'card_number'), '')
                    )
                           nullif(trim(source_entity_id), '') as source_driver_card_entity_id,
                           nullif(trim(payload ->> 'card_nation'), '') as card_nation,
                           nullif(trim(payload ->> 'card_number'), '') as card_number,
                           source_updated_at,
                           payload
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'DRIVER_CARD'
                      and nullif(trim(payload ->> 'card_nation'), '') is not null
                      and nullif(trim(payload ->> 'card_number'), '') is not null
                    order by nullif(trim(source_entity_id), ''),
                             nullif(trim(payload ->> 'card_nation'), ''),
                             nullif(trim(payload ->> 'card_number'), ''),
                             updated_at desc
                ),
                canonical_driver_cards as (
                    select distinct on (master.card_nation, master.card_number)
                           master.card_nation,
                           master.card_number,
                           master.source_updated_at,
                           master.payload
                    from master_driver_cards master
                    order by master.card_nation,
                             master.card_number,
                             case when master.source_driver_card_entity_id is null then 1 else 0 end,
                             master.source_updated_at desc,
                             master.source_driver_card_entity_id
                ),
                existing_driver_cards as (
                    select distinct on (existing.nation, existing.card_number)
                           existing.id,
                           existing.nation,
                           existing.card_number
                    from eventhub.driver_card existing
                    order by existing.nation,
                             existing.card_number,
                             case when existing.driver_id is null then 1 else 0 end,
                             existing.updated_at desc,
                             existing.created_at desc,
                             existing.id
                ),
                resolved_cards as (
                    select canonical.source_updated_at,
                           canonical.payload,
                           existing.id as driver_card_id
                    from canonical_driver_cards canonical
                    join existing_driver_cards existing
                      on existing.nation = canonical.card_nation
                     and existing.card_number = canonical.card_number
                )
                update eventhub.driver_card card
                set source_updated_at = coalesce(resolved.source_updated_at, card.source_updated_at),
                    payload = card.payload || resolved.payload,
                    updated_at = now()
                from resolved_cards resolved
                where card.id = resolved.driver_card_id
                """,
                eventSourceId,
                tenantKey
        );

        int linkedCards = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_driver_cards as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        nullif(trim(payload ->> 'card_nation'), ''),
                        nullif(trim(payload ->> 'card_number'), '')
                    )
                           event_source_id,
                           nullif(trim(source_entity_id), '') as source_driver_card_entity_id,
                           nullif(trim(payload ->> 'card_nation'), '') as card_nation,
                           nullif(trim(payload ->> 'card_number'), '') as card_number,
                           source_updated_at,
                           payload
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'DRIVER_CARD'
                      and nullif(trim(payload ->> 'card_nation'), '') is not null
                      and nullif(trim(payload ->> 'card_number'), '') is not null
                    order by nullif(trim(source_entity_id), ''),
                             nullif(trim(payload ->> 'card_nation'), ''),
                             nullif(trim(payload ->> 'card_number'), ''),
                             updated_at desc
                ),
                existing_driver_cards as (
                    select distinct on (existing.nation, existing.card_number)
                           existing.id,
                           existing.nation,
                           existing.card_number
                    from eventhub.driver_card existing
                    order by existing.nation,
                             existing.card_number,
                             case when existing.driver_id is null then 1 else 0 end,
                             existing.updated_at desc,
                             existing.created_at desc,
                             existing.id
                ),
                resolved_cards as (
                    select master.event_source_id,
                           master.source_driver_card_entity_id,
                           master.source_updated_at,
                           master.payload,
                           coalesce(identity.driver_card_id, existing.id) as driver_card_id
                    from master_driver_cards master
                    left join lateral (
                        select identity.driver_card_id
                        from eventhub.source_driver_card_identity identity
                        where identity.tenant_key = ?
                          and identity.event_source_id in (select id from compatible_sources)
                          and identity.source_driver_card_entity_id = master.source_driver_card_entity_id
                        order by identity.updated_at desc, identity.id desc
                        limit 1
                    ) identity on true
                    left join existing_driver_cards existing
                      on existing.nation = master.card_nation
                     and existing.card_number = master.card_number
                )
                insert into eventhub.source_driver_card_identity(
                    id, tenant_key, event_source_id, source_driver_card_entity_id,
                    driver_card_id, source_updated_at, payload, updated_at
                )
                select gen_random_uuid(),
                       ?,
                       resolved.event_source_id,
                       resolved.source_driver_card_entity_id,
                       resolved.driver_card_id,
                       resolved.source_updated_at,
                       resolved.payload,
                       now()
                from resolved_cards resolved
                where resolved.source_driver_card_entity_id is not null
                  and resolved.driver_card_id is not null
                on conflict (tenant_key, event_source_id, source_driver_card_entity_id)
                do update set
                    driver_card_id = excluded.driver_card_id,
                    source_updated_at = coalesce(excluded.source_updated_at, eventhub.source_driver_card_identity.source_updated_at),
                    payload = eventhub.source_driver_card_identity.payload || excluded.payload,
                    updated_at = now()
                """,
                eventSourceId,
                tenantKey,
                tenantKey,
                tenantKey
        );

        return insertedCards + updatedCards + linkedCards;
    }

    private int projectDriverCardLinksFromMasterData(String tenantKey, int eventSourceId) {
        return jdbcTemplate.update(
                compatibleSourcesCte() + """
                update eventhub.driver_card card
                set driver_id = driver_identity.driver_id,
                    source_updated_at = coalesce(relation.source_updated_at, card.source_updated_at),
                    updated_at = now()
                from eventhub.source_master_relation relation
                join eventhub.source_driver_card_identity card_identity
                  on card_identity.tenant_key = relation.tenant_key
                 and card_identity.event_source_id = relation.event_source_id
                 and card_identity.source_driver_card_entity_id = relation.from_source_entity_id
                join eventhub.source_driver_identity driver_identity
                  on driver_identity.tenant_key = relation.tenant_key
                 and driver_identity.event_source_id = relation.event_source_id
                 and driver_identity.source_driver_entity_id = relation.to_source_entity_id
                where relation.tenant_key = ?
                  and relation.event_source_id in (select id from compatible_sources)
                  and relation.relation_type = 'DRIVER_CARD_DRIVER'
                  and relation.from_entity_type = 'DRIVER_CARD'
                  and relation.to_entity_type = 'DRIVER'
                  and card.id = card_identity.driver_card_id
                  and (
                      card.driver_id is null
                      or card.driver_id = driver_identity.driver_id
                      or not exists (
                          select 1
                          from eventhub.source_driver_identity existing_identity
                          where existing_identity.driver_id = card.driver_id
                      )
                  )
                """,
                eventSourceId,
                tenantKey
        );
    }

    private UUID resolveOrCreateDriverCardId(
            String cardNation,
            Integer cardNationNumericCode,
            String cardNumber,
            UUID preferredDriverId
    ) {
        if ((cardNation == null && cardNationNumericCode == null) || cardNumber == null) {
            return null;
        }
        UUID driverCardId = findDriverCardByCard(cardNation, cardNationNumericCode, cardNumber);
        if (driverCardId == null) {
            Map<String, Object> payload = new LinkedHashMap<>();
            put(payload, "source", "event");
            put(payload, "card_nation", cardNation);
            put(payload, "card_nation_numeric_code", cardNationNumericCode);
            put(payload, "card_number", cardNumber);
            return createDriverCard(
                    preferredDriverId,
                    cardNation,
                    cardNationNumericCode,
                    cardNumber,
                    null,
                    payload
            );
        }
        if (preferredDriverId != null) {
            linkDriverCard(driverCardId, preferredDriverId);
        }
        return driverCardId;
    }

    private UUID findBySourceDriverEntityId(String tenantKey, int eventSourceId, String sourceDriverEntityId) {
        if (sourceDriverEntityId == null) {
            return null;
        }
        return jdbcTemplate.query(
                compatibleSourcesCte() + """
                select identity.driver_id
                from eventhub.source_driver_identity identity
                where identity.tenant_key = ?
                  and identity.event_source_id in (select id from compatible_sources)
                  and identity.source_driver_entity_id = ?
                order by identity.updated_at desc
                limit 1
                """,
                rs -> rs.next() ? (UUID) rs.getObject("driver_id") : null,
                eventSourceId,
                tenantKey,
                sourceDriverEntityId
        );
    }

    private UUID findDriverIdByCardId(UUID driverCardId) {
        if (driverCardId == null) {
            return null;
        }
        return jdbcTemplate.query(
                """
                select driver_id
                from eventhub.driver_card
                where id = ?
                """,
                rs -> rs.next() ? (UUID) rs.getObject("driver_id") : null,
                driverCardId
        );
    }

    private UUID findDriverCardByCard(String cardNation, Integer cardNationNumericCode, String cardNumber) {
        if ((cardNation == null && cardNationNumericCode == null) || cardNumber == null) {
            return null;
        }
        if (cardNationNumericCode != null) {
            return jdbcTemplate.query(
                    """
                    select card.id
                    from eventhub.driver_card card
                    where card.nation_numeric_code = ?
                      and card.card_number = ?
                    order by card.updated_at desc
                    limit 1
                    """,
                    rs -> rs.next() ? (UUID) rs.getObject("id") : null,
                    cardNationNumericCode,
                    cardNumber
            );
        }
        return jdbcTemplate.query(
                """
                select card.id
                from eventhub.driver_card card
                where card.nation = ?
                  and card.card_number = ?
                order by card.updated_at desc
                limit 1
                """,
                rs -> rs.next() ? (UUID) rs.getObject("id") : null,
                cardNation,
                cardNumber
        );
    }

    private UUID createDriver(
            String tenantKey,
            int eventSourceId,
            String sourceDriverEntityId,
            String firstNames,
            String lastName,
            OffsetDateTime sourceUpdatedAt,
            LocalDate birthDate,
            Map<String, Object> payload
    ) {
        UUID driverId = UUID.randomUUID();
        if (driverUsesLegacySchema()) {
            jdbcTemplate.update(
                    """
                    insert into eventhub.driver(
                        id, tenant_key, event_source_id, source_driver_entity_id,
                        first_names, last_name, birth_date, source_updated_at, payload, updated_at
                    ) values (?, ?, ?, ?, ?, ?, ?, ?, ?::jsonb, now())
                    """,
                    driverId,
                    tenantKey,
                    eventSourceId,
                    sourceDriverEntityId,
                    firstNames,
                    lastName,
                    birthDate,
                    sourceUpdatedAt,
                    toJson(payload)
            );
        } else {
            jdbcTemplate.update(
                    """
                    insert into eventhub.driver(
                        id, first_names, last_name, birth_date, source_updated_at, payload, updated_at
                    ) values (?, ?, ?, ?, ?, ?::jsonb, now())
                    """,
                    driverId,
                    firstNames,
                    lastName,
                    birthDate,
                    sourceUpdatedAt,
                    toJson(payload)
            );
        }
        return driverId;
    }

    private boolean driverUsesLegacySchema() {
        Integer legacyColumns = jdbcTemplate.queryForObject(
                """
                select count(*)
                from information_schema.columns
                where table_schema = 'eventhub'
                  and table_name = 'driver'
                  and column_name in ('tenant_key', 'event_source_id', 'source_driver_entity_id')
                """,
                Integer.class
        );
        return legacyColumns != null && legacyColumns == 3;
    }

    private String normalizeDriverCardNumber(String cardNumber) {
        return DriverCardNumberNormalizer.canonical(cardNumber);
    }

    private UUID createDriverCard(
            UUID driverId,
            String cardNation,
            Integer cardNationNumericCode,
            String cardNumber,
            OffsetDateTime sourceUpdatedAt,
            Map<String, Object> payload
    ) {
        UUID driverCardId = UUID.randomUUID();
        jdbcTemplate.update(
                """
                insert into eventhub.driver_card(
                    id, driver_id, nation, nation_numeric_code, card_number, source_updated_at, payload, updated_at
                ) values (?, ?, ?, ?, ?, ?, ?::jsonb, now())
                """,
                driverCardId,
                driverId,
                cardNation,
                cardNationNumericCode,
                cardNumber,
                sourceUpdatedAt,
                toJson(payload)
        );
        return driverCardId;
    }

    private void upsertSourceDriverIdentity(
            String tenantKey,
            int eventSourceId,
            String sourceDriverEntityId,
            UUID driverId,
            OffsetDateTime sourceUpdatedAt,
            Map<String, Object> payload
    ) {
        if (sourceDriverEntityId == null || driverId == null) {
            return;
        }
        jdbcTemplate.update(
                """
                insert into eventhub.source_driver_identity(
                    id, tenant_key, event_source_id, source_driver_entity_id,
                    driver_id, source_updated_at, payload, updated_at
                ) values (?, ?, ?, ?, ?, ?, ?::jsonb, now())
                on conflict (tenant_key, event_source_id, source_driver_entity_id)
                do update set
                    driver_id = excluded.driver_id,
                    source_updated_at = coalesce(excluded.source_updated_at, eventhub.source_driver_identity.source_updated_at),
                    payload = eventhub.source_driver_identity.payload || excluded.payload,
                    updated_at = now()
                """,
                UUID.randomUUID(),
                tenantKey,
                eventSourceId,
                sourceDriverEntityId,
                driverId,
                sourceUpdatedAt,
                toJson(payload)
        );
    }

    private void linkDriverCard(UUID driverCardId, UUID driverId) {
        if (driverCardId == null || driverId == null) {
            return;
        }
        jdbcTemplate.update(
                """
                update eventhub.driver_card
                set driver_id = ?,
                    updated_at = now()
                where id = ?
                  and (
                      driver_id is null
                      or driver_id = ?
                      or (
                          exists (
                              select 1
                              from eventhub.source_driver_identity preferred_identity
                              where preferred_identity.driver_id = ?
                          )
                          and not exists (
                              select 1
                              from eventhub.source_driver_identity current_identity
                              where current_identity.driver_id = eventhub.driver_card.driver_id
                          )
                      )
                  )
                """,
                driverId,
                driverCardId,
                driverId,
                driverId
        );
    }

    private String compatibleSourcesCte() {
        return """
                with source_context as (
                    select tenant_key, provider_key, source_instance_key, coalesce(tenant_provider_setting_key, '') as tenant_provider_setting_key
                    from eventhub.event_source
                    where id = ?
                ),
                compatible_sources as (
                    select es.id
                    from eventhub.event_source es
                    join source_context ctx on ctx.tenant_key = es.tenant_key
                        and ctx.provider_key = es.provider_key
                        and ctx.source_instance_key = es.source_instance_key
                        and ctx.tenant_provider_setting_key = coalesce(es.tenant_provider_setting_key, '')
                )
                """;
    }

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

    private void put(Map<String, Object> payload, String key, Object value) {
        if (value != null) {
            payload.put(key, value);
        }
    }

    private String normalizeRequired(String value, String fieldName) {
        String normalized = normalizeNullable(value);
        if (normalized == null) {
            throw new IllegalArgumentException(fieldName + " must not be blank");
        }
        return normalized;
    }

    private String normalizeNullable(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

    public record ResolvedDriverReference(UUID driverId, UUID driverCardId) {
        public static ResolvedDriverReference empty() {
            return new ResolvedDriverReference(null, null);
        }
    }
}
