-- Fix historical customer-code duplicates before restoring the intended constraint.
-- The first row keeps its business code; later duplicates receive a traceable migration code.
begin;

set local lock_timeout = '10s';
set local statement_timeout = '5min';

select pg_advisory_xact_lock(hashtext('20260713_fix_crm_codes_pg17'));

with ranked_customer_codes as (
  select
    id,
    row_number() over (partition by customer_code order by created_at nulls last, id) as row_no
  from crm_customer
  where customer_code is not null
    and btrim(customer_code) <> ''
)
update crm_customer customer
set customer_code = 'CUS-MIG-' || customer.id::text,
    updated_at = now()
from ranked_customer_codes duplicate
where duplicate.id = customer.id
  and duplicate.row_no > 1;

do $$
declare
  constraint_definition text;
begin
  select pg_get_constraintdef(oid)
  into constraint_definition
  from pg_constraint
  where conrelid = 'crm_customer'::regclass
    and conname = 'uk_crm_customer_code';

  if constraint_definition is not null
     and constraint_definition not ilike 'UNIQUE (customer_code)%' then
    alter table crm_customer drop constraint uk_crm_customer_code;
    constraint_definition := null;
  end if;

  if constraint_definition is null then
    alter table crm_customer
      add constraint uk_crm_customer_code unique (customer_code);
  end if;
end $$;

create sequence if not exists crm_customer_code_seq start with 1 increment by 1 minvalue 1;
create sequence if not exists crm_opportunity_code_seq start with 1 increment by 1 minvalue 1;

-- Lock sequence consumers before reading their current values.
alter sequence crm_customer_code_seq no cycle;
alter sequence crm_opportunity_code_seq no cycle;

do $$
declare
  sales_id_sequence regclass;
  channel_id_sequence regclass;
begin
  if to_regclass('crm_sales_expansion') is not null then
    sales_id_sequence := pg_get_serial_sequence('crm_sales_expansion', 'id')::regclass;
  end if;
  if to_regclass('crm_channel_expansion') is not null then
    channel_id_sequence := pg_get_serial_sequence('crm_channel_expansion', 'id')::regclass;
  end if;

  if sales_id_sequence is not null then
    execute format('alter sequence %s no cycle', sales_id_sequence);
  end if;
  if channel_id_sequence is not null then
    execute format('alter sequence %s no cycle', channel_id_sequence);
  end if;
end $$;

-- ALTER SEQUENCE RESTART is transactional; setval() would survive a rollback.
do $$
declare
  customer_code_next bigint;
  opportunity_code_next bigint;
begin
  select greatest(
    coalesce((
      select max(substring(customer_code from '([0-9]+)$')::bigint) + 1
      from crm_customer
      where customer_code ~ '^CUS-[0-9]{8}-[0-9]+$'
    ), 1),
    (select case when is_called then last_value + 1 else last_value end from crm_customer_code_seq)
  )
  into customer_code_next;

  select greatest(
    coalesce((
      select max(substring(opportunity_code from '([0-9]+)$')::bigint) + 1
      from crm_opportunity
      where opportunity_code ~ '^OPP-[0-9]{8}-[0-9]+$'
    ), 1),
    (select case when is_called then last_value + 1 else last_value end from crm_opportunity_code_seq)
  )
  into opportunity_code_next;

  execute format('alter sequence crm_customer_code_seq restart with %s', customer_code_next);
  execute format('alter sequence crm_opportunity_code_seq restart with %s', opportunity_code_next);
end $$;

do $$
declare
  sales_id_sequence regclass;
  channel_id_sequence regclass;
  sales_id_max bigint;
  sales_id_current_next bigint;
  channel_id_max bigint;
  channel_id_current_next bigint;
begin
  if to_regclass('crm_sales_expansion') is not null then
    sales_id_sequence := pg_get_serial_sequence('crm_sales_expansion', 'id')::regclass;
  end if;
  if to_regclass('crm_channel_expansion') is not null then
    channel_id_sequence := pg_get_serial_sequence('crm_channel_expansion', 'id')::regclass;
  end if;

  if sales_id_sequence is not null then
    select coalesce(max(id), 0) into sales_id_max from crm_sales_expansion;
    if sales_id_max = 9223372036854775807 then
      raise exception 'crm_sales_expansion identity sequence has exhausted bigint values';
    end if;
    execute format(
      'select case when is_called then last_value + 1 else last_value end from %s',
      sales_id_sequence
    ) into sales_id_current_next;
    execute format(
      'alter sequence %s restart with %s',
      sales_id_sequence,
      greatest(sales_id_max + 1, sales_id_current_next)
    );
  end if;

  if channel_id_sequence is not null then
    select coalesce(max(id), 0) into channel_id_max from crm_channel_expansion;
    if channel_id_max = 9223372036854775807 then
      raise exception 'crm_channel_expansion identity sequence has exhausted bigint values';
    end if;
    execute format(
      'select case when is_called then last_value + 1 else last_value end from %s',
      channel_id_sequence
    ) into channel_id_current_next;
    execute format(
      'alter sequence %s restart with %s',
      channel_id_sequence,
      greatest(channel_id_max + 1, channel_id_current_next)
    );
  end if;
end $$;

commit;
