/**
 * Shared per-day sequential invoice numbering, used by every billing source
 * (freight `FRT-…`, warehouse fees `WHF-…`, …) so the format and the
 * `MAX(seq)+1` allocation live in one place instead of being copy-pasted per
 * service.
 *
 * Produces `<CODE>-YYYYMMDD-00001`: the sequence is the max existing suffix for
 * the day + 1. Run inside the caller's transaction (pass that transaction's
 * manager) so concurrent generation within a transaction stays consistent.
 */

/** Anything exposing TypeORM's `.query` — an `EntityManager` or `DataSource`. */
export interface SqlRunner {
  query(sql: string, params?: unknown[]): Promise<unknown>;
}

export interface InvoiceNumberOptions {
  /** Schema-qualified table to scan, e.g. `freight.invoices`. */
  table: string;
  /** Document code prefix, e.g. `FRT` or `WHF`. */
  code: string;
  /** Column holding the number; defaults to `invoice_number`. */
  column?: string;
  /** Clock injection point (tests); defaults to now. */
  now?: Date;
}

export async function nextDailyInvoiceNumber(
  runner: SqlRunner,
  opts: InvoiceNumberOptions,
): Promise<string> {
  const now = opts.now ?? new Date();
  const ymd = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}`;
  const prefix = `${opts.code}-${ymd}-`;
  const column = opts.column ?? "invoice_number";

  // Serialize concurrent allocation for this exact day+code prefix so two
  // simultaneous transactions can't both read the same MAX(seq) and mint a
  // duplicate number. Session-scoped to the caller's transaction — released
  // automatically on commit/rollback. Different prefixes hash to different
  // keys and never contend with each other.
  await runner.query(`SELECT pg_advisory_xact_lock(hashtext($1))`, [prefix]);

  const rows = (await runner.query(
    `SELECT COALESCE(MAX(CAST(split_part(${column}, '-', 3) AS int)), 0) AS seq
       FROM ${opts.table} WHERE ${column} LIKE $1`,
    [`${prefix}%`],
  )) as Array<{ seq: number | string }>;
  const next = Number(rows[0]?.seq ?? 0) + 1;
  return `${prefix}${String(next).padStart(5, "0")}`;
}
