import type { EntityManager } from 'typeorm';

/**
 * Atomically allocate the next receipt number for a tenant + year +
 * prefix combo. Uses `INSERT ... ON CONFLICT DO UPDATE ... RETURNING`
 * so the increment + read happen in one round-trip and concurrent
 * payments never collide.
 *
 * Format: `{prefix}-{year}-{NNNN}` zero-padded to 4 digits, growing
 * past 4 only if a single tenant clears 9,999 receipts in one year.
 * Annual reset is implicit in the `(tenant_id, year, prefix)` unique
 * key on `settlement_receipt_sequences`.
 *
 * Tenant scope is the caller's responsibility — pass an explicit
 * `tenantId` derived from CLS, never accept it from request input.
 *
 *   prefix = 'RCP' → collections, 'RFD' → refunds, 'PAY' → supplier
 *   payouts (§12e), 'INV' → customer tax invoices (Accounting Tier 1).
 */
export async function allocateReceiptNumber(
  manager: EntityManager,
  tenantId: string,
  year: number,
  prefix: 'RCP' | 'RFD' | 'PAY' | 'INV',
): Promise<{ receipt_no: string; receipt_year: number }> {
  const rows = (await manager.query(
    `INSERT INTO settlement_receipt_sequences (tenant_id, year, prefix, last_number)
     VALUES ($1, $2, $3, 1)
     ON CONFLICT (tenant_id, year, prefix) DO UPDATE
        SET last_number = settlement_receipt_sequences.last_number + 1,
            updated_at  = NOW()
     RETURNING last_number`,
    [tenantId, year, prefix],
  )) as { last_number: number }[];

  const n = rows[0]?.last_number;
  if (!Number.isInteger(n) || n <= 0) {
    // Defensive — should be impossible with the upsert above, but a
    // failed assertion here is a money-side bug we want to crash on.
    throw new Error(
      `allocateReceiptNumber: sequence row did not return a number for ${tenantId}/${year}/${prefix}`,
    );
  }

  const padded = String(n).padStart(4, '0');
  return { receipt_no: `${prefix}-${year}-${padded}`, receipt_year: year };
}
