import type { BudgetYearRow } from "../../shared/types";

export type BudgetSectionType = "income" | "expense" | "transfer";

/**
 * Sign multiplier applied to budgeted (months/annual) magnitudes. Budgeted
 * amounts are entered/stored as positive magnitudes regardless of type and
 * flipped for display by category type — mirrors `BudgetTable`'s `signFor`.
 */
export function signForBudgetType(type: BudgetSectionType): 1 | -1 {
  return type === "expense" ? -1 : 1;
}

/**
 * Aggregate of the budget grid's 3 comparable column groups, already signed:
 * 12 monthly (budgeted) totals, the annual (budgeted) total, and the
 * previous-year (actual) total.
 */
export interface BudgetTotals {
  /** index 0-11 = Jan-Dec, signed budgeted total */
  months: number[];
  /** signed budgeted annual total */
  annual: number;
  /** signed actual total from the previous year (already signed in the DB) */
  previousYearTotal: number;
}

function zeroTotals(): BudgetTotals {
  return { months: Array(12).fill(0) as number[], annual: 0, previousYearTotal: 0 };
}

/**
 * Sum every non-subtotal (leaf) row of a single type into one `BudgetTotals`.
 *
 * The type's sign is applied to the budgeted figures (`months`/`annual`),
 * matching how `BudgetTable` displays them. `previousYearTotal` is NOT
 * re-signed: it comes from `getActualTotalsForYear` (a plain signed
 * `SUM(amount)` over the transactions table), already negative for expenses
 * and ~0-net for balanced transfers — the same convention `compareResults.ts`
 * relies on for its own previous-period figures.
 */
export function sumLeavesForType(rows: BudgetYearRow[], type: BudgetSectionType): BudgetTotals {
  const sign = signForBudgetType(type);
  const totals = zeroTotals();
  for (const row of rows) {
    if (row.is_parent || row.category_type !== type) continue;
    for (let m = 0; m < 12; m++) totals.months[m] += row.months[m] * sign;
    totals.annual += row.annual * sign;
    totals.previousYearTotal += row.previousYearTotal;
  }
  return totals;
}

/** Component-wise sum of two BudgetTotals (both already signed). */
function combine(a: BudgetTotals, b: BudgetTotals): BudgetTotals {
  return {
    months: a.months.map((v, i) => v + b.months[i]),
    annual: a.annual + b.annual,
    previousYearTotal: a.previousYearTotal + b.previousYearTotal,
  };
}

export interface BudgetResults {
  income: BudgetTotals;
  expense: BudgetTotals;
  transfer: BudgetTotals;
  /** Budgeted/actual revenues − expenses: the operating result, before transfers. */
  resultBefore: BudgetTotals;
  /** resultBefore + transfers: the bottom-line total. */
  resultNet: BudgetTotals;
  hasTransfers: boolean;
}

/**
 * Income-statement roll-up for the budget grid (Issue #278), mirroring
 * `compareResults.ts`'s shape (see that module's doc comment for the sign
 * convention this relies on).
 *
 * Operates on the flat `BudgetYearRow[]` the grid already renders (produced
 * by `useBudget`) — LEAVES only (`is_parent === false`), so parent subtotal
 * rows are never double-counted. All three column groups (previous-year
 * actual, budgeted annual, budgeted monthly) are folded into the same result
 * rows so "Résultat avant transferts" / "Résultat net" read consistently
 * across every column of the grid.
 */
export function computeBudgetResults(rows: BudgetYearRow[]): BudgetResults {
  const income = sumLeavesForType(rows, "income");
  const expense = sumLeavesForType(rows, "expense");
  const transfer = sumLeavesForType(rows, "transfer");
  const resultBefore = combine(income, expense);
  const resultNet = combine(resultBefore, transfer);
  const hasTransfers = rows.some((r) => !r.is_parent && r.category_type === "transfer");
  return { income, expense, transfer, resultBefore, resultNet, hasTransfers };
}
