import { queryRaw, queryRow } from "@/lib/db";

/**
 * A split has to add up to 100%.
 *
 * The database was happy to hold a transaction whose only split row said
 * "Sonu 50%", because everything that *reads* a split treats the payer's share
 * as whatever is left over — see `myShare` in analytics-sql.ts, which falls back
 * to `100 - SUM(everyone else)`. The arithmetic was never wrong.
 *
 * It was still a bug, because a ledger is read as well as computed. On screen
 * that row is a 50% share and a blank, which looks like half the money is
 * unallocated, and there is no way to tell it apart from a split someone left
 * half-finished. It also leaks: the participant filter on the Shared view
 * (`getSharedTransactions`) selects transactions that have an explicit row for
 * that participant, so filtering by the payer silently drops every transaction
 * where their share was only ever implied.
 *
 * So the remainder gets written down. `completeSplit` is the single place that
 * does it, and every write path ends in it.
 *
 * **This is deliberately balance-neutral.** The owner's row on their own
 * transaction is excluded from both halves of the balance query in
 * `getParticipantBalances` — "they owe me" reads `ts.participant_id != $1` on
 * transactions I own, "I owe them" reads transactions I do not own. So adding a
 * row for the transaction's own owner cannot create, enlarge or discharge a
 * debt; it makes explicit the number every reader was already inferring. That
 * is why the remainder is assigned to the *owner* rather than to "me": a row
 * for me on someone else's transaction is a real obligation, and this helper
 * must never invent one of those.
 */

/** The owner whose share is implied: the transaction's, falling back to its statement's. */
async function effectiveOwner(transactionId: number): Promise<number | null> {
  const row = await queryRow<{ owner_id: number | null }>(
    `SELECT COALESCE(t.owner_id, s.owner_id) AS owner_id
       FROM transactions t
       LEFT JOIN statements s ON s.id = t.statement_id
      WHERE t.id = $1`,
    [transactionId]
  );
  return row?.owner_id ?? null;
}

/**
 * Materialise the owner's share so the split totals 100%.
 *
 * Does nothing to a transaction with no splits — an unshared transaction is not
 * a 100% split of itself, and writing one would put every row in the Shared
 * view. Does nothing when the other shares already total 100% (they owe all of
 * it, and the owner's share is a genuine zero), beyond removing a stale owner
 * row if one is left behind.
 *
 * Over-allocated splits (>100%) are left exactly as they are. That is a
 * mistake the caller should have rejected, and quietly deleting someone's share
 * to force the total down would destroy the evidence of it.
 */
export async function completeSplit(transactionId: number): Promise<void> {
  const owner = await effectiveOwner(transactionId);
  if (owner === null) return;

  const rows = await queryRaw<{ participant_id: number; share_percent: string }>(
    `SELECT participant_id, share_percent FROM transaction_splits WHERE transaction_id = $1`,
    [transactionId]
  );
  if (rows.length === 0) return;

  const others = rows
    .filter((r) => r.participant_id !== owner)
    .reduce((sum, r) => sum + Number(r.share_percent), 0);
  const remainder = Number((100 - others).toFixed(2));

  if (remainder > 0.01) {
    await queryRaw(
      `INSERT INTO transaction_splits (transaction_id, participant_id, share_percent)
       VALUES ($1, $2, $3)
       ON CONFLICT (transaction_id, participant_id)
         DO UPDATE SET share_percent = EXCLUDED.share_percent`,
      [transactionId, owner, remainder]
    );
  } else if (remainder > -0.01) {
    // Exactly nothing left for the owner. Drop their row rather than storing a
    // 0% share, so "they owe all of it" keeps looking like one row, not two.
    await queryRaw(
      `DELETE FROM transaction_splits WHERE transaction_id = $1 AND participant_id = $2`,
      [transactionId, owner]
    );
  }
}

/** `completeSplit` over many transactions, for bulk and rule-run paths. */
export async function completeSplits(transactionIds: number[]): Promise<void> {
  for (const id of transactionIds) await completeSplit(id);
}
