// Standard-accounting picker grouping — Task 3 of
// docs/superpowers/plans/2026-08-02-chart-management.md (spec Part 3,
// binding). Every account picker in the app builds its groups here, from the
// FLAT GET /api/c/accounts list (active-only by default — pickers never show
// inactive accounts), so "Money accounts / Income / Spending / Other" is
// retired app-wide in favor of the standard sections the Chart of Accounts
// screen already uses (CLAUDE.md §9 as amended 2026-08-02: a credit card
// must be findable as a credit card): Bank accounts, Credit cards, Lines of
// credit, Accounts receivable, Cost of goods sold, and so on.
//
// The `types` argument is both a FILTER and an ORDER: a context-restricted
// picker passes just its types (an income-only picker shows only the Income
// sections), and a category picker passes the likeliest type first
// (money-in leads with income, money-out with expenses — the same reordering
// the old four-bucket groups did). Sections within a type follow
// subtypes.ts's display order; accounts within a section keep the API's
// code order.
//
// Untagged accounts (subtype null) group under the bare type heading
// ("Assets"), matching the chart screen's rule. Three subtype labels collide
// with their type heading — equity/"Equity", income/"Income", and
// expense/"Expenses" — and each merges into the generic section instead of
// showing two identical headers, in code order.

import type { PickerGroup } from "../components/SearchablePicker";
import {
  LEDGER_TYPES,
  SUBTYPES_BY_TYPE,
  SUBTYPE_LABELS,
  TYPE_HEADINGS,
  type ApiAccount,
  type LedgerType,
} from "./subtypes";

/** One standard section: header label + the full account rows under it
 * (native-select consumers render these as <optgroup>s and need more than
 * an id/label pair). */
export interface AccountSection {
  label: string;
  accounts: ApiAccount[];
}

/** Category-picker type orders — the standard-groups successor to the old
 * income-first / spending-first four-bucket reorderings (Record.tsx and
 * StatementReview.tsx both offered every category either way; only the
 * likeliest group led). */
export const CATEGORY_TYPES_MONEY_IN: readonly LedgerType[] =
  ["income", "expense", "liability", "equity"];
export const CATEGORY_TYPES_MONEY_OUT: readonly LedgerType[] =
  ["expense", "income", "liability", "equity"];

export function accountSections(
  accounts: readonly ApiAccount[] | null | undefined,
  types: readonly LedgerType[] = LEDGER_TYPES,
): AccountSection[] {
  if (!accounts) return [];
  const out: AccountSection[] = [];
  for (const type of types) {
    const ofType = accounts.filter((a) => a.type === type);
    if (ofType.length === 0) continue;
    const heading = TYPE_HEADINGS[type];
    // Does the bare type heading collide with one of this type's own subtype
    // section labels? (Equity is the one case.) Untagged rows then merge
    // into that section rather than duplicating its header.
    const collides = SUBTYPES_BY_TYPE[type].some((s) => SUBTYPE_LABELS[s] === heading);
    const sections: AccountSection[] = SUBTYPES_BY_TYPE[type].map((s) => ({
      label: SUBTYPE_LABELS[s],
      accounts: ofType.filter(
        (a) => a.subtype === s || (a.subtype === null && collides && SUBTYPE_LABELS[s] === heading),
      ),
    }));
    if (!collides) {
      sections.push({ label: heading, accounts: ofType.filter((a) => a.subtype === null) });
    }
    out.push(...sections.filter((s) => s.accounts.length > 0));
  }
  return out;
}

/** accountSections shaped for SearchablePicker: options are plain account
 * names, never codes (CLAUDE.md §9 "Categories, not account numbers"). */
export function accountPickerGroups(
  accounts: readonly ApiAccount[] | null | undefined,
  types: readonly LedgerType[] = LEDGER_TYPES,
): PickerGroup[] {
  return accountSections(accounts, types).map((s) => ({
    label: s.label,
    options: s.accounts.map((a) => ({ id: a.id, label: a.name })),
  }));
}
