// BalanceEvolutionChart — line / stacked-area chart of net worth over time.
//
// Issue #141 (Bilan #3). Reuses the established Recharts patterns from the
// reports/* charts (see decisions-log #141 — native SVG was reconsidered;
// Recharts is the single chart pattern in this codebase). Two modes:
//   - 'line'    : a single LineChart of `SUM(value)` per snapshot date.
//   - 'stacked' : an AreaChart with one Area per category (stackId='all').
//
// Tooltip shows per-category breakdown in stacked mode and just the total in
// line mode.

import { useMemo } from "react";
import { useTranslation } from "react-i18next";
import {
  LineChart,
  Line,
  AreaChart,
  Area,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  ResponsiveContainer,
  Legend,
  ReferenceLine,
} from "recharts";
import type {
  SnapshotTotalPoint,
  SnapshotCategoryBreakdownPoint,
  SnapshotVehicleBreakdownPoint,
} from "../../services/balance.service";
import type {
  BalanceChartMode,
  BalanceGroupAxis,
} from "../../hooks/useBalanceOverview";
import type { BalanceAccountTransferWithTransaction } from "../../shared/types";

// Stable palette for the stacked-by-category areas. Indexed deterministically
// by category sort order so the colour assignment stays consistent across
// renders and period changes. Reused from the reports CategoryBarChart palette.
const CATEGORY_PALETTE = [
  "#3b82f6", // blue
  "#10b981", // emerald
  "#f59e0b", // amber
  "#8b5cf6", // violet
  "#ef4444", // red
  "#06b6d4", // cyan
  "#ec4899", // pink
  "#84cc16", // lime
  "#f97316", // orange
  "#6366f1", // indigo
];

export interface BalanceEvolutionChartProps {
  mode: BalanceChartMode;
  /**
   * Stacked-mode grouping axis (Issue #204). `'class'` stacks by asset class
   * (the `byCategory` series), `'vehicle'` stacks by fiscal envelope (the
   * `byVehicle` series). Ignored in line mode. Defaults to `'class'`.
   */
  groupAxis?: BalanceGroupAxis;
  totals: SnapshotTotalPoint[];
  byCategory: SnapshotCategoryBreakdownPoint[];
  /** Per-vehicle breakdown for the `groupAxis === 'vehicle'` stacked variant. */
  byVehicle?: SnapshotVehicleBreakdownPoint[];
  /** Map category_key → translated label so the legend reads naturally. */
  categoryLabels?: Record<string, string>;
  /** Map vehicle_key (incl. 'none') → translated label for the vehicle axis. */
  vehicleLabels?: Record<string, string>;
  /**
   * Issue #142 — every linked transfer in the visible range. Rendered as
   * vertical `<ReferenceLine>` markers on the X axis: green for `in`
   * (capital added), red for `out` (capital removed). The label tooltip
   * shows the underlying transaction date + description.
   */
  transferMarkers?: BalanceAccountTransferWithTransaction[];
}

export default function BalanceEvolutionChart({
  mode,
  groupAxis = "class",
  totals,
  byCategory,
  byVehicle = [],
  categoryLabels = {},
  vehicleLabels = {},
  transferMarkers = [],
}: BalanceEvolutionChartProps) {
  const { t, i18n } = useTranslation();

  // The stacked chart is driven by whichever axis is active. Both breakdowns
  // share the `{ snapshot_date, <map> }` shape, so we normalize to a common
  // `{ snapshot_date, series }` form here and feed a single rendering path.
  const stackedSource = useMemo(
    () =>
      groupAxis === "vehicle"
        ? byVehicle.map((p) => ({
            snapshot_date: p.snapshot_date,
            series: p.byVehicle,
          }))
        : byCategory.map((p) => ({
            snapshot_date: p.snapshot_date,
            series: p.byCategory,
          })),
    [groupAxis, byCategory, byVehicle]
  );

  // Label map for the active axis (legend + tooltip). Falls back to the raw
  // key when no translation is provided.
  const activeLabels = groupAxis === "vehicle" ? vehicleLabels : categoryLabels;

  const cadFormatter = useMemo(
    () =>
      new Intl.NumberFormat(i18n.language === "fr" ? "fr-CA" : "en-CA", {
        style: "currency",
        currency: "CAD",
        maximumFractionDigits: 0,
      }),
    [i18n.language]
  );

  const dateLocale = i18n.language === "fr" ? "fr-CA" : "en-CA";
  const formatDate = (iso: string) =>
    new Date(iso).toLocaleDateString(dateLocale, {
      year: "numeric",
      month: "short",
      day: "numeric",
    });

  // --- Line-mode dataset ---
  const lineData = useMemo(
    () =>
      totals.map((p) => ({
        snapshot_date: p.snapshot_date,
        total: p.total,
      })),
    [totals]
  );

  // --- Stacked-area dataset ---
  // We transpose the per-snapshot bucket into one row per snapshot_date with
  // one column per series key (asset class OR fiscal envelope, per groupAxis).
  // Keys absent at a snapshot date are emitted as 0 so Recharts renders a
  // continuous stack.
  const { stackedData, categoryKeys } = useMemo(() => {
    const keys = new Set<string>();
    for (const point of stackedSource) {
      for (const k of Object.keys(point.series)) keys.add(k);
    }
    const orderedKeys = Array.from(keys).sort();
    const data = stackedSource.map((point) => {
      const row: Record<string, string | number> = {
        snapshot_date: point.snapshot_date,
      };
      for (const k of orderedKeys) {
        row[k] = point.series[k] ?? 0;
      }
      return row;
    });
    return { stackedData: data, categoryKeys: orderedKeys };
  }, [stackedSource]);

  const isEmpty =
    mode === "line" ? lineData.length === 0 : stackedData.length === 0;

  // Filter transfer markers to dates that are actually rendered on the X
  // axis (categorical scale ignores unknown ticks). We don't aggregate or
  // dedupe — the user can have several transfers on the same day across
  // accounts; ReferenceLine tolerates duplicates fine.
  const xAxisDates = useMemo(() => {
    const dates = new Set<string>();
    if (mode === "line") {
      for (const p of lineData) dates.add(p.snapshot_date);
    } else {
      for (const p of stackedData) dates.add(p.snapshot_date as string);
    }
    return dates;
  }, [mode, lineData, stackedData]);

  const renderableMarkers = useMemo(
    () =>
      transferMarkers
        .filter((m) => xAxisDates.has(m.transaction_date))
        // Sort so 'in' (green) draws before 'out' (red) for stable z-order.
        .sort((a, b) =>
          a.direction === b.direction ? 0 : a.direction === "in" ? -1 : 1
        ),
    [transferMarkers, xAxisDates]
  );

  if (isEmpty) {
    return (
      <div className="bg-[var(--card)] border border-[var(--border)] rounded-xl p-6">
        <p className="text-center text-[var(--muted-foreground)] italic py-12">
          {t("balance.chart.empty")}
        </p>
      </div>
    );
  }

  const tooltipContentStyle = {
    backgroundColor: "var(--card)",
    border: "1px solid var(--border)",
    borderRadius: "0.5rem",
    color: "var(--foreground)",
  };

  return (
    <div className="bg-[var(--card)] border border-[var(--border)] rounded-xl p-4">
      <ResponsiveContainer width="100%" height={360}>
        {mode === "line" ? (
          <LineChart
            data={lineData}
            margin={{ top: 10, right: 16, bottom: 10, left: 10 }}
          >
            <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
            <XAxis
              dataKey="snapshot_date"
              stroke="var(--muted-foreground)"
              fontSize={11}
              tickFormatter={(s: string) => formatDate(s)}
            />
            <YAxis
              stroke="var(--muted-foreground)"
              fontSize={11}
              tickFormatter={(v: number) => cadFormatter.format(v)}
              width={88}
            />
            <Tooltip
              formatter={(value: number | undefined) =>
                cadFormatter.format(value ?? 0)
              }
              labelFormatter={(label) => formatDate(String(label))}
              contentStyle={tooltipContentStyle}
            />
            <Line
              type="monotone"
              dataKey="total"
              name={t("balance.chart.totalSeriesLabel")}
              stroke="var(--primary)"
              strokeWidth={2}
              dot={{ r: 3 }}
              activeDot={{ r: 5 }}
            />
            {renderableMarkers.map((m) => (
              <ReferenceLine
                key={`tm-${m.id}`}
                x={m.transaction_date}
                stroke={
                  m.direction === "in" ? "var(--positive)" : "var(--negative)"
                }
                strokeDasharray="3 3"
                strokeWidth={1}
                ifOverflow="extendDomain"
                label={{
                  value: t(
                    m.direction === "in"
                      ? "balance.evolution.transferIn"
                      : "balance.evolution.transferOut"
                  ),
                  position: "insideTopRight",
                  fontSize: 9,
                  fill: m.direction === "in" ? "var(--positive)" : "var(--negative)",
                }}
              />
            ))}
          </LineChart>
        ) : (
          <AreaChart
            data={stackedData}
            margin={{ top: 10, right: 16, bottom: 10, left: 10 }}
          >
            <CartesianGrid strokeDasharray="3 3" stroke="var(--border)" />
            <XAxis
              dataKey="snapshot_date"
              stroke="var(--muted-foreground)"
              fontSize={11}
              tickFormatter={(s: string) => formatDate(s)}
            />
            <YAxis
              stroke="var(--muted-foreground)"
              fontSize={11}
              tickFormatter={(v: number) => cadFormatter.format(v)}
              width={88}
            />
            <Tooltip
              formatter={(value: number | undefined, name) => [
                cadFormatter.format(value ?? 0),
                activeLabels[String(name)] ?? String(name),
              ]}
              labelFormatter={(label) => formatDate(String(label))}
              contentStyle={tooltipContentStyle}
            />
            <Legend
              formatter={(value) => activeLabels[String(value)] ?? String(value)}
            />
            {categoryKeys.map((key, idx) => (
              <Area
                key={key}
                type="monotone"
                dataKey={key}
                stackId="all"
                stroke={CATEGORY_PALETTE[idx % CATEGORY_PALETTE.length]}
                fill={CATEGORY_PALETTE[idx % CATEGORY_PALETTE.length]}
                fillOpacity={0.5}
                name={key}
              />
            ))}
            {renderableMarkers.map((m) => (
              <ReferenceLine
                key={`tm-${m.id}`}
                x={m.transaction_date}
                stroke={
                  m.direction === "in" ? "var(--positive)" : "var(--negative)"
                }
                strokeDasharray="3 3"
                strokeWidth={1}
                ifOverflow="extendDomain"
              />
            ))}
          </AreaChart>
        )}
      </ResponsiveContainer>
    </div>
  );
}
