package at.procon.eventhub.persistence;

import at.procon.eventhub.dto.VehicleRefDto;
import at.procon.eventhub.dto.VehicleRegistrationRefDto;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.OffsetDateTime;
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 VehicleIdentityRepository {

    private static final String YELLOWFOX_SYNTHETIC_REFERENCE_NATION = "YELLOWFOX";
    private static final String UNKNOWN_REGISTRATION_NATION = "UNKNOWN";

    private final JdbcTemplate jdbcTemplate;
    private final ObjectMapper objectMapper;

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

    public ResolvedVehicleReferenceResolution resolveOrCreateVehicleReference(
            String tenantKey,
            int eventSourceId,
            VehicleRefDto vehicleRef,
            OffsetDateTime occurredAt
    ) {
        if (vehicleRef == null || !vehicleRef.hasAnyReference()) {
            return ResolvedVehicleReferenceResolution.empty();
        }

        String normalizedTenantKey = normalizeRequired(tenantKey, "tenantKey");
        String sourceVehicleEntityId = normalizeNullable(vehicleRef.sourceVehicleEntityId());
        String vin = normalizeNullable(vehicleRef.vin());
        String sourceRegistrationEntityId = normalizeNullable(vehicleRef.sourceRegistrationEntityId());
        VehicleRegistrationRefDto registration = vehicleRef.vehicleRegistration();
        String registrationNation = registration == null ? null : normalizeNullable(registration.nation());
        String registrationNumber = registration == null ? null : normalizeNullable(registration.number());
        String registrationNationForCreate = creationNation(registrationNation, registrationNumber);

        UUID registrationId = resolveRegistrationId(
                normalizedTenantKey,
                eventSourceId,
                sourceRegistrationEntityId,
                registrationNation,
                registrationNumber
        );
        if (registrationId == null && (sourceRegistrationEntityId != null || registrationNumber != null)) {
            registrationId = createRegistration(
                    registrationNationForCreate,
                    registrationNumber,
                    null,
                    Map.of("source", "event")
            );
        }
        if (registrationId != null && sourceRegistrationEntityId != null) {
            upsertSourceVehicleRegistrationIdentity(
                    normalizedTenantKey,
                    eventSourceId,
                    sourceRegistrationEntityId,
                    registrationId,
                    null,
                    Map.of("source", "event")
            );
        }

        UUID vehicleId = resolveVehicleId(
                normalizedTenantKey,
                eventSourceId,
                sourceVehicleEntityId,
                vin
        );
        AssignedVehicleReference assignedVehicle = null;
        if (vehicleId == null && registrationId != null) {
            assignedVehicle = resolveAssignedVehicleReference(normalizedTenantKey, eventSourceId, registrationId, occurredAt);
            vehicleId = assignedVehicle == null ? null : assignedVehicle.vehicleId();
        }
        if (vehicleId == null && vin != null) {
            vehicleId = createVehicle(vin);
        }
        if (vehicleId != null && sourceVehicleEntityId != null) {
            upsertSourceVehicleIdentity(
                    normalizedTenantKey,
                    eventSourceId,
                    sourceVehicleEntityId,
                    vehicleId,
                    null,
                    Map.of("source", "event")
            );
        }
        touchVehicle(vehicleId, vin);
        touchRegistration(registrationId, registrationNationForCreate, registrationNumber);

        return new ResolvedVehicleReferenceResolution(
                new ResolvedVehicleReference(vehicleId, registrationId),
                assignedVehicle != null,
                assignedVehicle == null ? null : assignedVehicle.validFrom(),
                assignedVehicle == null ? null : assignedVehicle.validTo()
        );
    }

    @Transactional
    public int reconcileFromMasterData(String tenantKey, int eventSourceId) {
        return reconcileFromMasterData(tenantKey, eventSourceId, true);
    }

    @Transactional
    public int reconcileFromMasterData(String tenantKey, int eventSourceId, boolean syncVehicleRegistrations) {
        String normalizedTenantKey = normalizeRequired(tenantKey, "tenantKey");
        int updates = reconcileVehiclesFromMasterData(normalizedTenantKey, eventSourceId);
        if (syncVehicleRegistrations) {
            updates += reconcileRegistrationsFromMasterData(normalizedTenantKey, eventSourceId);
            updates += projectVehicleRegistrationAssignments(normalizedTenantKey, eventSourceId);
        }
        return updates;
    }

    private int reconcileVehiclesFromMasterData(String tenantKey, int eventSourceId) {
        int insertedVehicles = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_vehicles as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        nullif(trim(source_external_key), '')
                    )
                           event_source_id,
                           nullif(trim(source_entity_id), '') as source_vehicle_entity_id,
                           nullif(trim(source_external_key), '') as vin
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'VEHICLE'
                      and nullif(trim(source_external_key), '') is not null
                    order by nullif(trim(source_entity_id), ''),
                             nullif(trim(source_external_key), ''),
                             updated_at desc
                ),
                resolved_vehicles as (
                    select master.event_source_id,
                           master.source_vehicle_entity_id,
                           master.vin,
                           coalesce(identity.vehicle_id, existing.id, gen_random_uuid()) as vehicle_id
                    from master_vehicles master
                    left join lateral (
                        select identity.vehicle_id
                        from eventhub.source_vehicle_identity identity
                        where identity.tenant_key = ?
                          and identity.event_source_id in (select id from compatible_sources)
                          and identity.source_vehicle_entity_id = master.source_vehicle_entity_id
                        order by identity.updated_at desc, identity.id desc
                        limit 1
                    ) identity on true
                    left join lateral (
                        select existing.id
                        from eventhub.vehicle existing
                        where master.vin is not null
                          and existing.vin = master.vin
                        order by existing.updated_at desc, existing.created_at desc, existing.id
                        limit 1
                    ) existing on true
                )
                insert into eventhub.vehicle(id, vin, updated_at)
                select distinct on (resolved.vehicle_id)
                       resolved.vehicle_id,
                       resolved.vin,
                       now()
                from resolved_vehicles resolved
                where not exists (
                    select 1
                    from eventhub.vehicle existing
                    where existing.id = resolved.vehicle_id
                )
                """,
                eventSourceId,
                tenantKey,
                tenantKey
        );

        int updatedVehicles = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_vehicles as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        nullif(trim(source_external_key), '')
                    )
                           nullif(trim(source_entity_id), '') as source_vehicle_entity_id,
                           nullif(trim(source_external_key), '') as vin
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'VEHICLE'
                      and nullif(trim(source_external_key), '') is not null
                    order by nullif(trim(source_entity_id), ''),
                             nullif(trim(source_external_key), ''),
                             updated_at desc
                )
                update eventhub.vehicle vehicle
                set vin = coalesce(vehicle.vin, master.vin),
                    updated_at = now()
                from master_vehicles master
                left join lateral (
                    select identity.vehicle_id
                    from eventhub.source_vehicle_identity identity
                    where identity.tenant_key = ?
                      and identity.event_source_id in (select id from compatible_sources)
                      and identity.source_vehicle_entity_id = master.source_vehicle_entity_id
                    order by identity.updated_at desc, identity.id desc
                    limit 1
                ) identity on true
                where vehicle.id = coalesce(identity.vehicle_id, (
                    select existing.id
                    from eventhub.vehicle existing
                    where master.vin is not null
                      and existing.vin = master.vin
                    order by existing.updated_at desc, existing.created_at desc, existing.id
                    limit 1
                ))
                """,
                eventSourceId,
                tenantKey,
                tenantKey
        );

        int linkedVehicles = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_vehicles as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        nullif(trim(source_external_key), '')
                    )
                           event_source_id,
                           nullif(trim(source_entity_id), '') as source_vehicle_entity_id,
                           nullif(trim(source_external_key), '') as vin
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'VEHICLE'
                      and nullif(trim(source_entity_id), '') is not null
                      and nullif(trim(source_external_key), '') is not null
                    order by nullif(trim(source_entity_id), ''),
                             nullif(trim(source_external_key), ''),
                             updated_at desc
                ),
                resolved_vehicles as (
                    select master.event_source_id,
                           master.source_vehicle_entity_id,
                           coalesce(identity.vehicle_id, existing.id) as vehicle_id
                    from master_vehicles master
                    left join lateral (
                        select identity.vehicle_id
                        from eventhub.source_vehicle_identity identity
                        where identity.tenant_key = ?
                          and identity.event_source_id in (select id from compatible_sources)
                          and identity.source_vehicle_entity_id = master.source_vehicle_entity_id
                        order by identity.updated_at desc, identity.id desc
                        limit 1
                    ) identity on true
                    left join lateral (
                        select existing.id
                        from eventhub.vehicle existing
                        where master.vin is not null
                          and existing.vin = master.vin
                        order by existing.updated_at desc, existing.created_at desc, existing.id
                        limit 1
                    ) existing on true
                )
                insert into eventhub.source_vehicle_identity(
                    id, tenant_key, event_source_id, source_vehicle_entity_id,
                    vehicle_id, source_updated_at, payload, updated_at
                )
                select gen_random_uuid(),
                       ?,
                       resolved.event_source_id,
                       resolved.source_vehicle_entity_id,
                       resolved.vehicle_id,
                       null,
                       '{}'::jsonb,
                       now()
                from resolved_vehicles resolved
                where resolved.source_vehicle_entity_id is not null
                  and resolved.vehicle_id is not null
                on conflict (tenant_key, event_source_id, source_vehicle_entity_id)
                do update set
                    vehicle_id = excluded.vehicle_id,
                    updated_at = now()
                """,
                eventSourceId,
                tenantKey,
                tenantKey,
                tenantKey
        );

        return insertedVehicles + updatedVehicles + linkedVehicles;
    }

    private int reconcileRegistrationsFromMasterData(String tenantKey, int eventSourceId) {
        int insertedRegistrations = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_registrations as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        coalesce(
                            nullif(trim(payload ->> 'registration_nation'), ''),
                            nullif(split_part(source_external_key, ':', 1), '')
                        ),
                        coalesce(
                            nullif(trim(payload ->> 'registration_number'), ''),
                            nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                        )
                    )
                           event_source_id,
                           nullif(trim(source_entity_id), '') as source_registration_entity_id,
                           coalesce(
                               nullif(trim(payload ->> 'registration_nation'), ''),
                               nullif(split_part(source_external_key, ':', 1), '')
                           ) as nation,
                           coalesce(
                               nullif(trim(payload ->> 'registration_number'), ''),
                               nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                           ) as registration_number,
                           source_updated_at
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'VEHICLE_REGISTRATION'
                      and (
                          nullif(trim(payload ->> 'registration_nation'), '') is not null
                          or source_external_key like '%:%'
                      )
                      and (
                          nullif(trim(payload ->> 'registration_number'), '') is not null
                          or source_external_key like '%:%'
                      )
                    order by nullif(trim(source_entity_id), ''),
                             coalesce(
                                 nullif(trim(payload ->> 'registration_nation'), ''),
                                 nullif(split_part(source_external_key, ':', 1), '')
                             ),
                             coalesce(
                                 nullif(trim(payload ->> 'registration_number'), ''),
                                 nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                             ),
                             updated_at desc
                ),
                canonical_registrations as (
                    select distinct on (master.nation, master.registration_number)
                           master.nation,
                           master.registration_number,
                           master.source_updated_at
                    from master_registrations master
                    where master.nation is not null
                      and master.registration_number is not null
                    order by master.nation,
                             master.registration_number,
                             case when master.source_registration_entity_id is null then 1 else 0 end,
                             master.source_updated_at desc,
                             master.source_registration_entity_id
                ),
                existing_registrations as (
                    select distinct on (existing.nation, existing.registration_number)
                           existing.id,
                           existing.nation,
                           existing.registration_number
                    from eventhub.vehicle_registration existing
                    order by existing.nation,
                             existing.registration_number,
                             existing.updated_at desc,
                             existing.created_at desc,
                             existing.id
                ),
                resolved_registrations as (
                    select canonical.nation,
                           canonical.registration_number,
                           canonical.source_updated_at,
                           coalesce(existing.id, gen_random_uuid()) as registration_id
                    from canonical_registrations canonical
                    left join existing_registrations existing
                      on existing.nation = canonical.nation
                     and existing.registration_number = canonical.registration_number
                )
                insert into eventhub.vehicle_registration(
                    id, nation, registration_number, source_updated_at, payload, updated_at
                )
                select distinct on (resolved.registration_id)
                       resolved.registration_id,
                       resolved.nation,
                       resolved.registration_number,
                       resolved.source_updated_at,
                       jsonb_build_object('source', 'master-data'),
                       now()
                from resolved_registrations resolved
                where not exists (
                    select 1
                    from eventhub.vehicle_registration existing
                    where existing.id = resolved.registration_id
                  )
                """,
                eventSourceId,
                tenantKey
        );

        int updatedRegistrations = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_registrations as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        coalesce(
                            nullif(trim(payload ->> 'registration_nation'), ''),
                            nullif(split_part(source_external_key, ':', 1), '')
                        ),
                        coalesce(
                            nullif(trim(payload ->> 'registration_number'), ''),
                            nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                        )
                    )
                           nullif(trim(source_entity_id), '') as source_registration_entity_id,
                           coalesce(
                               nullif(trim(payload ->> 'registration_nation'), ''),
                               nullif(split_part(source_external_key, ':', 1), '')
                           ) as nation,
                           coalesce(
                               nullif(trim(payload ->> 'registration_number'), ''),
                               nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                           ) as registration_number,
                           source_updated_at
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'VEHICLE_REGISTRATION'
                      and (
                          nullif(trim(payload ->> 'registration_nation'), '') is not null
                          or source_external_key like '%:%'
                      )
                      and (
                          nullif(trim(payload ->> 'registration_number'), '') is not null
                          or source_external_key like '%:%'
                      )
                    order by nullif(trim(source_entity_id), ''),
                             coalesce(
                                 nullif(trim(payload ->> 'registration_nation'), ''),
                                 nullif(split_part(source_external_key, ':', 1), '')
                             ),
                             coalesce(
                                 nullif(trim(payload ->> 'registration_number'), ''),
                                 nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                             ),
                             updated_at desc
                ),
                canonical_registrations as (
                    select distinct on (master.nation, master.registration_number)
                           master.nation,
                           master.registration_number,
                           master.source_updated_at
                    from master_registrations master
                    where master.nation is not null
                      and master.registration_number is not null
                    order by master.nation,
                             master.registration_number,
                             case when master.source_registration_entity_id is null then 1 else 0 end,
                             master.source_updated_at desc,
                             master.source_registration_entity_id
                ),
                existing_registrations as (
                    select distinct on (existing.nation, existing.registration_number)
                           existing.id,
                           existing.nation,
                           existing.registration_number
                    from eventhub.vehicle_registration existing
                    order by existing.nation,
                             existing.registration_number,
                             existing.updated_at desc,
                             existing.created_at desc,
                             existing.id
                ),
                resolved_registrations as (
                    select canonical.source_updated_at,
                           existing.id as registration_id
                    from canonical_registrations canonical
                    join existing_registrations existing
                      on existing.nation = canonical.nation
                     and existing.registration_number = canonical.registration_number
                )
                update eventhub.vehicle_registration registration
                set source_updated_at = coalesce(resolved.source_updated_at, registration.source_updated_at),
                    payload = registration.payload || jsonb_build_object('source', 'master-data'),
                    updated_at = now()
                from resolved_registrations resolved
                where registration.id = resolved.registration_id
                """,
                eventSourceId,
                tenantKey
        );

        int linkedRegistrations = jdbcTemplate.update(
                compatibleSourcesCte() + """
                , master_registrations as (
                    select distinct on (
                        nullif(trim(source_entity_id), ''),
                        coalesce(
                            nullif(trim(payload ->> 'registration_nation'), ''),
                            nullif(split_part(source_external_key, ':', 1), '')
                        ),
                        coalesce(
                            nullif(trim(payload ->> 'registration_number'), ''),
                            nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                        )
                    )
                           event_source_id,
                           nullif(trim(source_entity_id), '') as source_registration_entity_id,
                           coalesce(
                               nullif(trim(payload ->> 'registration_nation'), ''),
                               nullif(split_part(source_external_key, ':', 1), '')
                           ) as nation,
                           coalesce(
                               nullif(trim(payload ->> 'registration_number'), ''),
                               nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                           ) as registration_number,
                           source_updated_at
                    from eventhub.source_master_entity
                    where tenant_key = ?
                      and event_source_id in (select id from compatible_sources)
                      and entity_type = 'VEHICLE_REGISTRATION'
                      and nullif(trim(source_entity_id), '') is not null
                    order by nullif(trim(source_entity_id), ''),
                             coalesce(
                                 nullif(trim(payload ->> 'registration_nation'), ''),
                                 nullif(split_part(source_external_key, ':', 1), '')
                             ),
                             coalesce(
                                 nullif(trim(payload ->> 'registration_number'), ''),
                                 nullif(substring(source_external_key from position(':' in source_external_key) + 1), '')
                             ),
                             updated_at desc
                ),
                existing_registrations as (
                    select distinct on (existing.nation, existing.registration_number)
                           existing.id,
                           existing.nation,
                           existing.registration_number
                    from eventhub.vehicle_registration existing
                    order by existing.nation,
                             existing.registration_number,
                             existing.updated_at desc,
                             existing.created_at desc,
                             existing.id
                ),
                resolved_registrations as (
                    select master.event_source_id,
                           master.source_registration_entity_id,
                           master.source_updated_at,
                           coalesce(identity.vehicle_registration_id, existing.id) as registration_id
                    from master_registrations master
                    left join lateral (
                        select identity.vehicle_registration_id
                        from eventhub.source_vehicle_registration_identity identity
                        where identity.tenant_key = ?
                          and identity.event_source_id in (select id from compatible_sources)
                          and identity.source_registration_entity_id = master.source_registration_entity_id
                        order by identity.updated_at desc, identity.id desc
                        limit 1
                    ) identity on true
                    left join existing_registrations existing
                      on existing.nation = master.nation
                     and existing.registration_number = master.registration_number
                )
                insert into eventhub.source_vehicle_registration_identity(
                    id, tenant_key, event_source_id, source_registration_entity_id,
                    vehicle_registration_id, source_updated_at, payload, updated_at
                )
                select gen_random_uuid(),
                       ?,
                       resolved.event_source_id,
                       resolved.source_registration_entity_id,
                       resolved.registration_id,
                       resolved.source_updated_at,
                       '{}'::jsonb,
                       now()
                from resolved_registrations resolved
                where resolved.source_registration_entity_id is not null
                  and resolved.registration_id is not null
                on conflict (tenant_key, event_source_id, source_registration_entity_id)
                do update set
                    vehicle_registration_id = excluded.vehicle_registration_id,
                    source_updated_at = coalesce(excluded.source_updated_at, eventhub.source_vehicle_registration_identity.source_updated_at),
                    updated_at = now()
                """,
                eventSourceId,
                tenantKey,
                tenantKey,
                tenantKey
        );

        return insertedRegistrations + updatedRegistrations + linkedRegistrations;
    }

    private UUID resolveVehicleId(
            String tenantKey,
            int eventSourceId,
            String sourceVehicleEntityId,
            String vin
    ) {
        UUID vehicleId = findVehicleBySourceVehicleEntityId(tenantKey, eventSourceId, sourceVehicleEntityId);
        if (vehicleId == null) {
            vehicleId = findVehicleByVin(vin);
        }
        return vehicleId;
    }

    private UUID resolveRegistrationId(
            String tenantKey,
            int eventSourceId,
            String sourceRegistrationEntityId,
            String nation,
            String registrationNumber
    ) {
        if (isYellowFoxSyntheticRegistrationNation(nation) && registrationNumber != null) {
            UUID registrationId = findRegistrationByNumber(registrationNumber);
            if (registrationId != null) {
                return registrationId;
            }
            return findRegistrationBySourceRegistrationEntityId(tenantKey, eventSourceId, sourceRegistrationEntityId);
        }
        UUID registrationId = findRegistrationBySourceRegistrationEntityId(tenantKey, eventSourceId, sourceRegistrationEntityId);
        if (registrationId == null) {
            registrationId = findRegistrationByPlate(nation, registrationNumber);
        }
        return registrationId;
    }

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

    private UUID findVehicleByVin(String vin) {
        if (vin == null) {
            return null;
        }
        return jdbcTemplate.query(
                """
                select v.id
                from eventhub.vehicle v
                where v.vin = ?
                order by v.updated_at desc
                limit 1
                """,
                rs -> rs.next() ? (UUID) rs.getObject("id") : null,
                vin
        );
    }

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

    private UUID findRegistrationByPlate(String nation, String registrationNumber) {
        if (nation == null || registrationNumber == null) {
            return null;
        }
        return jdbcTemplate.query(
                """
                select r.id
                from eventhub.vehicle_registration r
                where r.nation = ?
                  and r.registration_number = ?
                order by r.updated_at desc
                limit 1
                """,
                rs -> rs.next() ? (UUID) rs.getObject("id") : null,
                nation,
                registrationNumber
        );
    }

    private UUID findRegistrationByNumber(String registrationNumber) {
        if (registrationNumber == null) {
            return null;
        }
        return jdbcTemplate.query(
                """
                select r.id
                from eventhub.vehicle_registration r
                where r.registration_number = ?
                order by
                    case when r.nation = ? then 0 else 1 end,
                    r.updated_at desc,
                    r.created_at desc,
                    r.id
                limit 1
                """,
                rs -> rs.next() ? (UUID) rs.getObject("id") : null,
                registrationNumber,
                UNKNOWN_REGISTRATION_NATION
        );
    }

    private AssignedVehicleReference resolveAssignedVehicleReference(
            String tenantKey,
            int eventSourceId,
            UUID registrationId,
            OffsetDateTime occurredAt
    ) {
        return jdbcTemplate.query(
                compatibleSourcesCte() + """
                select vehicle_id, valid_from, valid_to
                from eventhub.vehicle_registration_assignment
                where tenant_key = ?
                  and event_source_id in (select id from compatible_sources)
                  and vehicle_registration_id = ?
                  and (? is null or valid_from is null or valid_from <= ?)
                  and (? is null or valid_to is null or ? < valid_to)
                order by valid_from desc nulls last, updated_at desc
                limit 1
                """,
                rs -> rs.next()
                        ? new AssignedVehicleReference(
                                (UUID) rs.getObject("vehicle_id"),
                                rs.getObject("valid_from", OffsetDateTime.class),
                                rs.getObject("valid_to", OffsetDateTime.class)
                        )
                        : null,
                eventSourceId,
                tenantKey,
                registrationId,
                occurredAt,
                occurredAt,
                occurredAt,
                occurredAt
        );
    }

    private UUID createVehicle(String vin) {
        UUID vehicleId = UUID.randomUUID();
        jdbcTemplate.update(
                """
                insert into eventhub.vehicle(id, vin, updated_at)
                values (?, ?, now())
                """,
                vehicleId,
                vin
        );
        return vehicleId;
    }

    private UUID createRegistration(
            String nation,
            String registrationNumber,
            OffsetDateTime sourceUpdatedAt,
            Map<String, Object> payload
    ) {
        if (nation == null || registrationNumber == null) {
            return null;
        }
        UUID registrationId = UUID.randomUUID();
        jdbcTemplate.update(
                """
                insert into eventhub.vehicle_registration(
                    id, nation, registration_number, source_updated_at, payload, updated_at
                ) values (?, ?, ?, ?, ?::jsonb, now())
                """,
                registrationId,
                nation,
                registrationNumber,
                sourceUpdatedAt,
                toJson(payload)
        );
        return registrationId;
    }

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

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

    private void touchVehicle(UUID vehicleId, String vin) {
        if (vehicleId == null || vin == null) {
            return;
        }
        jdbcTemplate.update(
                """
                update eventhub.vehicle
                set vin = coalesce(vin, cast(? as text)),
                    updated_at = now()
                where id = ?
                  and vin is null
                """,
                vin,
                vehicleId
        );
    }

    private int projectVehicleRegistrationAssignments(String tenantKey, int eventSourceId) {
        return jdbcTemplate.update(
                compatibleSourcesCte() + """
                insert into eventhub.vehicle_registration_assignment(
                    id, tenant_key, event_source_id, vehicle_registration_id, vehicle_id,
                    valid_from, valid_to, source_updated_at, payload, updated_at
                )
                select gen_random_uuid(),
                       relation.tenant_key,
                       relation.event_source_id,
                       registration_identity.vehicle_registration_id,
                       vehicle_identity.vehicle_id,
                       relation.valid_from,
                       relation.valid_to,
                       relation.source_updated_at,
                       jsonb_build_object(
                           'source', 'master-data',
                           'sourceRelationId', relation.id,
                           'relationKey', relation.relation_key
                       ),
                       now()
                from eventhub.source_master_relation relation
                join eventhub.source_vehicle_registration_identity registration_identity
                  on registration_identity.tenant_key = relation.tenant_key
                 and registration_identity.event_source_id = relation.event_source_id
                 and registration_identity.source_registration_entity_id = relation.from_source_entity_id
                join eventhub.source_vehicle_identity vehicle_identity
                  on vehicle_identity.tenant_key = relation.tenant_key
                 and vehicle_identity.event_source_id = relation.event_source_id
                 and vehicle_identity.source_vehicle_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 = 'VEHICLE_REGISTRATION_VEHICLE'
                  and relation.from_entity_type = 'VEHICLE_REGISTRATION'
                  and relation.to_entity_type = 'VEHICLE'
                  and not exists (
                      select 1
                      from eventhub.vehicle_registration_assignment existing
                      where existing.tenant_key = relation.tenant_key
                        and existing.event_source_id = relation.event_source_id
                        and existing.vehicle_registration_id = registration_identity.vehicle_registration_id
                        and existing.vehicle_id = vehicle_identity.vehicle_id
                        and existing.valid_from is not distinct from relation.valid_from
                        and existing.valid_to is not distinct from relation.valid_to
                  )
                """,
                eventSourceId,
                tenantKey
        );
    }

    private void touchRegistration(UUID registrationId, String nation, String registrationNumber) {
        if (registrationId == null || (nation == null && registrationNumber == null)) {
            return;
        }
        jdbcTemplate.update(
                """
                update eventhub.vehicle_registration
                set nation = coalesce(cast(? as text), nation),
                    registration_number = coalesce(cast(? as text), registration_number),
                    updated_at = now()
                where id = ?
                  and (
                      (nation is null and cast(? as text) is not null)
                      or (registration_number is null and cast(? as text) is not null)
                  )
                """,
                nation,
                registrationNumber,
                registrationId,
                nation,
                registrationNumber
        );
    }

    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 vehicle identity payload", e);
        }
    }

    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;
    }

    private boolean isYellowFoxSyntheticRegistrationNation(String nation) {
        return YELLOWFOX_SYNTHETIC_REFERENCE_NATION.equalsIgnoreCase(nation);
    }

    private String creationNation(String registrationNation, String registrationNumber) {
        if (registrationNumber == null) {
            return registrationNation;
        }
        return isYellowFoxSyntheticRegistrationNation(registrationNation)
                ? UNKNOWN_REGISTRATION_NATION
                : registrationNation;
    }

    public record ResolvedVehicleReference(UUID vehicleId, UUID vehicleRegistrationId) {
        public static ResolvedVehicleReference empty() {
            return new ResolvedVehicleReference(null, null);
        }
    }

    public record ResolvedVehicleReferenceResolution(
            ResolvedVehicleReference reference,
            boolean resolvedFromAssignment,
            OffsetDateTime assignmentValidFrom,
            OffsetDateTime assignmentValidTo
    ) {
        public static ResolvedVehicleReferenceResolution empty() {
            return new ResolvedVehicleReferenceResolution(ResolvedVehicleReference.empty(), false, null, null);
        }
    }

    private record AssignedVehicleReference(
            UUID vehicleId,
            OffsetDateTime validFrom,
            OffsetDateTime validTo
    ) {
    }
}
