"use client";

import { useMemo, useState } from "react";
import {
  Bar,
  BarChart,
  CartesianGrid,
  Legend,
  Line,
  LineChart,
  ResponsiveContainer,
  Tooltip,
  XAxis,
  YAxis,
} from "recharts";
import { BookOpen, X } from "lucide-react";
import { formatChf } from "@/lib/format";
import { CATEGORY_LABELS } from "@/lib/elements";
import type { ElementCategory } from "@/lib/elements";
import type {
  CashBridge,
  ElementYearPoint,
  PhaseComputed,
  PlanComputed,
  Trace,
  TraceStep,
  WealthBridge,
} from "@/lib/calculations";

// --- Wasserfall ---------------------------------------------------------------------------
// Bewusst NICHT mit Recharts, sondern als eigene HTML/CSS-Darstellung. Ein Wasserfall lebt
// von drei Dingen, die Recharts hier nicht hergibt: Verbindungslinien zwischen den Balken
// (ohne sie sieht man nicht, dass jeder Balken dort ansetzt, wo der vorherige aufhört),
// Wertbeschriftung an jedem Balken, und eine klare optische Trennung von Zwischenständen
// und Veränderungen.
//
// Liegend statt stehend: Die Beschriftungen sind lang ("Wertsteigerung Immobilie"), stehend
// müssten sie gedreht werden. Liegend ist es ausserdem konsistent zum Tornado.

interface WaterfallItem {
  label: string;
  value: number;
  total?: boolean; // Zwischen-/Endsumme: absoluter Stand statt Veränderung
  section?: string; // optionale Abschnitts-Überschrift VOR diesem Eintrag
}

interface WaterfallRow {
  label: string;
  section?: string;
  from: number;
  to: number;
  value: number;
  running: number; // Stand NACH diesem Schritt
  kind: "total" | "pos" | "neg";
}

function waterfallRows(items: WaterfallItem[]): WaterfallRow[] {
  let running = 0;
  return items.map((it) => {
    if (it.total) {
      running = it.value;
      return { label: it.label, section: it.section, from: 0, to: it.value, value: it.value, running, kind: "total" as const };
    }
    const from = running;
    running += it.value;
    return {
      label: it.label,
      section: it.section,
      from,
      to: running,
      value: it.value,
      running,
      kind: (it.value >= 0 ? "pos" : "neg") as "pos" | "neg",
    };
  });
}

const WF_FILL = { total: "var(--accent)", pos: "#16a34a", neg: "#dc2626" };

const ROW_H = 34;
const BAR_H = 20;

function Waterfall({ items }: { items: WaterfallItem[] }) {
  const rows = useMemo(() => waterfallRows(items), [items]);
  if (rows.length === 0) return null;

  const lo = Math.min(0, ...rows.map((r) => Math.min(r.from, r.to)));
  const hi = Math.max(0, ...rows.map((r) => Math.max(r.from, r.to)));
  const span = hi - lo || 1;
  const pos = (v: number) => ((v - lo) / span) * 100;

  return (
    <div>
      <div className="overflow-hidden rounded-xl border border-border">
        {rows.map((r, i) => {
          const left = pos(Math.min(r.from, r.to));
          const width = Math.max(0.4, Math.abs(pos(r.to) - pos(r.from)));
          const isLast = i === rows.length - 1;
          return (
            <div key={`${r.label}-${i}`}>
              {r.section && (
                <div className="border-b border-border bg-surface-2 px-3 py-1 text-[10px] font-semibold uppercase tracking-wide text-faint">
                  {r.section}
                </div>
              )}
              <div className={`flex items-stretch ${r.kind === "total" ? "bg-surface-2" : ""}`}>
                <div
                  className={`w-44 shrink-0 border-r border-border px-3 py-2 text-[11px] leading-tight ${
                    r.kind === "total" ? "font-semibold text-fg" : "text-muted"
                  }`}
                >
                  {r.label}
                </div>
                <div className="relative min-w-0 flex-1" style={{ height: ROW_H }}>
                  {/* Nulllinie */}
                  <div
                    className="absolute top-0 h-full border-l border-dashed border-border"
                    style={{ left: `${pos(0)}%` }}
                  />
                  {/* Balken */}
                  <div
                    className="absolute rounded-sm"
                    style={{
                      left: `${left}%`,
                      width: `${width}%`,
                      top: (ROW_H - BAR_H) / 2,
                      height: BAR_H,
                      backgroundColor: WF_FILL[r.kind],
                      opacity: r.kind === "total" ? 0.85 : 1,
                    }}
                    title={`${r.label}: ${formatChf(r.value)}`}
                  />
                  {/* Verbindungslinie zum nächsten Balken: auf dem Stand NACH diesem Schritt */}
                  {!isLast && (
                    <div
                      className="absolute border-l border-dotted border-faint"
                      style={{
                        left: `${pos(r.to)}%`,
                        top: (ROW_H + BAR_H) / 2,
                        height: (ROW_H - BAR_H) / 2,
                      }}
                    />
                  )}
                </div>
                <div
                  className={`w-28 shrink-0 border-l border-border px-3 py-2 text-right text-[11px] tabular-nums ${
                    r.kind === "total" ? "font-semibold text-fg" : r.kind === "neg" ? "text-danger" : "text-success"
                  }`}
                >
                  {r.kind === "total" ? formatChf(r.value) : `${r.value >= 0 ? "+" : "−"}${formatChf(Math.abs(r.value))}`}
                </div>
              </div>
            </div>
          );
        })}
      </div>

      {/* Zahlen mit laufendem Zwischenstand -- bei stark unterschiedlichen Grössenordnungen
          ist die Tabelle der Grafik überlegen. */}
      <details className="mt-2">
        <summary className="cursor-pointer text-[11px] text-muted hover:text-fg">Zahlen mit Zwischenstand anzeigen</summary>
        <div className="mt-2 overflow-x-auto rounded-lg border border-border">
          <table className="w-full border-collapse text-xs">
            <thead>
              <tr className="bg-surface-2 text-[10px] uppercase tracking-wide text-faint">
                <th className="px-3 py-1.5 text-left font-semibold">Schritt</th>
                <th className="px-3 py-1.5 text-right font-semibold">Betrag</th>
                <th className="px-3 py-1.5 text-right font-semibold">Zwischenstand</th>
              </tr>
            </thead>
            <tbody>
              {rows.map((r, i) => (
                <tr key={i} className={`border-t border-border ${r.kind === "total" ? "bg-surface-2 font-semibold" : ""}`}>
                  <td className="px-3 py-1.5 text-fg">{r.label}</td>
                  <td
                    className={`px-3 py-1.5 text-right tabular-nums ${
                      r.kind === "total" ? "text-faint" : r.kind === "neg" ? "text-danger" : "text-success"
                    }`}
                  >
                    {r.kind === "total" ? "—" : `${r.value >= 0 ? "+" : "−"}${formatChf(Math.abs(r.value))}`}
                  </td>
                  <td className="px-3 py-1.5 text-right tabular-nums text-fg">{formatChf(r.running)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </details>
    </div>
  );
}

// Kontrollgrösse: Ist die Zerlegung vollständig, muss die Differenz zwischen Endwert und
// der Summe der Schritte 0 sein. Sichtbar machen statt verstecken -- ein Wasserfall, der
// nicht aufgeht, ist ein Fehler und kein Schönheitsproblem.
function ResidualNote({ residual }: { residual: number }) {
  if (Math.abs(residual) <= 2) return null;
  return (
    <p className="mt-2 rounded-lg border border-danger bg-danger-soft px-3 py-2 text-[11px] text-danger">
      <strong>Die Zerlegung geht nicht auf.</strong> Nicht zugeordnete Differenz: {formatChf(residual)}. Bitte melden –
      das ist ein Fehler in der Berechnung, nicht in der Darstellung.
    </p>
  );
}

function bridgeItems(w: WealthBridge, isFirst: boolean): WaterfallItem[] {
  const items: WaterfallItem[] = [];
  if (!isFirst) {
    items.push({ label: "Vermögen Ende Vorphase", value: w.openingWealth, total: true, section: "Am Übergang in diese Phase" });
    if (w.oneOffInflow) items.push({ label: "Einmaliger Zufluss", value: w.oneOffInflow });
    if (w.oneOffOutflow) items.push({ label: "Einmalige Kosten", value: -w.oneOffOutflow });
    if (w.transitionTax) items.push({ label: "Steuern am Übergang", value: -w.transitionTax });
    if (w.pensionConversion) items.push({ label: "PK in Rente umgewandelt", value: -w.pensionConversion });
    if (w.saleGainLoss) items.push({ label: "Verkaufsdifferenz", value: w.saleGainLoss });
  }
  items.push({
    label: "Vermögen Phasenbeginn",
    value: w.startWealth,
    total: true,
    section: isFirst ? undefined : "Innerhalb der Phase",
  });
  if (w.quotaTotal) items.push({ label: "Spar-/Verzehrquote", value: w.quotaTotal });
  if (w.investmentReturn) items.push({ label: "Kapitalerträge", value: w.investmentReturn });
  if (w.propertyAppreciation) items.push({ label: "Wertsteigerung Immobilie", value: w.propertyAppreciation });
  if (w.pensionFundContribution) items.push({ label: "PK-Beiträge", value: w.pensionFundContribution });
  items.push({ label: "Vermögen Phasenende", value: w.endWealth, total: true });
  return items;
}

function cashItems(c: CashBridge, isFirst: boolean): WaterfallItem[] {
  const items: WaterfallItem[] = [];
  items.push({
    label: isFirst ? "Cash-Anfangswert" : "Cash Ende Vorphase",
    value: c.openingCash,
    total: true,
    section: isFirst ? undefined : "Am Übergang in diese Phase",
  });
  if (c.capitalInflow) items.push({ label: "Kapitalzufluss", value: c.capitalInflow });
  if (c.oneOffInflow) items.push({ label: "Einmaliger Zufluss", value: c.oneOffInflow });
  if (c.immediateRepay) items.push({ label: "Sofort-Tilgung", value: -c.immediateRepay });
  if (c.oneOffOutflow) items.push({ label: "Einmalige Kosten", value: -c.oneOffOutflow });
  if (c.investments) items.push({ label: "Investitionen", value: -c.investments });
  items.push({ label: "Cash Phasenbeginn", value: c.cashStart, total: true, section: "Innerhalb der Phase" });
  if (c.quotaTotal) items.push({ label: "Spar-/Verzehrquote", value: c.quotaTotal });
  if (c.savingRates) items.push({ label: "Sparraten", value: -c.savingRates });
  if (c.debtRates) items.push({ label: "Amort./Tilgung", value: -c.debtRates });
  if (c.withdrawals) items.push({ label: "Bezugsraten", value: c.withdrawals });
  items.push({ label: "Cash Phasenende", value: c.cashEnd, total: true });
  return items;
}

// --- Rechenweg ----------------------------------------------------------------------------

function TraceStepRow({ step }: { step: TraceStep }) {
  const unit = step.unit ?? "CHF";
  const value =
    unit === "" ? step.substituted ?? "" : unit === "%" ? `${step.result} %` : unit === "Jahre" ? `${step.result}` : formatChf(step.result);
  return (
    <tr className="border-t border-border align-top">
      <td className="px-3 py-1.5 text-fg">{step.label}</td>
      <td className="px-3 py-1.5 text-[11px] text-muted">
        {step.formula && <div className="font-mono">{step.formula}</div>}
        {step.substituted && unit !== "" && <div className="font-mono text-faint">{step.substituted}</div>}
        {step.note && <div className="mt-0.5 italic text-faint">{step.note}</div>}
      </td>
      <td className="whitespace-nowrap px-3 py-1.5 text-right font-medium text-fg">{value}</td>
    </tr>
  );
}

export function TraceBlock({ trace, onOpenSpec }: { trace: Trace; onOpenSpec?: (anchor: string) => void }) {
  return (
    <div className="rounded-xl border border-border">
      <div className="flex items-center justify-between gap-2 border-b border-border bg-surface-2 px-3 py-2">
        <span className="text-xs font-semibold text-fg">{trace.title}</span>
        {trace.specAnchor && onOpenSpec && (
          <button
            type="button"
            onClick={() => onOpenSpec(trace.specAnchor!)}
            className="flex shrink-0 items-center gap-1 rounded-md border border-border px-2 py-0.5 text-[11px] text-muted hover:bg-surface"
          >
            <BookOpen className="h-3 w-3" /> in der Spezifikation
          </button>
        )}
      </div>
      <table className="w-full border-collapse text-sm">
        <tbody>
          {trace.steps.map((s, i) => (
            <TraceStepRow key={i} step={s} />
          ))}
        </tbody>
      </table>
    </div>
  );
}

// --- Gemeinsame Dialog-Huelle mit Reitern --------------------------------------------------

function DetailShell({
  title,
  subtitle,
  tabs,
  onClose,
}: {
  title: string;
  subtitle?: string;
  tabs: { key: string; label: string; content: React.ReactNode }[];
  onClose: () => void;
}) {
  const [active, setActive] = useState(tabs[0]?.key);
  const current = tabs.find((t) => t.key === active) ?? tabs[0];
  return (
    <div className="fixed inset-0 z-50 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-start justify-between gap-3">
          <div className="min-w-0">
            <h2 className="truncate text-base font-semibold text-fg">{title}</h2>
            {subtitle && <p className="text-xs text-muted">{subtitle}</p>}
          </div>
          <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>

        <div className="inline-flex w-fit rounded-lg border border-border bg-surface p-0.5 text-xs">
          {tabs.map((t) => (
            <button
              key={t.key}
              type="button"
              onClick={() => setActive(t.key)}
              className={`rounded-md px-3 py-1 font-medium ${
                current?.key === t.key ? "bg-accent text-accent-fg" : "text-muted hover:bg-surface-2"
              }`}
            >
              {t.label}
            </button>
          ))}
        </div>

        <div className="flex flex-col gap-4">{current?.content}</div>

        <p className="text-[11px] text-faint">Nur-Lese-Ansicht. Werte werden hier nicht verändert.</p>
      </div>
    </div>
  );
}

// --- Element-Detailansicht ----------------------------------------------------------------

const MULTI_SERIES: ElementCategory[] = ["REAL_ESTATE"];

export function ElementDetailDialog({
  elementId,
  name,
  category,
  computed,
  onClose,
  onOpenSpec,
}: {
  elementId: string;
  name: string;
  category: ElementCategory;
  computed: PlanComputed;
  onClose: () => void;
  onOpenSpec?: (anchor: string) => void;
}) {
  // Verlauf über ALLE Phasen zusammensetzen.
  const points = useMemo(() => {
    const out: ElementYearPoint[] = [];
    for (const ph of computed.phases) {
      const ec = ph.elements.find((e) => e.elementId === elementId);
      if (ec) out.push(...ec.yearly);
    }
    return out;
  }, [computed, elementId]);

  const perPhase = useMemo(
    () =>
      computed.phases
        .map((ph) => ({ phase: ph, ec: ph.elements.find((e) => e.elementId === elementId) }))
        .filter((x) => x.ec),
    [computed, elementId]
  );

  const isFlow = category === "INCOME" || category === "EXPENSE";
  const valueLabel = isFlow
    ? category === "INCOME"
      ? "Einkommen (nominal)"
      : "Ausgaben (nominal)"
    : category === "OTHER_DEBT"
      ? "Beitrag zum Vermögen"
      : category === "REAL_ESTATE"
        ? "Eigenkapital"
        : "Wert";

  // Zweite Achse nur, wenn dieses Element überhaupt eine Rate trägt (AHV und Schulden nicht).
  const rateLabel =
    category === "REAL_ESTATE"
      ? "Wertsteigerung"
      : isFlow
        ? category === "INCOME"
          ? "Lohnentwicklung"
          : "Reale Mehrausgaben"
        : "Erwartete Rendite";
  const hasRate = points.some((p) => typeof p.rate === "number");

  const verlauf = (
    <>
      {points.length === 0 ? (
        <p className="text-sm text-muted">Für dieses Element gibt es in diesem Plan keinen Verlauf.</p>
      ) : (
        <div className="h-80 w-full">
          <ResponsiveContainer width="100%" height="100%">
            <LineChart data={points} 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 yAxisId="chf" tick={{ fontSize: 11 }} tickFormatter={(v) => Intl.NumberFormat("de-CH", { notation: "compact" }).format(v)} />
              {hasRate && (
                <YAxis
                  yAxisId="rate"
                  orientation="right"
                  tick={{ fontSize: 11 }}
                  tickFormatter={(v) => `${v} %`}
                  // Etwas Luft nach oben und unten, damit eine konstante Rate nicht als
                  // Linie direkt auf der Achse klebt.
                  domain={([min, max]: readonly [number, number]) => [Math.min(0, min - 1), max + 1]}
                />
              )}
              <Tooltip
                formatter={(v, n) =>
                  typeof v !== "number" ? [v, n] : n === rateLabel ? [`${v} %`, n] : [formatChf(v), n]
                }
                labelFormatter={(v) => `Alter ${v}`}
              />
              <Legend wrapperStyle={{ fontSize: 12 }} />
              <Line yAxisId="chf" dataKey="value" name={valueLabel} stroke="var(--accent)" strokeWidth={2} dot={false} isAnimationActive={false} />
              {MULTI_SERIES.includes(category) && (
                <>
                  <Line yAxisId="chf" dataKey="propertyValue" name="Verkehrswert" stroke="#0ea5e9" strokeWidth={1.5} dot={false} isAnimationActive={false} />
                  <Line yAxisId="chf" dataKey="mortgage" name="Restschuld" stroke="#dc2626" strokeWidth={1.5} strokeDasharray="5 3" dot={false} isAnimationActive={false} />
                </>
              )}
              {hasRate && (
                // Stufenlinie, nicht interpoliert: Die Rate ist innerhalb einer Phase
                // konstant und springt an der Phasengrenze. Eine weiche Kurve würde einen
                // gleitenden Übergang suggerieren, den die Berechnung nicht macht.
                <Line
                  yAxisId="rate"
                  type="stepAfter"
                  dataKey="rate"
                  name={rateLabel}
                  stroke="#d97706"
                  strokeWidth={1.5}
                  strokeDasharray="4 2"
                  dot={false}
                  connectNulls
                  isAnimationActive={false}
                />
              )}
            </LineChart>
          </ResponsiveContainer>
        </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">Lebensphase</th>
              <th className="px-3 py-2 text-right font-semibold">Beginn</th>
              <th className="px-3 py-2 text-right font-semibold">Ende</th>
              <th className="px-3 py-2 text-left font-semibold">Hinweis</th>
            </tr>
          </thead>
          <tbody>
            {perPhase.map(({ phase, ec }) => (
              <tr key={phase.id} className="border-t border-border">
                <td className="px-3 py-2 text-fg">{phase.name}</td>
                <td className="px-3 py-2 text-right text-muted">{formatChf(ec!.startValue)}</td>
                <td className="px-3 py-2 text-right text-muted">{formatChf(ec!.endValue)}</td>
                <td className="px-3 py-2 text-[11px] text-faint">{ec!.note ?? ""}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </>
  );

  const rechenweg = (
    <>
      {perPhase.every(({ ec }) => !ec!.trace && !ec!.transitionTrace) && (
        <p className="text-sm text-muted">Für dieses Element gibt es keinen eigenen Rechenweg.</p>
      )}
      {perPhase.map(({ phase, ec }) => (
        <div key={phase.id} className="flex flex-col gap-2">
          {(ec!.trace || ec!.transitionTrace) && (
            <div className="text-xs font-semibold uppercase tracking-wide text-faint">{phase.name}</div>
          )}
          {ec!.trace && <TraceBlock trace={ec!.trace} onOpenSpec={onOpenSpec} />}
          {ec!.transitionTrace && <TraceBlock trace={ec!.transitionTrace} onOpenSpec={onOpenSpec} />}
        </div>
      ))}
    </>
  );

  return (
    <DetailShell
      title={name}
      subtitle={CATEGORY_LABELS[category]}
      onClose={onClose}
      tabs={[
        { key: "verlauf", label: "Verlauf", content: verlauf },
        { key: "rechenweg", label: "Rechenweg", content: rechenweg },
      ]}
    />
  );
}

// --- Phasen-Detailansicht ------------------------------------------------------------------

const ASSET_CATS: ElementCategory[] = ["PENSION_FUND", "PILLAR_3A", "REAL_ESTATE", "OTHER_ASSET"];
const ALLOC_PALETTE = ["#4f46e5", "#0ea5e9", "#16a34a", "#d97706", "#dc2626", "#7c3äd"];

export function PhaseDetailDialog({
  phase,
  isFirst,
  onClose,
  onOpenSpec,
}: {
  phase: PhaseComputed;
  isFirst: boolean;
  onClose: () => void;
  onOpenSpec?: (anchor: string) => void;
}) {
  const alloc = useMemo(() => {
    const rows = phase.elements.filter((e) => ASSET_CATS.includes(e.category) && (e.startValue > 0 || e.endValue > 0));
    return [
      { label: "Beginn", ...Object.fromEntries(rows.map((r) => [r.elementId, Math.max(0, r.startValue)])) },
      { label: "Ende", ...Object.fromEntries(rows.map((r) => [r.elementId, Math.max(0, r.endValue)])) },
    ];
  }, [phase]);
  const allocEls = useMemo(
    () => phase.elements.filter((e) => ASSET_CATS.includes(e.category) && (e.startValue > 0 || e.endValue > 0)),
    [phase]
  );

  const übersicht = (
    <>
      <div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
        <Kpi label="Typ" text={phase.type === "ERWERB" ? "Erwerb" : phase.type === "PENSION" ? "Pension" : "Misch"} />
        <Kpi label="Dauer" text={`${phase.durationYears} Jahre`} />
        <Kpi label="Vermögen Beginn" text={formatChf(phase.startWealthNominal)} />
        <Kpi label="Vermögen Ende" text={formatChf(phase.endWealthNominal)} />
      </div>

      <section>
        <h3 className="mb-1 text-xs font-semibold text-fg">Vermögensaufteilung</h3>
        <p className="mb-2 text-[11px] text-muted">Zusammensetzung des Anlagevermögens zu Beginn und am Ende dieser Phase.</p>
        {allocEls.length === 0 ? (
          <p className="text-sm text-muted">In dieser Phase gibt es kein Anlagevermögen.</p>
        ) : (
          <div className="h-56 w-full">
            <ResponsiveContainer width="100%" height="100%">
              <BarChart data={alloc} margin={{ top: 8, right: 16, left: 8, bottom: 8 }}>
                <CartesianGrid strokeDasharray="3 3" className="stroke-border" />
                <XAxis dataKey="label" tick={{ fontSize: 11 }} />
                <YAxis tick={{ fontSize: 11 }} tickFormatter={(v) => Intl.NumberFormat("de-CH", { notation: "compact" }).format(v)} />
                <Tooltip formatter={(v) => (typeof v === "number" ? formatChf(v) : v)} />
                <Legend wrapperStyle={{ fontSize: 12 }} />
                {allocEls.map((el, i) => (
                  <Bar key={el.elementId} dataKey={el.elementId} name={el.name} stackId="a" fill={ALLOC_PALETTE[i % ALLOC_PALETTE.length]} isAnimationActive={false} />
                ))}
              </BarChart>
            </ResponsiveContainer>
          </div>
        )}
      </section>
    </>
  );

  const wasserfall = (
    <>
      <section>
        <h3 className="mb-1 text-xs font-semibold text-fg">Woher kommt die Vermögensänderung?</h3>
        <p className="mb-2 text-[11px] text-muted">
          Nur <strong>echte</strong> Zu- und Abgänge. Sparraten, Amortisationen und Zusatzinvestitionen erscheinen hier
          bewusst <strong>nicht</strong>: Sie verschieben Geld vom Cash in einen Vermögenswert, ohne das Vermögen zu
          verändern – als Balken gezeichnet würden sie einen Verlust vortäuschen, den es nicht gibt.
        </p>
        <Waterfall items={bridgeItems(phase.wealthBridge, isFirst)} />
        <ResidualNote residual={phase.wealthBridge.residual} />
      </section>

      <section>
        <h3 className="mb-1 text-xs font-semibold text-fg">Wohin ist das Cash geflossen?</h3>
        <p className="mb-2 text-[11px] text-muted">
          Hier erscheinen die Umbuchungen: Sparraten, Amortisationen und Investitionen verlassen das Cash-Konto, auch
          wenn sie das Vermögen nicht mindern.
        </p>
        <Waterfall items={cashItems(phase.cashBridge, isFirst)} />
        <ResidualNote residual={phase.cashBridge.residual} />
      </section>
    </>
  );

  const rechenweg = (
    <>
      {(phase.traces ?? []).length === 0 && <p className="text-sm text-muted">Kein Rechenweg verfügbar.</p>}
      {(phase.traces ?? []).map((t, i) => (
        <TraceBlock key={i} trace={t} onOpenSpec={onOpenSpec} />
      ))}
    </>
  );

  return (
    <DetailShell
      title={phase.name}
      subtitle={`Lebensphase ${phase.sequenceNumber} · ${phase.durationYears} Jahre`}
      onClose={onClose}
      tabs={[
        { key: "übersicht", label: "Übersicht", content: übersicht },
        { key: "wasserfall", label: "Wasserfall", content: wasserfall },
        { key: "rechenweg", label: "Rechenweg", content: rechenweg },
      ]}
    />
  );
}

function Kpi({ label, text }: { label: string; text: string }) {
  return (
    <div className="rounded-lg border border-border bg-surface-2 px-3 py-2">
      <div className="text-[11px] text-muted">{label}</div>
      <div className="text-sm font-semibold text-fg">{text}</div>
    </div>
  );
}

// --- Plan-weite Rechenwege (Deflatoren, AHV-Karriere, Ruinalter) ---------------------------

export function PlanTraceDialog({
  computed,
  onClose,
  onOpenSpec,
}: {
  computed: PlanComputed;
  onClose: () => void;
  onOpenSpec?: (anchor: string) => void;
}) {
  return (
    <DetailShell
      title="Rechenwege dieses Szenarios"
      subtitle="Plan-weite Grössen, die in alle Phasen hineinwirken"
      onClose={onClose}
      tabs={[
        {
          key: "plan",
          label: "Plan-Ebene",
          content: (
            <>
              {(computed.traces ?? []).map((t, i) => (
                <TraceBlock key={i} trace={t} onOpenSpec={onOpenSpec} />
              ))}
            </>
          ),
        },
      ]}
    />
  );
}
