import type { OverTimeRow } from "../../shared/types";
import type { CollapseAccessors } from "../../utils/collapsibleRows";
import type { OverTimeAnalysis, OverTimeType } from "./overTimeResults";

/**
 * Render-model helpers for the hierarchical trends table (issue #265). Kept in a
 * pure module — the project has no React render harness, so the grouping and
 * result-interleaving logic is unit-tested here while `CategoryOverTimeTable`
 * stays a thin renderer.
 */

/**
 * Collapse groups keyed by category id; a row is hidden when any ancestor
 * (walking parent_id) is collapsed, so every parent level is collapsible. The
 * `OverTimeRow` hierarchy block is snake_case (mirrors `CategoryDelta`), so these
 * accessors compose with `collapsibleRows` / `useCollapsibleGroups` unchanged.
 */
export const OVERTIME_COLLAPSE_ACCESSORS: CollapseAccessors<OverTimeRow> = {
  keyOf: (row) => `p:${row.categoryId}`,
  parentKeyOf: (row) => (row.parent_id != null ? `p:${row.parent_id}` : null),
  depthOf: (row) => row.depth,
  isParent: (row) => row.is_parent,
};

/** Distinct localStorage key so the trends expansion state never collides with
 * the comparable tables' `reports-compare-expanded` (issue #265). */
export const OVERTIME_EXPANDED_STORAGE_KEY = "reports-trends-expanded";

/**
 * One rendered section: its type, the FULL hierarchy rows of that type (parents
 * + leaves, parent-first depth-first — straight off `data.tree`), and the
 * leaf-summed per-month + total subtotals carried over from the reducer.
 *
 * `rows` carries the parents (unlike `OverTimeSection.rows`, which is leaves
 * only) so the table can render the hierarchy and collapse it; `monthly`/`total`
 * stay the reducer's leaf sums, so they are unaffected by which rows are folded
 * away — the "collapse is purely visual" invariant.
 */
export interface OverTimeRenderSection {
  type: OverTimeType;
  rows: OverTimeRow[];
  /** month (YYYY-MM) -> section subtotal (leaf-summed, collapse-invariant). */
  monthly: Record<string, number>;
  /** sum across every month (leaf-summed, collapse-invariant). */
  total: number;
}

const typeOf = (row: OverTimeRow): OverTimeType => {
  const t = row.category_type;
  return t === "income" || t === "transfer" ? t : "expense";
};

/**
 * Splits the id-keyed tree into render sections, driven by the reducer's own
 * (ordered, non-empty, income → expense → transfer) `analysis.sections` so the
 * ordering and the subtotals match `computeOverTimeResults` exactly. Each
 * section is given the FULL hierarchy rows of its type (from `tree`), while its
 * subtotals stay the reducer's leaf sums.
 *
 * Returns the non-transfer sections and the transfer section separately so the
 * caller can interleave the "result before transfers" line between them
 * (parity with `ComparePeriodTable`).
 */
export function groupOverTimeSections(
  analysis: OverTimeAnalysis,
  tree: OverTimeRow[],
): {
  nonTransferSections: OverTimeRenderSection[];
  transferSection: OverTimeRenderSection | null;
} {
  // Full hierarchy rows (parents + leaves) bucketed by type, tree order kept.
  const rowsByType: Record<OverTimeType, OverTimeRow[]> = {
    income: [],
    expense: [],
    transfer: [],
  };
  for (const row of tree) rowsByType[typeOf(row)].push(row);

  const sections: OverTimeRenderSection[] = analysis.sections.map((s) => ({
    type: s.type,
    rows: rowsByType[s.type],
    monthly: s.monthly,
    total: s.total,
  }));

  return {
    nonTransferSections: sections.filter((s) => s.type !== "transfer"),
    transferSection: sections.find((s) => s.type === "transfer") ?? null,
  };
}
