// BalancePage — overview of net worth at `/balance`.
//
// Issue #141 (Bilan #3). Composes:
//   - BalanceOverviewCard (latest total + Δ% + staleness warning + new-snapshot CTA)
//   - Period selector (3M / 6M / 1A / 3A / Tout)
//   - Chart-mode toggle (Line / Stacked-by-category)
//   - BalanceEvolutionChart
//   - BalanceAccountsTable (one row per active account with latest value + Δ%)
//
// All data flows through `useBalanceOverview` (scoped useReducer). Returns
// (Modified Dietz) are deferred to Issue #142 — the accounts table reserves
// columns with a TODO comment.

import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { Wallet } from "lucide-react";
import {
  useBalanceOverview,
  type BalancePeriod,
  type BalanceChartMode,
} from "../hooks/useBalanceOverview";
import {
  archiveBalanceAccount,
  listAccountTransfers,
  type AccountLatestSnapshot,
} from "../services/balance.service";
import { getAllCategories } from "../services/transactionService";
import type { Category, BalanceAccountTransferWithTransaction } from "../shared/types";
import BalanceOverviewCard from "../components/balance/BalanceOverviewCard";
import BalanceOnboardingCard from "../components/balance/BalanceOnboardingCard";
import BalanceEvolutionChart from "../components/balance/BalanceEvolutionChart";
import BalanceAccountsTable from "../components/balance/BalanceAccountsTable";
import LinkTransfersModal from "../components/balance/LinkTransfersModal";
import StarterAccountsModal from "../components/balance/StarterAccountsModal";
import { getPreference, setPreference } from "../services/userPreferenceService";

const STARTER_PREF_KEY = "balance_starter_proposed";

const PERIOD_OPTIONS: BalancePeriod[] = ["3M", "6M", "1A", "3A", "all"];

export default function BalancePage() {
  const { t } = useTranslation();
  const { state, setPeriod, setChartMode, reload } = useBalanceOverview();

  // Issue #142 — link-transfers modal state. Categories list is loaded once
  // on mount (used by the modal's filter dropdown).
  const [linkTarget, setLinkTarget] = useState<AccountLatestSnapshot | null>(
    null
  );
  const [categories, setCategories] = useState<Category[]>([]);
  const [transfersByAccount, setTransfersByAccount] = useState<
    Map<number, BalanceAccountTransferWithTransaction[]>
  >(new Map());

  useEffect(() => {
    void getAllCategories().then(setCategories).catch(() => setCategories([]));
  }, []);

  // Issue #179 — one-shot starter-accounts modal for existing profiles. The
  // pref `balance_starter_proposed` is written once (confirmed or dismissed),
  // so the modal never re-appears. New profiles get both the 4 starters AND
  // the pref pre-seeded via consolidated_schema.sql, so they never hit this
  // branch at all (S1 fix from #187).
  const [showStarterModal, setShowStarterModal] = useState(false);
  useEffect(() => {
    let cancelled = false;
    void (async () => {
      try {
        const existing = await getPreference(STARTER_PREF_KEY);
        if (!cancelled && existing == null) {
          setShowStarterModal(true);
        }
      } catch {
        // Pref read failure: leave modal hidden — privacy-first default.
      }
    })();
    return () => {
      cancelled = true;
    };
  }, []);

  const handleStarterModalClose = async (acceptedIds: number[]) => {
    setShowStarterModal(false);
    try {
      await setPreference(
        STARTER_PREF_KEY,
        JSON.stringify({
          shown_at: new Date().toISOString(),
          accepted: acceptedIds,
        })
      );
    } catch {
      // Best-effort: a write failure here would cause the modal to re-show
      // on next visit, which is acceptable (data still consistent).
    }
    if (acceptedIds.length > 0) {
      await reload();
    }
  };

  // Refresh per-account transfer lists used by the chart markers. Keyed by
  // account_id → [transfers]. Used by `BalanceEvolutionChart` to plot
  // ReferenceLine markers (green for in, red for out).
  useEffect(() => {
    let cancelled = false;
    async function run() {
      const map = new Map<number, BalanceAccountTransferWithTransaction[]>();
      await Promise.all(
        state.accountsLatest.map(async (acc) => {
          try {
            const list = await listAccountTransfers(acc.account_id);
            map.set(acc.account_id, list);
          } catch {
            map.set(acc.account_id, []);
          }
        })
      );
      if (!cancelled) setTransfersByAccount(map);
    }
    void run();
    return () => {
      cancelled = true;
    };
  }, [state.accountsLatest]);

  const allTransferMarkers = useMemo(() => {
    const flat: BalanceAccountTransferWithTransaction[] = [];
    for (const list of transfersByAccount.values()) flat.push(...list);
    return flat;
  }, [transfersByAccount]);

  // Earliest snapshot date in the dataset, used to anchor the "depuis
  // création" Modified Dietz horizon in the accounts table.
  const earliestSnapshotDate = useMemo(() => {
    if (state.evolutionTotals.length === 0) return null;
    return state.evolutionTotals[0].snapshot_date;
  }, [state.evolutionTotals]);

  // Build a category_key → translated label map from the accounts payload —
  // the byCategory series is keyed by `key`, not by id, and the same
  // taxonomy is already loaded with `accountsLatest` joins.
  const categoryLabels = useMemo(() => {
    const m: Record<string, string> = {};
    for (const a of state.accountsLatest) {
      if (!m[a.category_key]) {
        m[a.category_key] = t(a.category_i18n_key, {
          defaultValue: a.category_key,
        });
      }
    }
    return m;
  }, [state.accountsLatest, t]);

  const handleArchiveAccount = async (accountId: number) => {
    try {
      await archiveBalanceAccount(accountId);
      await reload();
    } catch {
      // Reload swallows; the row simply stays. UX feedback can be added later.
    }
  };

  return (
    <div className={state.isLoading ? "opacity-60 pointer-events-none" : ""}>
      <div className="flex items-center gap-3 mb-6">
        <Wallet size={24} className="text-[var(--primary)]" />
        <h1 className="text-2xl font-bold">{t("balance.overview.title")}</h1>
      </div>

      {state.error && (
        <div className="mb-4 p-3 rounded-lg bg-[var(--negative)]/10 text-[var(--negative)] text-sm border border-[var(--negative)]/20">
          {state.error}
        </div>
      )}

      {/* Issue #178 — empty-state guard. We probe accountsLatest for ANY
          snapshot date so the guard is independent of the active period
          filter (state.period). When empty, we render only the onboarding
          card — period selector, chart and accounts table would all show
          empty states stacked under it (S2 from #187). */}
      {(() => {
        const accountsCount = state.accountsLatest.length;
        const hasAnySnapshot = state.accountsLatest.some(
          (a) => a.latest_snapshot_date != null
        );
        const isEmpty = accountsCount === 0 || !hasAnySnapshot;

        if (isEmpty) {
          return (
            <div className="space-y-6">
              <BalanceOnboardingCard
                accountsCount={accountsCount}
                snapshotsCount={hasAnySnapshot ? 1 : 0}
              />
            </div>
          );
        }

        return (
          <div className="space-y-6">
            <BalanceOverviewCard totals={state.evolutionTotals} />

            <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
              {/* Period selector */}
              <div
                role="group"
                aria-label={t("balance.period.legend")}
                className="inline-flex rounded-lg border border-[var(--border)] overflow-hidden"
              >
                {PERIOD_OPTIONS.map((p) => (
                  <button
                    key={p}
                    type="button"
                    onClick={() => setPeriod(p)}
                    className={`px-3 py-1.5 text-sm font-medium ${
                      state.period === p
                        ? "bg-[var(--primary)] text-white"
                        : "bg-[var(--card)] text-[var(--foreground)] hover:bg-[var(--muted)]/40"
                    }`}
                    aria-pressed={state.period === p}
                  >
                    {t(`balance.period.${p}`)}
                  </button>
                ))}
              </div>

              {/* Chart mode toggle */}
              <div
                role="group"
                aria-label={t("balance.chart.modeLegend")}
                className="inline-flex rounded-lg border border-[var(--border)] overflow-hidden"
              >
                {(["line", "stacked"] as BalanceChartMode[]).map((mode) => (
                  <button
                    key={mode}
                    type="button"
                    onClick={() => setChartMode(mode)}
                    className={`px-3 py-1.5 text-sm font-medium ${
                      state.chartMode === mode
                        ? "bg-[var(--primary)] text-white"
                        : "bg-[var(--card)] text-[var(--foreground)] hover:bg-[var(--muted)]/40"
                    }`}
                    aria-pressed={state.chartMode === mode}
                  >
                    {t(`balance.chart.mode.${mode}`)}
                  </button>
                ))}
              </div>
            </div>

            <BalanceEvolutionChart
              mode={state.chartMode}
              totals={state.evolutionTotals}
              byCategory={state.evolutionByCategory}
              categoryLabels={categoryLabels}
              transferMarkers={allTransferMarkers}
            />

            <div>
              <h2 className="text-lg font-semibold mb-3">
                {t("balance.overview.accountsTitle")}
              </h2>
              <BalanceAccountsTable
                accounts={state.accountsLatest}
                periodAnchor={state.accountsPeriodAnchor}
                sinceCreationDate={earliestSnapshotDate}
                onArchiveAccount={(acc) => handleArchiveAccount(acc.account_id)}
                onLinkTransfers={(acc) => setLinkTarget(acc)}
              />
            </div>
          </div>
        );
      })()}

      <StarterAccountsModal
        isOpen={showStarterModal}
        onClose={(ids) => {
          void handleStarterModalClose(ids);
        }}
      />

      {linkTarget && (
        <LinkTransfersModal
          accountId={linkTarget.account_id}
          accountName={linkTarget.account_name}
          categories={categories}
          onClose={() => setLinkTarget(null)}
          onLinked={() => {
            void reload();
          }}
        />
      )}
    </div>
  );
}
