import { Fragment, useState } from "react";
import { useTranslation } from "react-i18next";
import { ArrowUpDown, ChevronDown, ChevronRight, ChevronsDownUp, ChevronsUpDown } from "lucide-react";
import type { BudgetVsActualRow } from "../../shared/types";
import { reorderRows } from "../../utils/reorderRows";
import type { CollapseAccessors } from "../../utils/collapsibleRows";
import { useCollapsibleGroups } from "../../hooks/useCollapsibleGroups";
import { type SectionType, type Totals, sumLeaves, pct, computeResults } from "./budgetVsActualResults";

const cadFormatter = (value: number) =>
  new Intl.NumberFormat("en-CA", {
    style: "currency",
    currency: "CAD",
    maximumFractionDigits: 0,
  }).format(value);

const pctFormatter = (value: number | null) =>
  value == null ? "—" : `${(value * 100).toFixed(1)}%`;

function variationColor(value: number): string {
  if (value > 0) return "text-[var(--positive)]";
  if (value < 0) return "text-[var(--negative)]";
  return "";
}

interface BudgetVsActualTableProps {
  data: BudgetVsActualRow[];
}

const STORAGE_KEY = "subtotals-position";

// Collapse groups keyed by category id; a row is hidden when any ancestor
// (walking parent_id) is collapsed, so every parent level is collapsible.
const BVA_COLLAPSE_ACCESSORS: CollapseAccessors<BudgetVsActualRow> = {
  keyOf: (row) => `p:${row.category_id}`,
  parentKeyOf: (row) => (row.parent_id != null ? `p:${row.parent_id}` : null),
  depthOf: (row) => row.depth ?? (row.parent_id !== null && !row.is_parent ? 1 : 0),
  isParent: (row) => row.is_parent,
};

const BVA_EXPANDED_STORAGE_KEY = "reports-bva-expanded";

export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps) {
  const { t } = useTranslation();
  const [subtotalsOnTop, setSubtotalsOnTop] = useState(() => {
    const stored = localStorage.getItem(STORAGE_KEY);
    return stored === null ? true : stored === "top";
  });

  const toggleSubtotals = () => {
    setSubtotalsOnTop((prev) => {
      const next = !prev;
      localStorage.setItem(STORAGE_KEY, next ? "top" : "bottom");
      return next;
    });
  };

  // Collapse/expand of sub-category groups — collapsed by default (issue #254).
  const groups = useCollapsibleGroups<BudgetVsActualRow>(
    BVA_EXPANDED_STORAGE_KEY,
    BVA_COLLAPSE_ACCESSORS,
  );

  if (data.length === 0) {
    return (
      <div className="bg-[var(--card)] border border-[var(--border)] rounded-xl p-8 text-center text-[var(--muted-foreground)]">
        {t("reports.bva.noData")}
      </div>
    );
  }

  // Group rows into contiguous type sections (the service already type-sorts:
  // income → expense → transfer, Issue #277).
  const sections: { type: SectionType; label: string; rows: BudgetVsActualRow[] }[] = [];
  const typeLabels: Record<SectionType, string> = {
    expense: t("budget.expenses"),
    income: t("budget.income"),
    transfer: t("budget.transfers"),
  };
  const typeTotalKeys: Record<SectionType, string> = {
    expense: "budget.totalExpenses",
    income: "budget.totalIncome",
    transfer: "budget.totalTransfers",
  };

  let currentType: SectionType | null = null;
  for (const row of data) {
    if (row.category_type !== currentType) {
      currentType = row.category_type;
      sections.push({ type: currentType, label: typeLabels[currentType], rows: [] });
    }
    sections[sections.length - 1].rows.push(row);
  }

  // Income-statement result lines (Issue #277): income + expense (expense
  // already arrives signed negative in BudgetVsActualRow — see
  // budgetVsActualResults.ts), then the net after adding transfers. Replaces
  // the old flat grand total row.
  const results = computeResults(data);
  const nonTransferSections = sections.filter((s) => s.type !== "transfer");
  const transferSection = sections.find((s) => s.type === "transfer");

  const renderSection = (section: { type: SectionType; label: string; rows: BudgetVsActualRow[] }) => {
    const sectionTotals = sumLeaves(section.rows);
    const sectionMonthPct = pct(sectionTotals.monthVariation, sectionTotals.monthBudget);
    const sectionYtdPct = pct(sectionTotals.ytdVariation, sectionTotals.ytdBudget);
    return (
      <Fragment key={section.type}>
        <tr className="bg-[var(--muted)]">
          <td colSpan={9} className="px-3 py-1.5 font-semibold text-[var(--muted-foreground)] uppercase text-xs tracking-wider sticky left-0 bg-[var(--muted)]">
            {section.label}
          </td>
        </tr>
        {reorderRows(groups.visible(section.rows), subtotalsOnTop).map((row) => {
          const isParent = row.is_parent;
          const depth = row.depth ?? (row.parent_id !== null && !row.is_parent ? 1 : 0);
          const isTopParent = isParent && depth === 0;
          const isIntermediateParent = isParent && depth >= 1;
          const collapsed = isParent && groups.isCollapsed(row);
          const paddingClass = depth >= 3 ? "pl-20" : depth === 2 ? "pl-14" : depth === 1 ? "pl-8" : "px-3";
          return (
            <tr
              key={`${row.category_id}-${row.is_parent}-${depth}`}
              aria-level={isParent ? depth + 1 : undefined}
              className={`border-b border-[var(--border)]/50 ${
                isTopParent ? "bg-[color-mix(in_srgb,var(--muted)_30%,var(--card))] font-semibold" :
                isIntermediateParent ? "bg-[color-mix(in_srgb,var(--muted)_15%,var(--card))] font-medium" : ""
              }`}
            >
              <td className={`py-1.5 sticky left-0 z-10 ${
                isTopParent
                  ? "px-3 bg-[color-mix(in_srgb,var(--muted)_30%,var(--card))]"
                  : isIntermediateParent
                    ? `${paddingClass} bg-[color-mix(in_srgb,var(--muted)_15%,var(--card))]`
                    : `${paddingClass} bg-[var(--card)]`
              }`}>
                {isParent ? (
                  <button
                    type="button"
                    onClick={() => groups.toggle(row)}
                    aria-expanded={!collapsed}
                    className="flex items-center gap-2 w-full text-left hover:opacity-80 transition-opacity"
                  >
                    {collapsed ? (
                      <ChevronRight size={14} className="shrink-0 text-[var(--muted-foreground)]" />
                    ) : (
                      <ChevronDown size={14} className="shrink-0 text-[var(--muted-foreground)]" />
                    )}
                    <span
                      className="w-2.5 h-2.5 rounded-full shrink-0"
                      style={{ backgroundColor: row.category_color }}
                    />
                    {row.category_name}
                  </button>
                ) : (
                  <span className="flex items-center gap-2">
                    <span
                      className="w-2.5 h-2.5 rounded-full shrink-0"
                      style={{ backgroundColor: row.category_color }}
                    />
                    {row.category_name}
                  </span>
                )}
              </td>
              <td className={`text-right px-3 py-1.5 border-l border-[var(--border)]/50`}>
                {cadFormatter(row.monthActual)}
              </td>
              <td className="text-right px-3 py-1.5">{cadFormatter(row.monthBudget)}</td>
              <td className={`text-right px-3 py-1.5 ${variationColor(row.monthVariation)}`}>
                {cadFormatter(row.monthVariation)}
              </td>
              <td className={`text-right px-3 py-1.5 ${variationColor(row.monthVariation)}`}>
                {pctFormatter(row.monthVariationPct)}
              </td>
              <td className={`text-right px-3 py-1.5 border-l border-[var(--border)]/50`}>
                {cadFormatter(row.ytdActual)}
              </td>
              <td className="text-right px-3 py-1.5">{cadFormatter(row.ytdBudget)}</td>
              <td className={`text-right px-3 py-1.5 ${variationColor(row.ytdVariation)}`}>
                {cadFormatter(row.ytdVariation)}
              </td>
              <td className={`text-right px-3 py-1.5 ${variationColor(row.ytdVariation)}`}>
                {pctFormatter(row.ytdVariationPct)}
              </td>
            </tr>
          );
        })}
        <tr className="border-b border-[var(--border)] bg-[color-mix(in_srgb,var(--muted)_40%,var(--card))] font-semibold text-sm">
          <td className="px-3 py-2.5 sticky left-0 bg-[color-mix(in_srgb,var(--muted)_40%,var(--card))] z-10">{t(typeTotalKeys[section.type])}</td>
          <td className="text-right px-3 py-2.5 border-l border-[var(--border)]/50">
            {cadFormatter(sectionTotals.monthActual)}
          </td>
          <td className="text-right px-3 py-2.5">{cadFormatter(sectionTotals.monthBudget)}</td>
          <td className={`text-right px-3 py-2.5 ${variationColor(sectionTotals.monthVariation)}`}>
            {cadFormatter(sectionTotals.monthVariation)}
          </td>
          <td className={`text-right px-3 py-2.5 ${variationColor(sectionTotals.monthVariation)}`}>
            {pctFormatter(sectionMonthPct)}
          </td>
          <td className="text-right px-3 py-2.5 border-l border-[var(--border)]/50">
            {cadFormatter(sectionTotals.ytdActual)}
          </td>
          <td className="text-right px-3 py-2.5">{cadFormatter(sectionTotals.ytdBudget)}</td>
          <td className={`text-right px-3 py-2.5 ${variationColor(sectionTotals.ytdVariation)}`}>
            {cadFormatter(sectionTotals.ytdVariation)}
          </td>
          <td className={`text-right px-3 py-2.5 ${variationColor(sectionTotals.ytdVariation)}`}>
            {pctFormatter(sectionYtdPct)}
          </td>
        </tr>
      </Fragment>
    );
  };

  // A result line (before-transfers subtotal or the net total). Every cell is
  // coloured by its own sign — a surplus is green, a deficit red — mirroring
  // ComparePeriodTable.renderResultRow (unlike normal rows, which only colour
  // the variation/pct columns).
  const renderResultRow = (labelKey: string, tot: Totals, strong: boolean) => {
    const rowBg = strong
      ? "bg-[color-mix(in_srgb,var(--muted)_20%,var(--card))]"
      : "bg-[color-mix(in_srgb,var(--muted)_10%,var(--card))]";
    const rowClass = strong
      ? `border-t-2 border-[var(--border)] font-bold text-sm ${rowBg}`
      : `border-b border-[var(--border)] font-semibold text-sm ${rowBg}`;
    const cell = "text-right px-3 py-3";
    const monthPct = pct(tot.monthVariation, tot.monthBudget);
    const ytdPct = pct(tot.ytdVariation, tot.ytdBudget);
    return (
      <tr key={labelKey} className={rowClass}>
        <td className={`px-3 py-3 sticky left-0 z-10 ${rowBg}`}>{t(labelKey)}</td>
        <td className={`${cell} border-l border-[var(--border)]/50 ${variationColor(tot.monthActual)}`}>
          {cadFormatter(tot.monthActual)}
        </td>
        <td className={`${cell} ${variationColor(tot.monthBudget)}`}>{cadFormatter(tot.monthBudget)}</td>
        <td className={`${cell} ${variationColor(tot.monthVariation)}`}>
          {cadFormatter(tot.monthVariation)}
        </td>
        <td className={`${cell} ${variationColor(tot.monthVariation)}`}>{pctFormatter(monthPct)}</td>
        <td className={`${cell} border-l border-[var(--border)]/50 ${variationColor(tot.ytdActual)}`}>
          {cadFormatter(tot.ytdActual)}
        </td>
        <td className={`${cell} ${variationColor(tot.ytdBudget)}`}>{cadFormatter(tot.ytdBudget)}</td>
        <td className={`${cell} ${variationColor(tot.ytdVariation)}`}>{cadFormatter(tot.ytdVariation)}</td>
        <td className={`${cell} ${variationColor(tot.ytdVariation)}`}>{pctFormatter(ytdPct)}</td>
      </tr>
    );
  };

  const hasGroups = groups.groupCount(data) > 0;
  const allExpanded = groups.allExpanded(data);

  return (
    <div className="bg-[var(--card)] border border-[var(--border)] rounded-xl overflow-hidden">
      <div className="flex justify-end items-center gap-1 px-3 py-2 border-b border-[var(--border)]">
        {hasGroups && (
          <button
            type="button"
            onClick={() => (allExpanded ? groups.collapseAll(data) : groups.expandAll(data))}
            className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-medium text-[var(--muted-foreground)] hover:bg-[var(--muted)] transition-colors"
          >
            {allExpanded ? <ChevronsDownUp size={13} /> : <ChevronsUpDown size={13} />}
            {allExpanded ? t("reports.collapse.collapseAll") : t("reports.collapse.expandAll")}
          </button>
        )}
        <button
          onClick={toggleSubtotals}
          className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-xs font-medium text-[var(--muted-foreground)] hover:bg-[var(--muted)] transition-colors"
        >
          <ArrowUpDown size={13} />
          {subtotalsOnTop ? t("reports.subtotalsOnTop") : t("reports.subtotalsOnBottom")}
        </button>
      </div>
      <div className="overflow-x-auto overflow-y-auto" style={{ maxHeight: "calc(100vh - 220px)" }}>
      <table className="w-full text-sm">
        <thead className="sticky top-0 z-20">
          <tr className="border-b border-[var(--border)] bg-[var(--card)]">
            <th rowSpan={2} className="text-left px-3 py-2 font-medium text-[var(--muted-foreground)] align-bottom sticky left-0 bg-[var(--card)] z-30 min-w-[180px]">
              {t("budget.category")}
            </th>
            <th colSpan={4} className="text-center px-3 py-1 font-medium text-[var(--muted-foreground)] border-l border-[var(--border)] bg-[var(--card)]">
              {t("reports.bva.monthly")}
            </th>
            <th colSpan={4} className="text-center px-3 py-1 font-medium text-[var(--muted-foreground)] border-l border-[var(--border)] bg-[var(--card)]">
              {t("reports.bva.ytd")}
            </th>
          </tr>
          <tr className="border-b border-[var(--border)] bg-[var(--card)]">
            <th className="text-right px-3 py-1 font-medium text-[var(--muted-foreground)] border-l border-[var(--border)] bg-[var(--card)]">
              {t("budget.actual")}
            </th>
            <th className="text-right px-3 py-1 font-medium text-[var(--muted-foreground)] bg-[var(--card)]">
              {t("budget.planned")}
            </th>
            <th className="text-right px-3 py-1 font-medium text-[var(--muted-foreground)] bg-[var(--card)]">
              {t("reports.bva.dollarVar")}
            </th>
            <th className="text-right px-3 py-1 font-medium text-[var(--muted-foreground)] bg-[var(--card)]">
              {t("reports.bva.pctVar")}
            </th>
            <th className="text-right px-3 py-1 font-medium text-[var(--muted-foreground)] border-l border-[var(--border)] bg-[var(--card)]">
              {t("budget.actual")}
            </th>
            <th className="text-right px-3 py-1 font-medium text-[var(--muted-foreground)] bg-[var(--card)]">
              {t("budget.planned")}
            </th>
            <th className="text-right px-3 py-1 font-medium text-[var(--muted-foreground)] bg-[var(--card)]">
              {t("reports.bva.dollarVar")}
            </th>
            <th className="text-right px-3 py-1 font-medium text-[var(--muted-foreground)] bg-[var(--card)]">
              {t("reports.bva.pctVar")}
            </th>
          </tr>
        </thead>
        <tbody>
          {nonTransferSections.map(renderSection)}
          {/* Operating result (income + expense), shown before the transfers
              section only when transfers exist — otherwise it equals the net
              total below and would just be noise (mirrors ComparePeriodTable). */}
          {results.hasTransfers &&
            renderResultRow("reports.compare.resultBeforeTransfers", results.resultBefore, false)}
          {transferSection && renderSection(transferSection)}
          {/* Bottom line: result after adding transfers. */}
          {renderResultRow("reports.compare.resultNet", results.resultNet, true)}
        </tbody>
      </table>
      </div>
    </div>
  );
}
