import { Fragment } from "react";
import { useTranslation } from "react-i18next";
import { ChevronDown, ChevronRight, ChevronsDownUp, ChevronsUpDown } from "lucide-react";
import type { CategoryOverTimeData, OverTimeRow } from "../../shared/types";
import { useCollapsibleGroups } from "../../hooks/useCollapsibleGroups";
import { computeOverTimeResults, type OverTimeSeries, type OverTimeType } from "./overTimeResults";
import {
  OVERTIME_COLLAPSE_ACCESSORS,
  OVERTIME_EXPANDED_STORAGE_KEY,
  groupOverTimeSections,
  type OverTimeRenderSection,
} from "./overTimeTableModel";

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

function formatMonth(month: string): string {
  const [year, m] = month.split("-");
  const date = new Date(Number(year), Number(m) - 1);
  return date.toLocaleDateString("default", { month: "short", year: "2-digit" });
}

/** Colours a result figure by sign: surplus green, deficit red, zero neutral. */
function resultColor(value: number): string {
  if (value > 0) return "var(--positive, #10b981)";
  if (value < 0) return "var(--negative, #ef4444)";
  return "";
}

const SECTION_LABEL_KEY: Record<OverTimeType, string> = {
  income: "reports.compare.sections.income",
  expense: "reports.compare.sections.expenses",
  transfer: "reports.compare.sections.transfers",
};

const SECTION_TOTAL_KEY: Record<OverTimeType, string> = {
  income: "reports.compare.totalIncome",
  expense: "reports.compare.totalExpenses",
  transfer: "reports.compare.totalTransfers",
};

interface CategoryOverTimeTableProps {
  data: CategoryOverTimeData;
}

export default function CategoryOverTimeTable({ data }: CategoryOverTimeTableProps) {
  const { t } = useTranslation();

  // Collapse/expand of sub-category groups — collapsed by default (issue #265),
  // persisted under a key distinct from the comparable tables. Called before the
  // early return so the hook order stays stable.
  const groups = useCollapsibleGroups<OverTimeRow>(
    OVERTIME_EXPANDED_STORAGE_KEY,
    OVERTIME_COLLAPSE_ACCESSORS,
  );

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

  // Consumes the id-keyed tree (every category, no top-N/"Other"): subtotals and
  // the result lines are exact, and homonym categories never collide.
  const analysis = computeOverTimeResults(data);
  const { months, hasTransfers, net, beforeTransfers } = analysis;
  const { nonTransferSections, transferSection } = groupOverTimeSections(analysis, data.tree);
  const colSpan = months.length + 2;

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

  // A section = its header, its (visibly) indented hierarchy rows, and a
  // leaf-summed subtotal row. Only top-level parents collapse (parity with the
  // comparable tables); the subtotal is always the reducer's leaf sum, so it
  // never changes when a group is folded away.
  const renderSection = (section: OverTimeRenderSection) => (
    <Fragment key={section.type}>
      {/* Section header */}
      <tr className="bg-[var(--muted)]">
        <td
          colSpan={colSpan}
          className="px-3 py-1.5 font-semibold text-[var(--muted-foreground)] uppercase text-xs tracking-wider sticky left-0 bg-[var(--muted)]"
        >
          {t(SECTION_LABEL_KEY[section.type])}
        </td>
      </tr>
      {/* Category rows — neutral leaf/parent magnitudes (not deltas) */}
      {groups.visible(section.rows).map((row) => {
        const isParent = row.is_parent;
        const depth = row.depth;
        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.categoryId ?? "uncat"}-${isParent}-${depth}-${row.categoryName}`}
            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"
                  : "hover:bg-[var(--muted)]/40"
            }`}
          >
            <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.categoryColor }}
                  />
                  {row.categoryName}
                </button>
              ) : (
                <span className="flex items-center gap-2">
                  <span
                    className="w-2.5 h-2.5 rounded-full shrink-0"
                    style={{ backgroundColor: row.categoryColor }}
                  />
                  {row.categoryName}
                </span>
              )}
            </td>
            {months.map((month, monthIdx) => {
              const value = row.monthly[monthIdx] ?? 0;
              return (
                <td key={month} className="text-right px-3 py-1.5 tabular-nums">
                  {value ? cadFormatter(value) : "—"}
                </td>
              );
            })}
            <td className="text-right px-3 py-1.5 font-semibold border-l border-[var(--border)]/50 tabular-nums">
              {cadFormatter(row.total)}
            </td>
          </tr>
        );
      })}
      {/* Section subtotal */}
      <tr className="border-b border-[var(--border)] bg-[color-mix(in_srgb,var(--muted)_40%,var(--card))] font-semibold">
        <td className="px-3 py-2.5 sticky left-0 bg-[color-mix(in_srgb,var(--muted)_40%,var(--card))] z-10">
          {t(SECTION_TOTAL_KEY[section.type])}
        </td>
        {months.map((month) => (
          <td key={month} className="text-right px-3 py-2.5 tabular-nums">
            {cadFormatter(section.monthly[month])}
          </td>
        ))}
        <td className="text-right px-3 py-2.5 border-l border-[var(--border)]/50 tabular-nums">
          {cadFormatter(section.total)}
        </td>
      </tr>
    </Fragment>
  );

  // A result line (before-transfers subtotal or the net bottom line). Amounts are
  // coloured by sign (surplus green / deficit red). Computed from the raw tree,
  // so folding groups never moves these figures.
  const renderResultRow = (labelKey: string, series: OverTimeSeries, strong: boolean) => (
    <tr
      className={`font-bold bg-[color-mix(in_srgb,var(--muted)_20%,var(--card))] ${
        strong ? "border-t-2 border-[var(--border)]" : "border-b border-[var(--border)]"
      }`}
    >
      <td className="px-3 py-3 sticky left-0 bg-[color-mix(in_srgb,var(--muted)_20%,var(--card))] z-10">
        {t(labelKey)}
      </td>
      {months.map((month) => (
        <td
          key={month}
          className="text-right px-3 py-3 tabular-nums"
          style={{ color: resultColor(series.monthly[month]) }}
        >
          {cadFormatter(series.monthly[month])}
        </td>
      ))}
      <td
        className="text-right px-3 py-3 border-l border-[var(--border)]/50 tabular-nums"
        style={{ color: resultColor(series.total) }}
      >
        {cadFormatter(series.total)}
      </td>
    </tr>
  );

  return (
    <div className="bg-[var(--card)] border border-[var(--border)] rounded-xl overflow-hidden">
      {hasGroups && (
        <div className="flex justify-end items-center gap-1 px-3 py-2 border-b border-[var(--border)]">
          <button
            type="button"
            onClick={() => (allExpanded ? groups.collapseAll(data.tree) : groups.expandAll(data.tree))}
            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>
        </div>
      )}
      <div className="overflow-x-auto overflow-y-auto" style={{ maxHeight: "calc(100vh - 220px)" }}>
        <table className="w-full text-sm whitespace-nowrap">
          <thead className="sticky top-0 z-20">
            <tr className="border-b border-[var(--border)] bg-[var(--card)]">
              <th className="text-left px-3 py-2 font-medium text-[var(--muted-foreground)] bg-[var(--card)] sticky left-0 z-30 min-w-[140px]">
                {t("budget.category")}
              </th>
              {months.map((month) => (
                <th key={month} className="text-right px-3 py-2 font-medium text-[var(--muted-foreground)] bg-[var(--card)] min-w-[90px]">
                  {formatMonth(month)}
                </th>
              ))}
              <th className="text-right px-3 py-2 font-medium text-[var(--muted-foreground)] bg-[var(--card)] border-l border-[var(--border)] min-w-[90px]">
                {t("common.total")}
              </th>
            </tr>
          </thead>
          <tbody>
            {nonTransferSections.map(renderSection)}
            {/* Operating result (revenues − expenses), interleaved before the
                transfers section only when transfers exist — otherwise it equals
                the net total below and would just be noise. */}
            {hasTransfers && renderResultRow("reports.compare.resultBeforeTransfers", beforeTransfers, false)}
            {transferSection && renderSection(transferSection)}
            {/* Bottom line: result after netting transfers. */}
            {renderResultRow("reports.compare.resultNet", net, true)}
          </tbody>
        </table>
      </div>
    </div>
  );
}
