-- Invoice numbers were generated client-side from a row count, which reuses
-- numbers after deletes and collides under concurrency. Replace with a DB
-- function that takes max+1 for the year under an advisory lock, and enforce
-- uniqueness at the schema level.

create or replace function public.next_invoice_number()
returns text
language plpgsql
security definer
set search_path = public
as $$
declare
  yr text := to_char(now(), 'YYYY');
  next_n int;
begin
  -- Serialize concurrent invoice creation within this transaction
  perform pg_advisory_xact_lock(hashtext('invoice_number_' || yr));

  select coalesce(
    max((regexp_match(invoice_number, '^INV-' || yr || '-(\d+)$'))[1]::int),
    0
  ) + 1
  into next_n
  from public.invoices
  where invoice_number like 'INV-' || yr || '-%';

  return 'INV-' || yr || '-' || lpad(next_n::text, 3, '0');
end;
$$;

revoke all on function public.next_invoice_number() from public;
grant execute on function public.next_invoice_number() to authenticated;

-- Enforce uniqueness going forward (fails if historical duplicates exist —
-- those must be reviewed manually first, never auto-renumbered).
create unique index if not exists invoices_invoice_number_key
  on public.invoices (invoice_number);
