-- Fix: bill_approvals not reflecting "paid" when an accounting.bills row is
-- created already-paid.
--
-- Background (see 20260601140000_accounting_sync_bills.sql):
--   accounting.sync_accounting_bill_paid() flips public.bills + public.bill_approvals
--   to 'paid' once the matching accounting bill is fully paid. It runs from the
--   trigger trg_acct_bill_paid_back, which was AFTER UPDATE only.
--
--   When a public bill is ALREADY paid at the moment it first mirrors into
--   accounting, the forward sync INSERTs the accounting row at status='paid'
--   with paid_amount set. With no subsequent UPDATE to paid_amount/total, the
--   UPDATE-only trigger never fired, so the bill's approvals were left stuck at
--   'approved'/'pending'. This widens the back-sync to also run on INSERT.

-- ---------------------------------------------------------------------------
-- Back-sync trigger: now fires on INSERT as well as UPDATE.
-- ---------------------------------------------------------------------------
create or replace function accounting.tg_accounting_bill_paid_sync()
returns trigger
language plpgsql security definer set search_path to 'public','accounting'
as $$
begin
  begin
    if tg_op = 'INSERT' then
      -- a bill mirrored in already-paid never produces an UPDATE; handle it here
      perform accounting.sync_accounting_bill_paid(new.id);
    -- on UPDATE only act when the paid position actually changed, to avoid loops
    elsif old.paid_amount is distinct from new.paid_amount
       or old.total is distinct from new.total then
      perform accounting.sync_accounting_bill_paid(new.id);
    end if;
  exception when others then
    raise warning 'accounting: accounting bill paid sync failed for %: %', new.id, sqlerrm;
  end;
  return new;
end;
$$;

drop trigger if exists trg_acct_bill_paid_back on accounting.bills;
create trigger trg_acct_bill_paid_back
  after insert or update on accounting.bills
  for each row execute function accounting.tg_accounting_bill_paid_sync();

-- ---------------------------------------------------------------------------
-- One-time reconciliation: back-sync any accounting bills that are already
-- fully paid but whose linked public bill / approvals were never updated
-- (the rows that fell into the INSERT gap above).
-- ---------------------------------------------------------------------------
do $$
declare _id uuid;
begin
  for _id in
    select id from accounting.bills
    where external_source = 'acmacc_bill' and external_id is not null
      and coalesce(paid_amount,0) >= coalesce(total,0) and coalesce(total,0) > 0
  loop
    perform accounting.sync_accounting_bill_paid(_id);
  end loop;
end $$;

-- ---------------------------------------------------------------------------
-- Backfill orphaned, invoice-track approvals (bill_id IS NULL) that were
-- created via the invoice flow and never linked to a public.bills row.
-- Only link when exactly one matching bill exists for the same association,
-- invoice number and amount; adopt that bill's status when it is paid.
-- ---------------------------------------------------------------------------
update public.bill_approvals ba
set bill_id = m.bill_id,
    status  = case when m.bill_status = 'paid' then 'paid' else ba.status end,
    updated_at = now()
from (
  select i.id as invoice_id, b.id as bill_id, b.status as bill_status
  from public.invoices i
  join public.bills b
    on b.association_id = i.association_id
   and b.amount = i.amount
   and b.invoice_number is not distinct from i.invoice_number
  group by i.id, b.id, b.status
  having (
    select count(*) from public.bills b2
    where b2.association_id = i.association_id
      and b2.amount = i.amount
      and b2.invoice_number is not distinct from i.invoice_number
  ) = 1
) m
where ba.bill_id is null
  and ba.invoice_id = m.invoice_id;
