import type { CategoryOverTimeData, OverTimeRow } from "../../shared/types";

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

/**
 * Income-statement reading order: revenue first, then expenses, then transfers,
 * so the "net result" row reads naturally at the bottom (Income − Expenses +
 * Transfers). Matches the compare report's `COMPARE_TYPE_ORDER`, which is also
 * income-first since issue #253 (the earlier expense-first note is obsolete).
 */
export const OVER_TIME_TYPE_ORDER: Record<OverTimeType, number> = {
  income: 0,
  expense: 1,
  transfer: 2,
};

/** A month -> value map plus the sum across all its months. */
export interface OverTimeSeries {
  /** month (YYYY-MM) -> value */
  monthly: Record<string, number>;
  /** sum across every month */
  total: number;
}

/**
 * One rendered section: a type, its member leaf rows (id-keyed, from the tree),
 * and per-month + total subtotals. `rows` are the actual leaves in the section
 * (never subtotal rows), each carrying its own index-aligned `monthly[]`.
 */
export interface OverTimeSection {
  type: OverTimeType;
  rows: OverTimeRow[];
  /** month (YYYY-MM) -> section subtotal */
  monthly: Record<string, number>;
  /** sum across every month */
  total: number;
}

export interface OverTimeAnalysis {
  /** Ordered list of months (mirrors `data.data` order). */
  months: string[];
  /** Sections ordered income → expense → transfer; empty types are dropped. */
  sections: OverTimeSection[];
  /** True when at least one visible transfer-type category is present. */
  hasTransfers: boolean;
  /** Income − Expenses + Transfers, per month (+ grand total). */
  net: OverTimeSeries;
  /** Income − Expenses, per month (+ grand total). Only shown when `hasTransfers`. */
  beforeTransfers: OverTimeSeries;
}

/**
 * Pure reducer that turns the category-over-time report into an "income
 * statement" shape: per-type sections with per-month subtotals, plus the
 * net-result rows.
 *
 * Consumes the **id-keyed tree** (`data.tree`) — NOT the name-keyed pivot — so
 * two homonym categories of different types never collide: each leaf carries its
 * own `category_id`-resolved `category_type`, and the subtotals + result lines
 * are summed over the tree's LEAVES only (never its `is_parent` subtotal rows,
 * which would double-count their own descendants). Leaf `monthly[]` values are
 * positive magnitudes (`ABS(SUM())` in SQL), so income/expense/transfer
 * subtotals are all ≥ 0 and the net is `income − expense + transfer`.
 *
 * The tree is already ordered income → expense → transfer, so section ordering
 * falls out of the leaves' own `category_type`.
 */
export function computeOverTimeResults(data: CategoryOverTimeData): OverTimeAnalysis {
  const months = data.data.map((d) => d.month);

  // Leaves only — subtotals (`is_parent`) are excluded so a parent group can
  // never double-count against its own descendants.
  const typeOf = (row: OverTimeRow): OverTimeType => {
    const t = row.category_type;
    return t === "income" || t === "transfer" ? t : "expense";
  };
  const byType: Record<OverTimeType, OverTimeRow[]> = { income: [], expense: [], transfer: [] };
  for (const row of data.tree) {
    if (!row.is_parent) byType[typeOf(row)].push(row);
  }

  const buildSection = (type: OverTimeType): OverTimeSection => {
    const rows = byType[type];
    const monthly: Record<string, number> = {};
    let total = 0;
    months.forEach((month, i) => {
      const sum = rows.reduce((s, row) => s + (row.monthly[i] ?? 0), 0);
      monthly[month] = sum;
      total += sum;
    });
    return { type, rows, monthly, total };
  };

  const income = buildSection("income");
  const expense = buildSection("expense");
  const transfer = buildSection("transfer");

  const hasTransfers = transfer.rows.length > 0;

  const net: OverTimeSeries = { monthly: {}, total: 0 };
  const beforeTransfers: OverTimeSeries = { monthly: {}, total: 0 };
  for (const month of months) {
    const before = income.monthly[month] - expense.monthly[month];
    const netMonth = before + transfer.monthly[month];
    beforeTransfers.monthly[month] = before;
    beforeTransfers.total += before;
    net.monthly[month] = netMonth;
    net.total += netMonth;
  }

  const sections = [income, expense, transfer]
    .filter((s) => s.rows.length > 0)
    .sort((a, b) => OVER_TIME_TYPE_ORDER[a.type] - OVER_TIME_TYPE_ORDER[b.type]);

  return { months, sections, hasTransfers, net, beforeTransfers };
}
