create table if not exists eventhub.driver (
    id uuid primary key,
    tenant_key text not null,
    event_source_id integer not null references eventhub.event_source(id),
    source_driver_entity_id text,
    card_nation text,
    card_number text,
    first_names text,
    last_name text,
    birth_date date,
    source_updated_at timestamptz,
    payload jsonb not null default '{}'::jsonb,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint chk_driver_card_nation_when_number
        check (card_number is null or card_nation is not null)
);

insert into eventhub.driver(
    id, tenant_key, event_source_id, source_driver_entity_id,
    card_nation, card_number, first_names, last_name, birth_date,
    source_updated_at, payload, created_at, updated_at
)
select gen_random_uuid(),
       sme.tenant_key,
       sme.event_source_id,
       case
           when sme.source_entity_id like 'DRIVER_CARD:%' then null
           else sme.source_entity_id
       end as source_driver_entity_id,
       coalesce(
           nullif(trim(sme.payload ->> 'card_nation'), ''),
           nullif(trim(sme.payload ->> 'driver_card_nation'), ''),
           nullif(split_part(case when sme.source_entity_id like 'DRIVER_CARD:%' then substring(sme.source_entity_id from length('DRIVER_CARD:') + 1) else null end, ':', 1), '')
       ) as card_nation,
       coalesce(
           nullif(trim(sme.payload ->> 'card_number'), ''),
           nullif(trim(sme.payload ->> 'driver_card_number'), ''),
           nullif(substring(case when sme.source_entity_id like 'DRIVER_CARD:%' then substring(sme.source_entity_id from length('DRIVER_CARD:') + 1) else null end from position(':' in case when sme.source_entity_id like 'DRIVER_CARD:%' then substring(sme.source_entity_id from length('DRIVER_CARD:') + 1) else '' end) + 1), '')
       ) as card_number,
       coalesce(
           nullif(trim(sme.payload ->> 'first_names'), ''),
           nullif(trim(sme.payload ->> 'firstnames'), '')
       ) as first_names,
       coalesce(
           nullif(trim(sme.payload ->> 'last_name'), ''),
           nullif(trim(sme.payload ->> 'surname'), '')
       ) as last_name,
       cast(nullif(trim(sme.payload ->> 'birth_date'), '') as date) as birth_date,
       sme.source_updated_at,
       sme.payload,
       sme.created_at,
       sme.updated_at
from eventhub.source_master_entity sme
where sme.entity_type = 'DRIVER'
  and not exists (
      select 1
      from eventhub.driver existing
      where existing.tenant_key = sme.tenant_key
        and existing.event_source_id = sme.event_source_id
        and existing.source_driver_entity_id is not distinct from case
            when sme.source_entity_id like 'DRIVER_CARD:%' then null
            else sme.source_entity_id
        end
        and existing.card_nation is not distinct from coalesce(
            nullif(trim(sme.payload ->> 'card_nation'), ''),
            nullif(trim(sme.payload ->> 'driver_card_nation'), ''),
            nullif(split_part(case when sme.source_entity_id like 'DRIVER_CARD:%' then substring(sme.source_entity_id from length('DRIVER_CARD:') + 1) else null end, ':', 1), '')
        )
        and existing.card_number is not distinct from coalesce(
            nullif(trim(sme.payload ->> 'card_number'), ''),
            nullif(trim(sme.payload ->> 'driver_card_number'), ''),
            nullif(substring(case when sme.source_entity_id like 'DRIVER_CARD:%' then substring(sme.source_entity_id from length('DRIVER_CARD:') + 1) else null end from position(':' in case when sme.source_entity_id like 'DRIVER_CARD:%' then substring(sme.source_entity_id from length('DRIVER_CARD:') + 1) else '' end) + 1), '')
        )
  );

alter table eventhub.event
    add column if not exists driver_id uuid references eventhub.driver(id);

with source_driver_map as (
    select distinct on (sme.id)
           sme.id as source_master_entity_id,
           driver.id as driver_id
    from eventhub.source_master_entity sme
    join eventhub.driver driver
      on driver.tenant_key = sme.tenant_key
     and driver.event_source_id = sme.event_source_id
     and (
         (
             sme.source_entity_id not like 'DRIVER_CARD:%'
             and driver.source_driver_entity_id = sme.source_entity_id
         ) or (
             sme.source_entity_id like 'DRIVER_CARD:%'
             and driver.card_nation is not distinct from coalesce(
                 nullif(trim(sme.payload ->> 'card_nation'), ''),
                 nullif(trim(sme.payload ->> 'driver_card_nation'), ''),
                 nullif(split_part(substring(sme.source_entity_id from length('DRIVER_CARD:') + 1), ':', 1), '')
             )
             and driver.card_number is not distinct from coalesce(
                 nullif(trim(sme.payload ->> 'card_number'), ''),
                 nullif(trim(sme.payload ->> 'driver_card_number'), ''),
                 nullif(substring(substring(sme.source_entity_id from length('DRIVER_CARD:') + 1) from position(':' in substring(sme.source_entity_id from length('DRIVER_CARD:') + 1)) + 1), '')
             )
         )
     )
    where sme.entity_type = 'DRIVER'
    order by sme.id, driver.updated_at desc
)
update eventhub.event event
set driver_id = map.driver_id
from source_driver_map map
where event.driver_entity_id = map.source_master_entity_id
  and event.driver_id is null;

create index if not exists idx_driver_source_entity
    on eventhub.driver(tenant_key, event_source_id, source_driver_entity_id)
    where source_driver_entity_id is not null;

create index if not exists idx_driver_card
    on eventhub.driver(tenant_key, event_source_id, card_nation, card_number)
    where card_number is not null;

create index if not exists idx_event_driver_time
    on eventhub.event(driver_id, occurred_at desc)
    where driver_id is not null;

alter table eventhub.event
    drop constraint if exists chk_event_driver_or_vehicle_ref;

alter table eventhub.event
    add constraint chk_event_driver_or_vehicle_ref
        check (
            driver_id is not null
            or driver_entity_id is not null
            or vehicle_id is not null
            or vehicle_registration_id is not null
        );
