"use client";

import { useEffect, useMemo, useState } from "react";
import { Area, CartesianGrid, ComposedChart, Legend, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
import { Dices, X } from "lucide-react";
import { NumberField, SelectField, MoneyField, RequiredNumberField } from "@/components/FormField";
import { InfoBubble } from "@/components/InfoBubble";
import { api } from "@/lib/api-client";
import { formatChf } from "@/lib/format";
import {
  buildElementGroups,
  defaultVolatilityLevel,
  floorFor,
  paramsForScenario,
  runMonteCarloMulti,
  INFLATION_VOLATILITY_LEVELS,
  RETURN_BEARING,
  RETURN_VOLATILITY_LEVELS,
  type ElementGroup,
  type ElementMcParams,
  type InflationVolatilityLevel,
  type ReturnVolatilityLevel,
  type ScenarioMcResult,
  type ScenarioRunInput,
} from "@/lib/montecarlo";
import { CATEGORY_LABELS } from "@/lib/elements";
import type { PlanInput, ScenarioMeta } from "@/lib/types";
import type { PlanComputed } from "@/lib/calculations";

// Farben der Szenario-Serien -- wie im Vermögensverlauf, damit die Zuordnung vertraut bleibt.
const PALETTE = ["#4f46e5", "#0ea5e9", "#16a34a", "#d97706", "#dc2626", "#7c3äd"];

const RETURN_LEVEL_OPTIONS: { value: ReturnVolatilityLevel; label: string }[] = [
  { value: "sehr_niedrig", label: "Sehr niedrig" },
  { value: "niedrig", label: "Niedrig" },
  { value: "moderat", label: "Moderat" },
  { value: "hoch", label: "Hoch" },
  { value: "sehr_hoch", label: "Sehr hoch" },
  { value: "manuell", label: "Manuell" },
];
const INFLATION_LEVEL_OPTIONS: { value: InflationVolatilityLevel; label: string }[] = [
  { value: "sehr_niedrig", label: "Sehr niedrig" },
  { value: "niedrig", label: "Niedrig" },
  { value: "manuell", label: "Manuell" },
];

const RETURN_HELP =
  "Wie stark die Jahresrendite schwankt. Sehr niedrig ≈ Staatsanleihen/Geldmarkt · Niedrig ≈ Immobilien, defensive Mischportfolios · Moderat ≈ breit diversifizierte Aktien-ETFs/Fonds · Hoch ≈ Einzelaktien, Branchen-/Schwellenländerfonds · Sehr hoch ≈ Kryptowährungen, hochspekulative Anlagen.";
const INFLATION_HELP =
  "Wie stark die Inflation schwankt. Für die Schweiz ist sie historisch sehr stabil (Sehr niedrig ≈ 1 %). Höhere Stufen wären Hyperinflations-Annahmen.";

interface ElementDraft {
  mean: string;
  level: ReturnVolatilityLevel;
  manualSigma: string;
}

interface LoadedScenario {
  id: string;
  name: string;
  plan: PlanInput;
  computed: PlanComputed;
}

function returnSigma(el: ElementDraft): number {
  return el.level === "manuell" ? Number(el.manualSigma) || 0 : RETURN_VOLATILITY_LEVELS[el.level];
}
function inflationSigma(level: InflationVolatilityLevel, manual: string): number {
  return level === "manuell" ? Number(manual) || 0 : INFLATION_VOLATILITY_LEVELS[level];
}

// Deterministische Planungslinie eines Szenarios (Alterspunkte = Phasengrenzen).
function detPointsOf(plan: PlanInput, computed: PlanComputed): { age: number; det: number }[] {
  const startAge = plan.persons.find((p) => p.role === "PERSON_A")?.age ?? plan.persons[0]?.age ?? 0;
  if (computed.phases.length === 0) return [];
  const pts = [{ age: startAge, det: computed.phases[0].startWealthNominal }];
  let acc = 0;
  for (const ph of computed.phases) {
    acc += ph.durationYears;
    pts.push({ age: startAge + acc, det: ph.endWealthNominal });
  }
  return pts;
}

export function MonteCarloDialog({
  plan,
  computed,
  meta,
  scenarios,
  onClose,
}: {
  plan: PlanInput;
  computed: PlanComputed;
  meta: ScenarioMeta;
  // Alle Szenarien dieses Plans (auch nicht ausgewählte) -- sie werden geladen, damit sich
  // die Herkunfts-Kette der Elemente auch über übersprungene Zwischen-Szenarien auflöst.
  scenarios: ScenarioMeta[];
  onClose: () => void;
}) {
  const [loaded, setLoaded] = useState<Record<string, LoadedScenario>>(() => ({
    [meta.id]: { id: meta.id, name: meta.name, plan, computed },
  }));
  const [loading, setLoading] = useState(scenarios.some((s) => s.id !== meta.id));
  const [loadError, setLoadError] = useState<string | null>(null);
  const [selectedIds, setSelectedIds] = useState<string[]>([meta.id]);
  const [targets, setTargets] = useState<Record<string, number>>({
    [meta.id]: Math.max(0, computed.nachlass),
  });

  const [runs, setRuns] = useState(1000);
  const [inflMean, setInflMean] = useState("");
  const [inflLevel, setInflLevel] = useState<InflationVolatilityLevel>("sehr_niedrig");
  const [inflManual, setInflManual] = useState("1");
  const [drafts, setDrafts] = useState<Record<string, ElementDraft>>({});

  const [running, setRunning] = useState(false);
  const [progress, setProgress] = useState({ index: 0, count: 1, fraction: 0 });
  const [results, setResults] = useState<ScenarioMcResult[] | null>(null);

  const scenarioKey = scenarios.map((s) => s.id).join(",");

  // Die übrigen Szenarien einmalig nachladen. Ein Plan hat realistisch eine Handvoll
  // Szenarien -- gegenüber tausenden Simulationsläufen fällt das nicht ins Gewicht.
  useEffect(() => {
    const missing = scenarios.filter((s) => s.id !== meta.id);
    if (missing.length === 0) return;
    let cancelled = false;
    (async () => {
      try {
        const entries = await Promise.all(
          missing.map(async (s) => {
            const data = await api.get<{ plan: PlanInput; computed: PlanComputed }>(`/api/scenarios/${s.id}`);
            return [s.id, { id: s.id, name: s.name, plan: data.plan, computed: data.computed }] as const;
          })
        );
        if (cancelled) return;
        setLoaded((prev) => ({ ...prev, ...Object.fromEntries(entries) }));
        setTargets((prev) => ({
          ...prev,
          ...Object.fromEntries(entries.map(([id, v]) => [id, Math.max(0, v.computed.nachlass)])),
        }));
      } catch (e) {
        if (!cancelled) setLoadError(e instanceof Error ? e.message : "Szenarien konnten nicht geladen werden.");
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [scenarioKey, meta.id]);

  const allLoaded = useMemo(
    () => scenarios.map((s) => loaded[s.id]).filter((s): s is LoadedScenario => !!s),
    [scenarios, loaded]
  );

  // Ein Parametersatz je LOGISCHEM Element (über die sourceElementId-Kette zusammengefasst).
  const groups = useMemo(
    () => buildElementGroups(allLoaded, selectedIds),
    [allLoaded, selectedIds]
  );

  function draftFor(g: ElementGroup): ElementDraft {
    return drafts[g.rootId] ?? { mean: "", level: defaultVolatilityLevel(g.category), manualSigma: "10" };
  }
  function setDraft(rootId: string, group: ElementGroup, patch: Partial<ElementDraft>) {
    setDrafts((prev) => ({ ...prev, [rootId]: { ...draftFor(group), ...prev[rootId], ...patch } }));
  }

  function toggleScenario(id: string) {
    setSelectedIds((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));
    setResults(null);
  }

  const selected = selectedIds.map((id) => loaded[id]).filter((s): s is LoadedScenario => !!s);
  const anyReturnBearing = selected.some((s) => s.plan.elements.some((e) => RETURN_BEARING.includes(e.category)));

  // Pflichtfelder: historische Inflation + je logischem Element die historische Rendite.
  const missing =
    inflMean.trim() === "" || groups.some((g) => draftFor(g).mean.trim() === "");
  const canRun = !missing && selected.length > 0 && anyReturnBearing && !loading;

  const estSeconds = Math.max(1, Math.round((runs * Math.max(1, selected.length)) / 6000));

  async function run() {
    setRunning(true);
    setResults(null);
    setProgress({ index: 0, count: selected.length, fraction: 0 });
    try {
      const paramByRoot: Record<string, ElementMcParams> = Object.fromEntries(
        groups.map((g) => {
          const d = draftFor(g);
          return [g.rootId, { mean: Number(d.mean) || 0, sigma: returnSigma(d), floor: floorFor(g.category) }];
        })
      );
      // EIN Seed für alle Szenarien: strukturgleiche Szenarien teilen damit dieselben
      // Marktpfade, und die Unterschiede sind strukturell statt zufällig.
      const seed = (Math.random() * 2 ** 32) >>> 0;
      const runInputs: ScenarioRunInput[] = selected.map((s) => ({
        scenarioId: s.id,
        name: s.name,
        plan: s.plan,
        target: targets[s.id] ?? 0,
      }));
      const res = await runMonteCarloMulti(
        runInputs,
        {
          runs,
          inflationMean: Number(inflMean) || 0,
          inflationSigma: inflationSigma(inflLevel, inflManual),
          seed,
          elementsFor: (p) => paramsForScenario(p, groups, paramByRoot),
        },
        (index, count, fraction) => setProgress({ index, count, fraction })
      );
      setResults(res);
    } finally {
      setRunning(false);
    }
  }

  const overallProgress =
    progress.count > 0 ? (progress.index + progress.fraction) / progress.count : 0;

  return (
    <div className="fixed inset-0 z-40 flex items-start justify-center overflow-y-auto bg-black/40 px-4 py-8" onClick={onClose}>
      <div
        onClick={(e) => e.stopPropagation()}
        className="flex w-full max-w-4xl flex-col gap-4 rounded-2xl border border-border bg-surface p-6 shadow-xl"
      >
        <div className="flex items-center justify-between">
          <h2 className="flex items-center gap-2 text-base font-semibold text-fg">
            <Dices className="h-5 w-5 text-accent" /> Monte-Carlo-Simulation
          </h2>
          <button type="button" onClick={onClose} aria-label="Schliessen" className="rounded-md p-1 text-faint hover:bg-surface-2">
            <X className="h-4 w-4" />
          </button>
        </div>

        {/* Erklärung */}
        <div className="rounded-xl border border-border bg-surface-2 p-4 text-xs leading-relaxed text-muted">
          <p className="mb-2">
            <strong className="text-fg">Was ist das?</strong> Dein Plan rechnet mit einer festen Rendite und Inflation
            pro Jahr. Real schwanken beide. Die Simulation würfelt <strong className="text-fg">viele tausend mögliche
            Verläufe</strong> und zeigt, wie oft dein Plan aufgeht.
          </p>
          <p className="mb-2">
            <strong className="text-fg">Eingabe:</strong> je Anlage die historische Durchschnittsrendite (der Mittelpunkt,
            um den gewürfelt wird) und wie stark sie schwankt; dazu dieselben Angaben für die Inflation.
            <strong className="text-fg"> Ergebnis:</strong> die Wahrscheinlichkeit, dass das Geld reicht bzw. dein Zielbetrag
            erreicht wird, plus ein Fächer vom pessimistischen bis zum optimistischen Fall.
          </p>
          <p className="mb-2">
            <strong className="text-fg">Mehrere Szenarien:</strong> Du kannst unten mehrere Szenarien dieses Plans
            gleichzeitig rechnen. Die historischen Annahmen werden dabei nur <strong className="text-fg">einmal</strong> erfasst
            und für alle Szenarien verwendet – sonst würdest du deine Eingaben vergleichen statt der Szenarien. Alle
            Szenarien laufen zudem mit demselben Zufalls-Seed, damit Unterschiede nicht blosses Rauschen sind.
          </p>
          <p>
            <strong className="text-fg">Verteilung:</strong> Renditen werden mit «fetten Rändern» gezogen (Extremcrashs so
            häufig wie in der Realität, nicht wie in der Glockenkurve), und alle riskanten Anlagen fallen in einem
            Marktschock gemeinsam. <em>Die Prozentzahl gilt immer nur relativ zu deinen Annahmen — sie beurteilt nicht,
            ob deine Durchschnittswerte realistisch sind.</em>
          </p>
        </div>

        {/* Szenario-Auswahl inkl. Zielbetrag je Szenario */}
        <div>
          <div className="mb-2 flex items-center text-xs font-semibold uppercase tracking-wide text-faint">
            Szenarien
            <InfoBubble text="Wähle, für welche Szenarien dieses Plans gerechnet werden soll. Der Zielbetrag ist je Szenario eigen und mit dem jeweils geplanten Endvermögen vorbelegt: So misst die Erfolgswahrscheinlichkeit, wie oft jedes Szenario sein eigenes Versprechen hält. Ein pessimistisch geplantes Szenario hat einen tieferen Zielbetrag und damit eine höhere Erfolgswahrscheinlichkeit." />
          </div>
          {loading && <p className="text-xs text-muted">Szenarien werden geladen…</p>}
          {loadError && <p className="text-xs text-danger">{loadError}</p>}
          <div className="flex flex-col gap-2">
            {scenarios.map((s) => {
              const isLoaded = !!loaded[s.id];
              const checked = selectedIds.includes(s.id);
              return (
                <div
                  key={s.id}
                  className={`flex flex-wrap items-center gap-3 rounded-xl border p-3 ${
                    checked ? "border-accent bg-accent-soft/30" : "border-border bg-surface-2"
                  }`}
                >
                  <label className="flex min-w-0 flex-1 items-center gap-2 text-sm">
                    <input
                      type="checkbox"
                      checked={checked}
                      disabled={!isLoaded}
                      onChange={() => toggleScenario(s.id)}
                    />
                    <span className="truncate font-medium text-fg">{s.name}</span>
                    {s.isBase && <span className="rounded bg-surface px-1.5 text-[10px] text-muted">Basis</span>}
                    {!isLoaded && <span className="text-[11px] text-faint">lädt…</span>}
                  </label>
                  {checked && (
                    <div className="w-56">
                      <MoneyField
                        label="Zielbetrag (Endvermögen)"
                        help="Vorbelegt mit dem geplanten Endvermögen DIESES Szenarios. Die Erfolgswahrscheinlichkeit misst, wie oft dieser Betrag erreicht wird."
                        value={targets[s.id] ?? 0}
                        onChange={(v) => setTargets((prev) => ({ ...prev, [s.id]: v }))}
                      />
                    </div>
                  )}
                </div>
              );
            })}
          </div>
          {selected.length === 0 && (
            <p className="mt-2 text-xs text-danger">Bitte mindestens ein Szenario auswählen.</p>
          )}
        </div>

        {/* Inflation */}
        <div>
          <div className="mb-2 text-xs font-semibold uppercase tracking-wide text-faint">Inflation (Plan-Ebene)</div>
          <div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
            <RequiredNumberField
              label="Ø Inflation letzte 20 J. (%)"
              help={`Der Mittelpunkt, um den gewürfelt wird. Deine Planung nutzt aktuell ${plan.inflationRateDefault} %. CH langfristig ~2 %.`}
              value={inflMean}
              onChange={setInflMean}
            />
            <SelectField
              label="Streuung"
              help={INFLATION_HELP}
              value={inflLevel}
              onChange={(v: InflationVolatilityLevel) => setInflLevel(v)}
              options={INFLATION_LEVEL_OPTIONS}
            />
            {inflLevel === "manuell" ? (
              <NumberField label="Standardabw. (%)" step={0.1} value={Number(inflManual) || 0} onChange={(v) => setInflManual(String(v))} />
            ) : (
              <ReadOnlySigma value={INFLATION_VOLATILITY_LEVELS[inflLevel]} />
            )}
          </div>
        </div>

        {/* Renditetragende Elemente -- ein Satz je logischem Element */}
        <div>
          <div className="mb-2 flex items-center text-xs font-semibold uppercase tracking-wide text-faint">
            Renditetragende Elemente
            {selected.length > 1 && (
              <InfoBubble text="Ein Element, das in mehreren Szenarien vorkommt, erscheint hier nur einmal – erkannt über seine Herkunft beim Kopieren des Szenarios, nicht über den Namen. Elemente, die es nur in einem Szenario gibt, erscheinen mit einem entsprechenden Hinweis." />
            )}
          </div>
          {groups.length === 0 && (
            <p className="rounded-lg border border-dashed border-border bg-surface-2 p-3 text-xs text-muted">
              Die ausgewählten Szenarien haben keine renditetragenden Elemente (PK, 3a, Sonstiges Vermögen, Immobilie).
            </p>
          )}
          <div className="flex flex-col gap-3">
            {groups.map((g) => {
              const d = draftFor(g);
              const partial = g.scenarioIds.length < selected.length;
              return (
                <div key={g.rootId} className="rounded-xl border border-border bg-surface-2 p-3">
                  <div className="mb-2 flex flex-wrap items-center gap-2 text-sm">
                    <span className="font-semibold text-fg">{g.name}</span>
                    <span className="text-xs text-faint">{CATEGORY_LABELS[g.category]}</span>
                    {partial && (
                      <span className="rounded bg-surface px-1.5 py-0.5 text-[10px] text-muted">
                        nur in: {g.scenarioIds.map((id) => loaded[id]?.name ?? id).join(", ")}
                      </span>
                    )}
                  </div>
                  <div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
                    <RequiredNumberField
                      label="Ø Rendite letzte 20 J. (%)"
                      help="Der Mittelpunkt, um den gewürfelt wird – die realistische Rendite dieser Anlage, nicht deine (evtl. optimistische) Planannahme."
                      value={d.mean}
                      onChange={(v) => setDraft(g.rootId, g, { mean: v })}
                    />
                    <SelectField
                      label="Streuung"
                      help={RETURN_HELP}
                      value={d.level}
                      onChange={(v: ReturnVolatilityLevel) => setDraft(g.rootId, g, { level: v })}
                      options={RETURN_LEVEL_OPTIONS}
                    />
                    {d.level === "manuell" ? (
                      <NumberField
                        label="Standardabw. (%)"
                        step={0.5}
                        value={Number(d.manualSigma) || 0}
                        onChange={(v) => setDraft(g.rootId, g, { manualSigma: String(v) })}
                      />
                    ) : (
                      <ReadOnlySigma value={RETURN_VOLATILITY_LEVELS[d.level]} />
                    )}
                  </div>
                  {(g.category === "PENSION_FUND" || g.category === "PILLAR_3A") && (
                    <p className="mt-2 text-[11px] text-faint">Boden 0 %: {CATEGORY_LABELS[g.category]} schreibt keine negative Rendite gut.</p>
                  )}
                </div>
              );
            })}
          </div>
        </div>

        {/* Lauf-Parameter */}
        <div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
          <div>
            <SelectField
              label="Anzahl Simulationen"
              value={String(runs)}
              onChange={(v: string) => setRuns(Number(v))}
              options={[
                { value: "1000", label: "1'000 (schnell)" },
                { value: "5000", label: "5'000" },
                { value: "10000", label: "10'000 (genau)" },
              ]}
            />
            <p className="mt-1 text-[11px] text-faint">
              {selected.length > 1 ? `${selected.length} Szenarien · ` : ""}geschätzt ~{estSeconds} s
            </p>
          </div>
        </div>

        {missing && <p className="text-xs text-danger">Bitte alle Pflichtfelder (Ø-Werte) ausfüllen, um die Simulation zu starten.</p>}

        <div className="flex items-center gap-3">
          <button
            type="button"
            disabled={!canRun || running}
            onClick={run}
            className="rounded-lg bg-accent px-4 py-2 text-sm font-medium text-accent-fg shadow-sm hover:bg-accent-hover disabled:opacity-50"
          >
            {running
              ? `Berechne… ${progress.count > 1 ? `Szenario ${progress.index + 1}/${progress.count} · ` : ""}${Math.round(overallProgress * 100)} %`
              : "Berechnung jetzt durchführen"}
          </button>
          {running && (
            <div className="h-1.5 flex-1 overflow-hidden rounded-full bg-surface-2">
              <div className="h-full bg-accent transition-all" style={{ width: `${overallProgress * 100}%` }} />
            </div>
          )}
        </div>

        {results && results.length > 0 && (
          <MonteCarloResults
            results={results}
            detPoints={
              results.length === 1 && loaded[results[0].scenarioId]
                ? detPointsOf(loaded[results[0].scenarioId].plan, loaded[results[0].scenarioId].computed)
                : []
            }
          />
        )}
      </div>
    </div>
  );
}

function ReadOnlySigma({ value }: { value: number }) {
  return (
    <div>
      <label className="mb-1 flex items-center text-xs font-medium text-muted">
        Standardabweichung
        <InfoBubble text="Die hinter der gewählten Streuungsstufe hinterlegte Standardabweichung. Nur bei Manuell selbst eingebbar." />
      </label>
      <div className="w-full rounded-lg border border-dashed border-border bg-surface-2 px-2.5 py-1.5 text-sm text-muted">
        {value} %
      </div>
    </div>
  );
}

function MonteCarloResults({
  results,
  detPoints,
}: {
  results: ScenarioMcResult[];
  detPoints: { age: number; det: number }[];
}) {
  const single = results.length === 1;

  // Einzelnes Szenario: der gewohnte Fächer (Band + Median + Planungslinie).
  const singleData = useMemo(() => {
    if (!single) return [];
    return results[0].bands.map((b) => ({
      age: b.age,
      band: [b.p10, b.p90] as [number, number],
      median: b.p50,
      det: detPoints.find((d) => d.age === b.age)?.det ?? null,
    }));
  }, [single, results, detPoints]);

  // Mehrere Szenarien: nur die Median-Linien -- übereinandergelegte Bänder wären Farbbrei.
  const compareData = useMemo(() => {
    if (single) return [];
    const ages = Array.from(new Set(results.flatMap((r) => r.bands.map((b) => b.age)))).sort((a, b) => a - b);
    return ages.map((age) => {
      const row: Record<string, number | null> = { age };
      for (const r of results) row[r.scenarioId] = r.bands.find((b) => b.age === age)?.p50 ?? null;
      return row;
    });
  }, [single, results]);

  return (
    <div className="flex flex-col gap-4 border-t border-border pt-4">
      {/* Vergleichstabelle -- das eigentliche Vergleichsinstrument */}
      <div>
        <div className="mb-1 flex items-center text-xs font-semibold text-fg">
          Ergebnis je Szenario
          {!single && (
            <InfoBubble text="Die Erfolgswahrscheinlichkeit bezieht sich je Szenario auf dessen EIGENEN Zielbetrag (Spalte Ziel) – sie misst, wie oft ein Szenario sein eigenes Versprechen hält. Die Ruinwahrscheinlichkeit ist zielbetrags-unabhängig und damit direkt vergleichbar." />
          )}
        </div>
        <div className="overflow-x-auto rounded-xl border border-border">
          <table className="w-full border-collapse text-sm">
            <thead>
              <tr className="bg-surface-2 text-xs text-faint">
                <th className="px-3 py-2 text-left font-semibold">Szenario</th>
                <th className="px-3 py-2 text-right font-semibold">Ziel</th>
                <th className="px-3 py-2 text-right font-semibold">Erfolg</th>
                <th className="px-3 py-2 text-right font-semibold">Ruin</th>
                <th className="px-3 py-2 text-right font-semibold">Pessimistisch (10 %)</th>
                <th className="px-3 py-2 text-right font-semibold">Median</th>
                <th className="px-3 py-2 text-right font-semibold">Optimistisch (90 %)</th>
              </tr>
            </thead>
            <tbody>
              {results.map((r, i) => (
                <tr key={r.scenarioId} className="border-t border-border">
                  <td className="px-3 py-2">
                    <span className="flex items-center gap-2 font-medium text-fg">
                      {!single && (
                        <span
                          className="inline-block h-2 w-2 shrink-0 rounded-full"
                          style={{ backgroundColor: PALETTE[i % PALETTE.length] }}
                        />
                      )}
                      {r.name}
                    </span>
                  </td>
                  <td className="px-3 py-2 text-right text-muted">{formatChf(r.target)}</td>
                  <td className="px-3 py-2 text-right font-semibold text-success">
                    {Math.round(r.successProbability * 100)} %
                  </td>
                  <td className="px-3 py-2 text-right font-semibold text-danger">
                    {Math.round(r.ruinProbability * 100)} %
                  </td>
                  <td className="px-3 py-2 text-right text-muted">{formatChf(r.finalWealthP10)}</td>
                  <td className="px-3 py-2 text-right text-fg">{formatChf(r.finalWealthMedian)}</td>
                  <td className="px-3 py-2 text-right text-muted">{formatChf(r.finalWealthP90)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <p className="mt-1 text-[11px] text-faint">
          {results[0].runs.toLocaleString("de-CH")} Läufe je Szenario
          {!single && " · gemeinsamer Zufalls-Seed"}
        </p>
      </div>

      <div>
        <div className="mb-1 text-xs font-semibold text-fg">
          {single ? "Vermögensfächer nach Alter (nominal)" : "Vermögensverlauf im Median nach Alter (nominal)"}
        </div>
        <p className="mb-2 text-[11px] text-muted">
          {single ? (
            <>
              Das Band reicht vom pessimistischen (10 %) bis zum optimistischen (90 %) Fall, die dunkle Linie ist der
              Median. Die gestrichelte Linie ist deine deterministische Planung – sie liegt meist leicht über dem Median
              (Schwankung frisst Rendite).
            </>
          ) : (
            <>
              Je Szenario die mittlere Entwicklung (Median). Die vollständigen 10–90 %-Bänder werden hier bewusst nicht
              übereinandergelegt – wähle ein einzelnes Szenario, um den Fächer zu sehen.
            </>
          )}
        </p>
        <div className="h-72 w-full">
          <ResponsiveContainer width="100%" height="100%">
            {single ? (
              <ComposedChart data={singleData} margin={{ top: 8, right: 16, left: 8, bottom: 8 }}>
                <CartesianGrid strokeDasharray="3 3" className="stroke-border" />
                <XAxis dataKey="age" tick={{ fontSize: 11 }} tickFormatter={(v) => `${v} J.`} />
                <YAxis tick={{ fontSize: 11 }} tickFormatter={(v) => Intl.NumberFormat("de-CH", { notation: "compact" }).format(v)} />
                <Tooltip
                  formatter={(v, name) => [typeof v === "number" ? formatChf(v) : v, name]}
                  labelFormatter={(v) => `Alter ${v}`}
                />
                <Legend wrapperStyle={{ fontSize: 12 }} />
                <Area dataKey="band" name="10–90 % Band" stroke="none" fill="var(--accent)" fillOpacity={0.16} isAnimationActive={false} />
                <Line dataKey="median" name="Median" stroke="var(--accent)" strokeWidth={2} dot={false} isAnimationActive={false} />
                <Line dataKey="det" name="Planung (deterministisch)" stroke="var(--muted)" strokeWidth={1.5} strokeDasharray="5 3" dot={false} connectNulls isAnimationActive={false} />
              </ComposedChart>
            ) : (
              <LineChart data={compareData} margin={{ top: 8, right: 16, left: 8, bottom: 8 }}>
                <CartesianGrid strokeDasharray="3 3" className="stroke-border" />
                <XAxis
                  dataKey="age"
                  type="number"
                  domain={["dataMin", "dataMax"]}
                  tick={{ fontSize: 11 }}
                  tickFormatter={(v) => `${v} J.`}
                />
                <YAxis tick={{ fontSize: 11 }} tickFormatter={(v) => Intl.NumberFormat("de-CH", { notation: "compact" }).format(v)} />
                <Tooltip
                  formatter={(v, name) => [typeof v === "number" ? formatChf(v) : v, name]}
                  labelFormatter={(v) => `Alter ${v}`}
                />
                <Legend wrapperStyle={{ fontSize: 12 }} />
                {results.map((r, i) => (
                  <Line
                    key={r.scenarioId}
                    dataKey={r.scenarioId}
                    name={r.name}
                    stroke={PALETTE[i % PALETTE.length]}
                    strokeWidth={2}
                    dot={false}
                    connectNulls
                    isAnimationActive={false}
                  />
                ))}
              </LineChart>
            )}
          </ResponsiveContainer>
        </div>
      </div>
    </div>
  );
}
