"use client";

import { useEffect, useMemo, useState } from "react";
import {
  AlertCircle,
  Building2,
  CheckCircle2,
  ChevronDown,
  ChevronRight,
  CreditCard,
  Home,
  Landmark,
  PiggyBank,
  Plus,
  Settings2,
  ShoppingCart,
  TrendingUp,
  Wallet,
  X,
} from "lucide-react";
import { Timeline } from "@/components/Timeline";
import {
  CashTransitionFields,
  ElementDetail,
  ElementPhaseFields,
  ElementTransitionFields,
  cashTransitionSummary,
  isCashTransitionAnswered,
  isTransitionAnswered,
  withCashTransitionDefaults,
  withTransitionDefaults,
  type CellContext,
} from "@/components/ElementDetail";
import { PhaseDetail } from "@/components/PhaseDetail";
import { PlanProfileFields, type ProfileDraft } from "@/components/PlanProfileFields";
import { MoneyField } from "@/components/FormField";
import { api } from "@/lib/api-client";
import { formatChf } from "@/lib/format";
import {
  CATEGORY_LABELS,
  CATEGORY_ORDER,
  PERSON_ONLY_CATEGORIES,
  num,
  type CashTransitionData,
  type ElementCategory,
  type PhaseData,
  type TransitionData,
} from "@/lib/elements";
import type { PhaseComputed, PlanComputed } from "@/lib/calculations";
import type { ElementInput, PlanInput } from "@/lib/types";

const CATEGORY_ICON: Record<ElementCategory, React.ReactNode> = {
  INCOME: <Wallet className="h-4 w-4" />,
  EXPENSE: <ShoppingCart className="h-4 w-4" />,
  AHV: <Landmark className="h-4 w-4" />,
  PENSION_FUND: <Building2 className="h-4 w-4" />,
  PILLAR_3A: <PiggyBank className="h-4 w-4" />,
  REAL_ESTATE: <Home className="h-4 w-4" />,
  OTHER_ASSET: <TrendingUp className="h-4 w-4" />,
  OTHER_DEBT: <CreditCard className="h-4 w-4" />,
};

// Kategorien mit einem Uebergangs-Entscheid. AHV ist dabei ein Sonderfall: nur beim
// Pensions-Uebergang ist die Beitragskarriere zu pruefen (siehe transitionInactive).
const TRANSITION_CATEGORIES: ElementCategory[] = [
  "AHV",
  "PENSION_FUND",
  "PILLAR_3A",
  "REAL_ESTATE",
  "OTHER_ASSET",
  "OTHER_DEBT",
];

const VALUE_CATEGORIES: ElementCategory[] = [
  "PENSION_FUND",
  "PILLAR_3A",
  "REAL_ESTATE",
  "OTHER_ASSET",
  "OTHER_DEBT",
];

type Column =
  | { kind: "phase"; phase: PhaseComputed }
  | { kind: "transition"; fromPhase: PhaseComputed; toPhase: PhaseComputed };

type Selection =
  | { type: "phase"; phaseId: string };

export function PlanView({
  plan,
  computed,
  onChanged,
}: {
  plan: PlanInput;
  computed: PlanComputed;
  onChanged: () => void;
}) {
  const [selected, setSelected] = useState<Selection | null>(null);
  const [collapsedCats, setCollapsedCats] = useState<Set<ElementCategory>>(new Set());
  const [showAdd, setShowAdd] = useState(false);
  const [showAddPhase, setShowAddPhase] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [reviewFromPhaseId, setReviewFromPhaseId] = useState<string | null>(null);
  const [editTransition, setEditTransition] = useState<{ elementId: string; fromPhaseId: string } | null>(null);
  const [editPhaseCell, setEditPhaseCell] = useState<{ elementId: string; phaseId: string } | null>(null);
  const [showCashInit, setShowCashInit] = useState(false);
  // fromPhaseId des Cash-Uebergangs, der gerade bearbeitet wird.
  const [editCashTransition, setEditCashTransition] = useState<string | null>(null);
  const [valueMode, setValueMode] = useState<ValueMode>("nominal");

  useEffect(() => {
    const stored = typeof window !== "undefined" ? window.localStorage.getItem(VALUE_MODE_KEY) : null;
    // eslint-disable-next-line react-hooks/set-state-in-effect -- einmalige Uebernahme der gespeicherten Wahl
    if (stored === "nominal" || stored === "both" || stored === "real") setValueMode(stored);
  }, []);
  function changeValueMode(m: ValueMode) {
    setValueMode(m);
    if (typeof window !== "undefined") window.localStorage.setItem(VALUE_MODE_KEY, m);
  }

  const columns = useMemo<Column[]>(() => {
    const cols: Column[] = [];
    computed.phases.forEach((p, i) => {
      cols.push({ kind: "phase", phase: p });
      if (i < computed.phases.length - 1) {
        cols.push({ kind: "transition", fromPhase: p, toPhase: computed.phases[i + 1] });
      }
    });
    return cols;
  }, [computed.phases]);

  // Anzeigename einer Person: eigener Name, sonst Fallback "Person A"/"Person B".
  function personLabel(role: string): string {
    const p = plan.persons.find((x) => x.role === role);
    if (p?.name && p.name.trim()) return p.name.trim();
    return role === "PERSON_A" ? "Person A" : "Person B";
  }

  const personAxes = plan.persons.map((p) => ({
    role: p.role,
    label: personLabel(p.role),
    currentAge: p.age,
    retirementAge: p.retirementAge,
    color: p.role === "PERSON_A" ? "var(--person-a)" : "var(--person-b)",
  }));

  const elementsByCategory = useMemo(() => {
    const map = new Map<ElementCategory, ElementInput[]>();
    for (const cat of CATEGORY_ORDER) map.set(cat, []);
    for (const e of [...plan.elements].sort((a, b) => a.orderIndex - b.orderIndex)) {
      map.get(e.category)!.push(e);
    }
    return map;
  }, [plan.elements]);

  function computedElement(phaseId: string, elementId: string) {
    return computed.phases.find((p) => p.id === phaseId)?.elements.find((e) => e.elementId === elementId);
  }

  function isRetirementTransition(element: ElementInput, fromPhase: PhaseComputed, toPhase: PhaseComputed): boolean {
    if (!element.ownerRole || element.ownerRole === "HOUSEHOLD") return false;
    const before = fromPhase.persons.find((p) => p.role === element.ownerRole);
    const after = toPhase.persons.find((p) => p.role === element.ownerRole);
    return !!before?.working && !!after && !after.working;
  }

  // Beitragskarriere des Element-Besitzers (nur fuer AHV relevant).
  function careerFor(element: ElementInput) {
    if (element.category !== "AHV" || !element.ownerRole || element.ownerRole === "HOUSEHOLD") return null;
    const person = plan.persons.find((p) => p.role === element.ownerRole);
    return computed.ahvCareer.find((c) => c.personId === person?.id) ?? null;
  }

  // Anzahl Phasen NACH dieser (fuer den Fortschreibungs-Warnhinweis).
  function laterPhaseCount(phase: PhaseComputed): number {
    return computed.phases.length - phase.sequenceNumber;
  }

  // Baut den Kontext fuer eine Phasenzelle.
  function buildPhaseContext(phase: PhaseComputed, element: ElementInput): CellContext {
    const ce = computedElement(phase.id, element.id);
    const ownerWorking =
      element.ownerRole && element.ownerRole !== "HOUSEHOLD"
        ? phase.persons.find((p) => p.role === element.ownerRole)?.working ?? false
        : phase.type !== "PENSION";
    return {
      kind: "phase",
      phaseId: phase.id,
      ownerWorking,
      isConsumption: phase.isConsumption,
      durationYears: phase.durationYears,
      isRetirementTransition: false,
      carriedEndValue: ce?.endValue ?? 0,
      carried: ce?.carried ?? false,
      derivedStart: ce?.baseValue ?? 0,
      derivedMortgage: ce?.mortgageStart ?? 0,
      deflatorStart: phase.cumulativeInflationStart,
      laterPhaseCount: laterPhaseCount(phase),
      ahvCareer: careerFor(element),
    };
  }

  function buildTransitionContext(fromPhase: PhaseComputed, toPhase: PhaseComputed | undefined, element: ElementInput): CellContext {
    const ce = computedElement(fromPhase.id, element.id);
    return {
      kind: "transition",
      phaseId: fromPhase.id,
      ownerWorking: true,
      isConsumption: fromPhase.isConsumption,
      durationYears: fromPhase.durationYears,
      isRetirementTransition: toPhase ? isRetirementTransition(element, fromPhase, toPhase) : false,
      carriedEndValue: ce?.endValue ?? 0,
      carried: ce?.carried ?? false,
      derivedStart: 0,
      derivedMortgage: 0,
      deflatorStart: fromPhase.cumulativeInflationStart,
      laterPhaseCount: laterPhaseCount(fromPhase),
      ahvCareer: careerFor(element),
    };
  }

  // Am Uebergang nichts (mehr) zu tun: verkauft/getilgt ODER PK/3a nach der Pensionierung
  // (Besitzer ist zu Beginn der Von-Phase bereits pensioniert -> bereits bezogen/verrentet)
  // ODER AHV ausserhalb des Pensions-Uebergangs.
  function transitionInactive(el: ElementInput, fromPhase: PhaseComputed, toPhase?: PhaseComputed): boolean {
    const ce = computedElement(fromPhase.id, el.id);
    if (ce && ce.status !== "ACTIVE") return true;
    if (el.category === "AHV") {
      return !(toPhase && isRetirementTransition(el, fromPhase, toPhase));
    }
    if (el.category === "PENSION_FUND" || el.category === "PILLAR_3A") {
      if (el.ownerRole && el.ownerRole !== "HOUSEHOLD") {
        const owner = fromPhase.persons.find((p) => p.role === el.ownerRole);
        if (owner && !owner.working) return true;
      }
    }
    return false;
  }

  function cashTransitionFor(phaseId: string): CashTransitionData {
    return plan.phases.find((p) => p.id === phaseId)?.cashTransition ?? {};
  }

  // Anzahl offener (noch nicht getroffener) Uebergangs-Entscheide an einer Grenze.
  // Der Cash-Entscheid (einmalige Sonderein-/ausgaben) zaehlt mit.
  function transitionOpenCount(fromPhase: PhaseComputed, toPhase: PhaseComputed): number {
    let n = isCashTransitionAnswered(cashTransitionFor(fromPhase.id)) ? 0 : 1;
    for (const el of plan.elements) {
      if (!TRANSITION_CATEGORIES.includes(el.category)) continue;
      if (transitionInactive(el, fromPhase, toPhase)) continue;
      const td = el.transitionValues[fromPhase.id] ?? {};
      const retire = toPhase ? isRetirementTransition(el, fromPhase, toPhase) : false;
      if (!isTransitionAnswered(el.category, retire, td)) n++;
    }
    return n;
  }

  // Ist der Uebergangs-Entscheid dieses Elements noch offen?
  function transitionUnanswered(el: ElementInput, fromPhase: PhaseComputed, toPhase: PhaseComputed): boolean {
    if (transitionInactive(el, fromPhase, toPhase)) return false;
    const retire = isRetirementTransition(el, fromPhase, toPhase);
    return !isTransitionAnswered(el.category, retire, el.transitionValues[fromPhase.id] ?? {});
  }

  function transitionElements(fromPhase: PhaseComputed, toPhase?: PhaseComputed): ElementInput[] {
    return plan.elements
      .filter((el) => TRANSITION_CATEGORIES.includes(el.category))
      .filter((el) => !transitionInactive(el, fromPhase, toPhase))
      .sort((a, b) => a.orderIndex - b.orderIndex);
  }

  async function handleAddPhase(payload: { name?: string; durationYears?: number }) {
    await api.post(`/api/plans/${plan.id}/phases`, payload);
    setShowAddPhase(false);
    onChanged();
  }

  const hasPhases = computed.phases.length > 0;
  const firstPhase = computed.phases[0] ?? null;

  return (
    <div className="flex flex-col gap-5">
      <div className="flex items-center gap-2">
        <span className="text-xs text-muted">Anzeige</span>
        <div className="inline-flex rounded-lg border border-border bg-surface p-0.5 text-xs">
          {(["nominal", "both", "real"] as ValueMode[]).map((m) => (
            <button
              key={m}
              type="button"
              onClick={() => changeValueMode(m)}
              className={`rounded-md px-2.5 py-1 font-medium ${
                valueMode === m ? "bg-accent text-accent-fg" : "text-muted hover:bg-surface-2"
              }`}
            >
              {m === "nominal" ? "Nominal" : m === "both" ? "Beide" : "Real"}
            </button>
          ))}
        </div>
        <span className="text-[11px] text-faint">real = kaufkraftbereinigt (Planbeginn)</span>
      </div>

      <Timeline phases={computed.phases} persons={personAxes} ruinAge={computed.ruinAge} />

      {/* Plan-Profil */}
      <div className="flex flex-wrap items-center gap-3 rounded-xl border border-border bg-surface px-4 py-3 text-sm shadow-sm">
        <span className="text-xs font-semibold uppercase tracking-wide text-faint">Grundprofil (Plan)</span>
        {plan.persons.map((p) => (
          <span key={p.role} className="text-xs text-muted">
            {personLabel(p.role)}: {p.age} J., Pension {p.retirementAge}
          </span>
        ))}
        <span className="text-xs text-muted">Inflation {plan.inflationRateDefault}%</span>
        <button
          type="button"
          onClick={() => setShowSettings(true)}
          className="ml-auto flex items-center gap-1.5 rounded-lg border border-border px-2.5 py-1 text-xs font-medium text-muted hover:bg-surface-2"
        >
          <Settings2 className="h-3.5 w-3.5" /> Einstellungen
        </button>
      </div>

      {!hasPhases && (
        <div className="rounded-xl border border-dashed border-border bg-surface p-8 text-center">
          <p className="text-sm text-muted">Dieser Plan hat noch keine Lebensphasen.</p>
          <button
            type="button"
            onClick={() => setShowAddPhase(true)}
            className="mt-3 inline-flex items-center gap-1.5 rounded-lg bg-accent px-4 py-2 text-sm font-medium text-accent-fg hover:bg-accent-hover"
          >
            <Plus className="h-4 w-4" /> Erste Lebensphase
          </button>
        </div>
      )}

      {hasPhases && (
        <div className="flex flex-wrap items-center gap-2">
          <button
            type="button"
            onClick={() => setShowAdd(true)}
            className="flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-sm font-medium text-accent-fg shadow-sm hover:bg-accent-hover"
          >
            <Plus className="h-4 w-4" /> Finanzielles Element
          </button>
          <button
            type="button"
            onClick={() => setShowAddPhase(true)}
            className="flex items-center gap-1.5 rounded-lg border border-dashed border-accent bg-accent-soft px-3 py-1.5 text-sm font-medium text-accent-soft-fg hover:bg-accent-soft"
          >
            <Plus className="h-4 w-4" /> Lebensphase
          </button>
        </div>
      )}

      {computed.ruinAge !== null && (
        <div className="flex items-center gap-2 rounded-xl border border-danger bg-danger-soft px-4 py-2 text-sm font-medium text-danger">
          <AlertCircle className="h-4 w-4 shrink-0" /> Kapital aufgebraucht mit Alter {computed.ruinAge} – das Gesamtvermoegen (inkl. Cash) faellt danach unter 0.
        </div>
      )}

      {/* Matrix */}
      {hasPhases && (
        <div className="overflow-x-auto rounded-xl border border-border bg-surface shadow-sm">
          <table className="w-full border-collapse text-sm">
            <thead>
              <tr>
                <th className="sticky left-0 z-20 min-w-44 border-b border-r border-border bg-surface-2 px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-faint">
                  Finanzielle Elemente
                </th>
                {columns.map((col) =>
                  col.kind === "phase" ? (
                    <PhaseHeader
                      key={col.phase.id}
                      phase={col.phase}
                      personLabel={personLabel}
                      mode={valueMode}
                      onClick={() => setSelected({ type: "phase", phaseId: col.phase.id })}
                      active={selected?.type === "phase" && selected.phaseId === col.phase.id}
                    />
                  ) : (
                    <TransitionHeader
                      key={`t-${col.fromPhase.id}`}
                      openCount={transitionOpenCount(col.fromPhase, col.toPhase)}
                      onClick={() => setReviewFromPhaseId(col.fromPhase.id)}
                    />
                  )
                )}
              </tr>
            </thead>
            <tbody>
              {/* Cash-Zeile (systemseitig, immer sichtbar, read-only). */}
              <tr className="bg-surface-2/50">
                <td className="sticky left-0 z-10 border-b border-r border-border bg-surface px-3 py-1.5">
                  <span className="flex items-center gap-1.5 text-xs font-semibold text-fg">
                    <Wallet className="h-4 w-4 text-accent" /> Cash
                  </span>
                  <span className="text-[10px] text-faint">verfuegbares Kapital</span>
                </td>
                {columns.map((col) => {
                  const isFirst = col.kind === "phase" && col.phase.sequenceNumber === 1;
                  return col.kind === "phase" ? (
                    <td
                      key={`cash-${col.phase.id}`}
                      onClick={isFirst ? () => setShowCashInit(true) : undefined}
                      title={isFirst ? "Cash-Anfangswert bearbeiten" : undefined}
                      className={`border-b border-r border-border px-2 py-1.5 text-center text-xs ${
                        isFirst ? "cursor-pointer hover:bg-accent-soft" : ""
                      } ${col.phase.cashNegative ? "font-semibold text-danger" : "text-fg"}`}
                    >
                      <span className="whitespace-nowrap">
                        {valStr(col.phase.cashStart, col.phase.cumulativeInflationStart, valueMode)}{" "}
                        <span className="text-faint">→</span>{" "}
                        {valStr(col.phase.cashEnd, col.phase.cumulativeInflationEnd, valueMode)}
                      </span>
                    </td>
                  ) : (
                    (() => {
                      // Uebergangszelle der Cash-Zeile: einmalige Sonderein-/ausgaben.
                      const ct = cashTransitionFor(col.fromPhase.id);
                      const open = !isCashTransitionAnswered(ct);
                      return (
                        <td
                          key={`cash-t-${col.fromPhase.id}`}
                          onClick={() => setEditCashTransition(col.fromPhase.id)}
                          title="Einmalige Sonderein-/ausgaben"
                          className={`cursor-pointer border-b border-r border-border px-2 py-1.5 text-center text-[11px] ${
                            open ? "bg-accent font-semibold text-accent-fg" : "bg-accent-soft/40 text-accent"
                          }`}
                        >
                          {cashTransitionSummary(ct)}
                        </td>
                      );
                    })()
                  );
                })}
              </tr>
              {CATEGORY_ORDER.map((cat) => {
                const els = elementsByCategory.get(cat)!;
                if (els.length === 0) return null;
                const collapsed = collapsedCats.has(cat);
                return (
                  <FragmentRows key={cat}>
                    <tr className="bg-surface-2">
                      <td
                        className="sticky left-0 z-10 cursor-pointer border-b border-r border-border bg-surface-2 px-3 py-1.5"
                        onClick={() =>
                          setCollapsedCats((prev) => {
                            const next = new Set(prev);
                            if (next.has(cat)) next.delete(cat);
                            else next.add(cat);
                            return next;
                          })
                        }
                      >
                        <span className="flex items-center gap-1.5 text-xs font-semibold text-muted">
                          {collapsed ? <ChevronRight className="h-3.5 w-3.5" /> : <ChevronDown className="h-3.5 w-3.5" />}
                          <span className="text-accent">{CATEGORY_ICON[cat]}</span>
                          {CATEGORY_LABELS[cat]}
                        </span>
                      </td>
                      <td colSpan={columns.length} className="border-b border-border" />
                    </tr>
                    {!collapsed &&
                      els.map((el) => (
                        <tr key={el.id} className="hover:bg-surface-2">
                          <td className="sticky left-0 z-10 border-b border-r border-border bg-surface px-3 py-1.5">
                            <div className="truncate text-xs font-medium text-fg">{el.name}</div>
                            {el.ownerRole && el.ownerRole !== "HOUSEHOLD" && (
                              <div className="text-[10px] text-faint">{personLabel(el.ownerRole)}</div>
                            )}
                          </td>
                          {columns.map((col) => {
                            if (col.kind === "phase") {
                              const ce = computedElement(col.phase.id, el.id);
                              return (
                                <td
                                  key={col.phase.id}
                                  onClick={() => setEditPhaseCell({ elementId: el.id, phaseId: col.phase.id })}
                                  className={`cursor-pointer border-b border-r border-border px-2 py-1.5 text-center text-xs ${
                                    ce?.locked ? "text-faint" : "text-fg"
                                  }`}
                                >
                                  {phaseCellContent(ce, col.phase, valueMode)}
                                </td>
                              );
                            }
                            const locked = TRANSITION_CATEGORIES.includes(el.category) && transitionInactive(el, col.fromPhase, col.toPhase);
                            const canTransition = TRANSITION_CATEGORIES.includes(el.category) && !locked;
                            const open = canTransition && transitionUnanswered(el, col.fromPhase, col.toPhase);
                            return (
                              <td
                                key={`t-${col.fromPhase.id}`}
                                onClick={() =>
                                  canTransition &&
                                  setEditTransition({ elementId: el.id, fromPhaseId: col.fromPhase.id })
                                }
                                className={`border-b border-r border-border px-2 py-1.5 text-center text-[11px] ${
                                  canTransition ? "cursor-pointer" : "text-faint"
                                } ${open ? "bg-accent font-semibold text-accent-fg" : canTransition ? "bg-accent-soft/40 text-accent" : ""}`}
                              >
                                {canTransition ? transitionSummary(el, col.fromPhase, col.toPhase) : locked ? "–" : "→"}
                              </td>
                            );
                          })}
                        </tr>
                      ))}
                  </FragmentRows>
                );
              })}
              {plan.elements.length === 0 && (
                <tr>
                  <td className="sticky left-0 bg-surface px-3 py-4 text-xs text-faint" colSpan={columns.length + 1}>
                    Noch keine finanziellen Elemente. Fuegen Sie oben Ihr erstes Element hinzu.
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      )}

      {/* Detail-Panel. Der key erzwingt beim Wechsel von Zelle/Phase einen Neuaufbau,
          damit der lokale Formular-Zustand nicht vom vorher geoeffneten Element uebrig bleibt. */}
      {selected && (
        <div key={`ph-${selected.phaseId}`} className="rounded-xl border border-accent bg-surface p-4 shadow-sm">
          {renderDetail()}
        </div>
      )}

      {showAdd && firstPhase && (
        <AddElementDialog
          plan={plan}
          firstPhase={firstPhase}
          onClose={() => setShowAdd(false)}
          onCreated={() => {
            setShowAdd(false);
            onChanged();
          }}
        />
      )}

      {showAddPhase && (
        <AddPhaseDialog
          maxDurationYears={nextPhaseCap()}
          onClose={() => setShowAddPhase(false)}
          onCreate={handleAddPhase}
        />
      )}

      {showSettings && (
        <PlanSettingsDialog
          plan={plan}
          onClose={() => setShowSettings(false)}
          onSaved={() => {
            setShowSettings(false);
            onChanged();
          }}
        />
      )}

      {showCashInit && (
        <CashInitialDialog
          plan={plan}
          onClose={() => setShowCashInit(false)}
          onSaved={() => {
            setShowCashInit(false);
            onChanged();
          }}
        />
      )}

      {editCashTransition && (() => {
        const fromPhase = computed.phases.find((p) => p.id === editCashTransition);
        if (!fromPhase) return null;
        const toIndex = computed.phases.findIndex((p) => p.id === fromPhase.id) + 1;
        const toPhase = computed.phases[toIndex];
        return (
          <CashTransitionDialog
            key={`ct-${editCashTransition}`}
            fromPhase={fromPhase}
            toPhase={toPhase}
            initial={cashTransitionFor(fromPhase.id)}
            onClose={() => setEditCashTransition(null)}
            onSaved={() => {
              setEditCashTransition(null);
              onChanged();
            }}
          />
        );
      })()}

      {reviewFromPhaseId && (() => {
        const fromPhase = computed.phases.find((p) => p.id === reviewFromPhaseId);
        if (!fromPhase) return null;
        const toIndex = computed.phases.findIndex((p) => p.id === fromPhase.id) + 1;
        const toPhase = computed.phases[toIndex];
        const els = transitionElements(fromPhase, toPhase);
        return (
          <TransitionReviewDialog
            fromPhase={fromPhase}
            toPhase={toPhase}
            elements={els}
            initialCash={cashTransitionFor(fromPhase.id)}
            buildContext={(el) => buildTransitionContext(fromPhase, toPhase, el)}
            isRetirement={(el) => (toPhase ? isRetirementTransition(el, fromPhase, toPhase) : false)}
            onClose={() => setReviewFromPhaseId(null)}
            onSaved={() => {
              setReviewFromPhaseId(null);
              onChanged();
            }}
          />
        );
      })()}

      {editTransition && (() => {
        const element = plan.elements.find((e) => e.id === editTransition.elementId);
        const fromPhase = computed.phases.find((p) => p.id === editTransition.fromPhaseId);
        if (!element || !fromPhase) return null;
        const toIndex = computed.phases.findIndex((p) => p.id === fromPhase.id) + 1;
        const toPhase = computed.phases[toIndex];
        const context = buildTransitionContext(fromPhase, toPhase, element);
        return (
          <TransitionCellDialog
            key={`${editTransition.elementId}-${editTransition.fromPhaseId}`}
            element={element}
            fromPhase={fromPhase}
            toPhase={toPhase}
            context={context}
            onClose={() => setEditTransition(null)}
            onSaved={() => {
              setEditTransition(null);
              onChanged();
            }}
          />
        );
      })()}

      {editPhaseCell && (() => {
        const element = plan.elements.find((e) => e.id === editPhaseCell.elementId);
        const phase = computed.phases.find((p) => p.id === editPhaseCell.phaseId);
        if (!element || !phase) return null;
        const context = buildPhaseContext(phase, element);
        return (
          <PhaseCellDialog
            key={`${editPhaseCell.elementId}-${editPhaseCell.phaseId}`}
            element={element}
            phase={phase}
            context={context}
            phaseData={element.phaseValues[phase.id] ?? {}}
            onClose={() => setEditPhaseCell(null)}
            onSaved={() => {
              setEditPhaseCell(null);
              onChanged();
            }}
            onDeleted={() => {
              setEditPhaseCell(null);
              deleteElement(element.id);
            }}
          />
        );
      })()}
    </div>
  );

  // Naechste Phasen-Kappung (fuer das Phase-Popup).
  function nextPhaseCap(): number | null {
    // Simpel aus den Personen ableiten (Jahre nach Planbeginn = Summe der Dauern).
    const yearsBefore = plan.phases.reduce((s, p) => s + p.durationYears, 0);
    const caps = plan.persons
      .map((p) => p.retirementAge - (p.age + yearsBefore))
      .filter((d) => d > 0);
    return caps.length > 0 ? Math.min(...caps) : null;
  }

  function renderDetail() {
    if (!selected) return null;
    const phase = computed.phases.find((p) => p.id === selected.phaseId);
    const phaseInput = plan.phases.find((p) => p.id === selected.phaseId);
    if (!phase || !phaseInput) return null;
    const isLast = phase.sequenceNumber === computed.phases.length;
    return (
      <PhaseDetail
        phase={phaseInput}
        maxDurationYears={phase.maxDurationYears}
        isLast={isLast}
        laterPhaseCount={laterPhaseCount(phase)}
        onSaved={onChanged}
        onDeleted={() => {
          setSelected(null);
          onChanged();
        }}
      />
    );
  }

  async function deleteElement(id: string) {
    if (!confirm("Dieses Element wirklich loeschen (aus allen Phasen)?")) return;
    await api.delete(`/api/elements/${id}`);
    setSelected(null);
    onChanged();
  }

  function transitionSummary(el: ElementInput, fromPhase: PhaseComputed, toPhase: PhaseComputed): string {
    const td = el.transitionValues[fromPhase.id] ?? {};
    switch (el.category) {
      case "AHV":
        return td.reviewed === true ? "Geprueft" : "?";
      case "REAL_ESTATE":
      case "OTHER_ASSET":
        return td.decision === "SELL" ? "Verkauf" : td.decision === "HOLD" ? "Halten" : "?";
      case "PENSION_FUND":
        if (isRetirementTransition(el, fromPhase, toPhase)) {
          return td.payoutMode === "CAPITAL" ? "Kapital" : td.payoutMode === "COMBI" ? "Kombi" : td.payoutMode === "PENSION" ? "Rente" : "?";
        }
        if (td.withdrawalMode === undefined) return "?";
        return td.withdrawalMode === "AMOUNT" && num(td.withdrawal) > 0 ? `−${formatChf(num(td.withdrawal))}` : "Kein Bezug";
      case "PILLAR_3A":
        if (isRetirementTransition(el, fromPhase, toPhase)) return "Bezug";
        if (td.withdrawalMode === undefined) return "?";
        return td.withdrawalMode === "AMOUNT" && num(td.withdrawal) > 0 ? `−${formatChf(num(td.withdrawal))}` : "Kein Bezug";
      case "OTHER_DEBT":
        return num(td.immediateRepayment) > 0 ? "Tilgung" : "→";
      default:
        return "→";
    }
  }
}

// Zellinhalt: Start- UND Zielwert fuer wertbehaftete Elemente, sonst die Kennzahl.
const START_END_CATEGORIES: ElementCategory[] = [...VALUE_CATEGORIES, "INCOME", "EXPENSE"];

type ValueMode = "nominal" | "both" | "real";
const VALUE_MODE_KEY = "fpt-value-mode";

// Realwert = nominal / kumulierte Inflation (Kaufkraft zum Planbeginn).
function realOf(nominal: number, deflator: number): number {
  return Math.round(nominal / (deflator || 1));
}
function valStr(nominal: number, deflator: number, mode: ValueMode): string {
  if (mode === "real") return formatChf(realOf(nominal, deflator));
  if (mode === "both") return `${formatChf(nominal)} (${formatChf(realOf(nominal, deflator))})`;
  return formatChf(nominal);
}

function phaseCellContent(
  ce: ReturnType<PhaseComputed["elements"]["find"]> | undefined,
  phase: PhaseComputed,
  mode: ValueMode
): React.ReactNode {
  if (!ce) return "–";
  if (ce.note) return ce.note;
  const isFlow = ce.category === "INCOME" || ce.category === "EXPENSE";
  const dS = phase.cumulativeInflationStart;
  const dE = isFlow ? phase.flowDeflatorEnd : phase.cumulativeInflationEnd;
  if (START_END_CATEGORIES.includes(ce.category) && (ce.startValue !== 0 || ce.endValue !== 0)) {
    return (
      <span className="whitespace-nowrap">
        {valStr(ce.startValue, dS, mode)} <span className="text-faint">→</span> {valStr(ce.endValue, dE, mode)}
      </span>
    );
  }
  if ((ce.category === "AHV" || ce.category === "PENSION_FUND") && ce.startValue !== 0) {
    return `Rente ${valStr(ce.startValue, dS, mode)}`;
  }
  return ce.summary || "–";
}

function PhaseHeader({
  phase,
  personLabel,
  mode,
  onClick,
  active,
}: {
  phase: PhaseComputed;
  personLabel: (role: string) => string;
  mode: ValueMode;
  onClick: () => void;
  active: boolean;
}) {
  const quotaLabel = phase.isConsumption ? "Verzehr" : "Quote";
  const dS = phase.cumulativeInflationStart;
  const dE = phase.cumulativeInflationEnd; // Bestandswerte (Cash, Vermoegen)
  const dF = phase.flowDeflatorEnd; // Flow-Werte (Einkommen, Ausgaben, Quote)
  return (
    <th
      onClick={onClick}
      className={`min-w-44 cursor-pointer border-b border-r border-border px-2 py-2 text-left align-top ${
        active ? "bg-accent-soft" : "bg-surface"
      }`}
    >
      <div className="flex items-center gap-1">
        <span className="truncate text-xs font-semibold text-fg">{phase.name}</span>
        {phase.cashNegative ? (
          <AlertCircle className="h-3.5 w-3.5 shrink-0 text-danger" />
        ) : (
          <CheckCircle2 className="h-3.5 w-3.5 shrink-0 text-success" />
        )}
      </div>
      <div className="mt-0.5 flex flex-wrap gap-1">
        <span className="rounded bg-surface-2 px-1 text-[10px] text-muted">
          {phase.type === "ERWERB" ? "Erwerb" : phase.type === "PENSION" ? "Pension" : "Misch"}
        </span>
        <span className="text-[10px] text-faint">{phase.durationYears} J.</span>
      </div>
      <div className="mt-1 space-y-0.5 text-[10px] leading-tight text-muted">
        {phase.persons.map((p) => (
          <div key={p.role} className="text-faint">
            {personLabel(p.role)} {p.startAge} → {p.endAge}
          </div>
        ))}
        <div>Einkommen {valStr(phase.incomeStart, dS, mode)} → {valStr(phase.incomeEnd, dF, mode)}</div>
        <div>Ausgaben {valStr(phase.expenseStart, dS, mode)} → {valStr(phase.expenseEnd, dF, mode)}</div>
        <div>
          {quotaLabel} {valStr(phase.quotaStart, dS, mode)} → {valStr(phase.quotaEnd, dF, mode)}
        </div>
        <div className="my-1 border-t border-border" />
        <div>Geplante Sparrate {valStr(phase.plannedSaveRate, dS, mode)}</div>
        <div>Geplante Verzehrrate {valStr(phase.plannedWithdrawRate, dS, mode)}</div>

        {(phase.capitalInflow > 0 || phase.capitalInvest > 0) && (
          <>
            <div className="my-1 border-t border-border" />
            {phase.capitalInflow > 0 && <div>Kapitalzufluss {valStr(phase.capitalInflow, dS, mode)}</div>}
            {phase.capitalInvest > 0 && <div>Kapitalinvestitionen {valStr(phase.capitalInvest, dS, mode)}</div>}
          </>
        )}

        {(phase.oneOffInflow > 0 || phase.oneOffOutflow > 0) && (
          <>
            <div className="my-1 border-t border-border" />
            {phase.oneOffInflow > 0 && (
              <div className="text-success">
                + {phase.oneOffInflowLabel ?? "Einmaliger Zufluss"} {valStr(phase.oneOffInflow, dS, mode)}
              </div>
            )}
            {phase.oneOffOutflow > 0 && (
              <div className="text-danger">
                − {phase.oneOffOutflowLabel ?? "Einmalige Kosten"} {valStr(phase.oneOffOutflow, dS, mode)}
              </div>
            )}
          </>
        )}

        <div className="my-1 border-t border-border" />
        <div>
          Vermoegen {valStr(phase.startWealthNominal, dS, mode)} → {valStr(phase.endWealthNominal, dE, mode)}
        </div>
      </div>
    </th>
  );
}

function TransitionHeader({ openCount, onClick }: { openCount: number; onClick: () => void }) {
  const done = openCount === 0;
  return (
    <th
      onClick={onClick}
      className={`cursor-pointer border-b border-r border-border px-2 py-2 text-center align-top text-[11px] font-medium ${
        done ? "bg-success/10 text-success" : "bg-accent text-accent-fg"
      }`}
    >
      <div>Uebergang</div>
      {done ? (
        <div className="mt-1 flex items-center justify-center gap-1 text-[10px] font-semibold">
          <CheckCircle2 className="h-3 w-3" /> geprueft
        </div>
      ) : (
        <div className="mt-1 rounded-full bg-accent-fg/20 px-1.5 py-0.5 text-[10px] font-semibold">
          {openCount} offen
        </div>
      )}
    </th>
  );
}

function FragmentRows({ children }: { children: React.ReactNode }) {
  return <>{children}</>;
}

// --- Dialog: neues finanzielles Element mit Direkteingabe der Phase-1-Werte ---
function AddElementDialog({
  plan,
  firstPhase,
  onClose,
  onCreated,
}: {
  plan: PlanInput;
  firstPhase: PhaseComputed;
  onClose: () => void;
  onCreated: () => void;
}) {
  const [category, setCategory] = useState<ElementCategory>("INCOME");
  const [name, setName] = useState("");
  const [ownerRole, setOwnerRole] = useState<string>("PERSON_A");
  const [pd, setPd] = useState<PhaseData>({});
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const needsPerson = PERSON_ONLY_CATEGORIES.includes(category);
  const isCouple = plan.householdType === "COUPLE";
  const pLabel = (role: string) => {
    const p = plan.persons.find((x) => x.role === role);
    return p?.name && p.name.trim() ? p.name.trim() : role === "PERSON_A" ? "Person A" : "Person B";
  };

  const ownerOptions = needsPerson
    ? isCouple
      ? [
          { value: "PERSON_A", label: pLabel("PERSON_A") },
          { value: "PERSON_B", label: pLabel("PERSON_B") },
        ]
      : [{ value: "PERSON_A", label: pLabel("PERSON_A") }]
    : isCouple
      ? [
          { value: "HOUSEHOLD", label: "Gemeinsam" },
          { value: "PERSON_A", label: pLabel("PERSON_A") },
          { value: "PERSON_B", label: pLabel("PERSON_B") },
        ]
      : [
          { value: "HOUSEHOLD", label: "Gemeinsam" },
          { value: "PERSON_A", label: pLabel("PERSON_A") },
        ];

  // Kontext fuer die Phase-1-Felder des neuen Elements.
  const owner = needsPerson || ownerRole !== "HOUSEHOLD" ? ownerRole : null;
  const ownerWorking =
    owner && owner !== "HOUSEHOLD"
      ? firstPhase.persons.find((p) => p.role === owner)?.working ?? false
      : firstPhase.type !== "PENSION";
  const context: CellContext = {
    kind: "phase",
    phaseId: firstPhase.id,
    ownerWorking,
    isConsumption: firstPhase.isConsumption,
    durationYears: firstPhase.durationYears,
    isRetirementTransition: false,
    carriedEndValue: 0,
    carried: false,
    derivedStart: 0,
    derivedMortgage: 0,
    deflatorStart: firstPhase.cumulativeInflationStart,
    laterPhaseCount: 0, // beim Anlegen bewusst kein Warnhinweis
    ahvCareer: null,
  };

  async function create() {
    setSaving(true);
    setError(null);
    try {
      const { element } = await api.post<{ element: { id: string } }>(`/api/plans/${plan.id}/elements`, {
        category,
        name: name.trim() || CATEGORY_LABELS[category],
        ownerRole,
      });
      // Ist-Zustand direkt in Phase 1 speichern (sofern Felder ausgefuellt).
      if (Object.keys(pd).length > 0) {
        await api.put(`/api/elements/${element.id}/phase/${firstPhase.id}`, pd);
      }
      onCreated();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Erstellen fehlgeschlagen.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <DialogShell title="Finanzielles Element" onClose={onClose} wide>
      <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
        <div>
          <label className="mb-1 block text-xs font-medium text-muted">Kategorie</label>
          <select
            value={category}
            onChange={(e) => {
              const c = e.target.value as ElementCategory;
              setCategory(c);
              setPd({});
              if (PERSON_ONLY_CATEGORIES.includes(c) && ownerRole === "HOUSEHOLD") setOwnerRole("PERSON_A");
              if (!name) setName(CATEGORY_LABELS[c]);
            }}
            className="w-full rounded-lg border border-border bg-input px-3 py-2 text-sm text-fg focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/25"
          >
            {CATEGORY_ORDER.map((c) => (
              <option key={c} value={c}>
                {CATEGORY_LABELS[c]}
              </option>
            ))}
          </select>
        </div>
        <div>
          <label className="mb-1 block text-xs font-medium text-muted">Zuordnung</label>
          <select
            value={ownerRole}
            onChange={(e) => setOwnerRole(e.target.value)}
            className="w-full rounded-lg border border-border bg-input px-3 py-2 text-sm text-fg focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/25"
          >
            {ownerOptions.map((o) => (
              <option key={o.value} value={o.value}>
                {o.label}
              </option>
            ))}
          </select>
        </div>
        <div className="sm:col-span-2">
          <label className="mb-1 block text-xs font-medium text-muted">Bezeichnung</label>
          <input
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder={CATEGORY_LABELS[category]}
            className="w-full rounded-lg border border-border bg-input px-3 py-2 text-sm text-fg focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/25"
          />
        </div>
      </div>

      <div className="mt-1 border-t border-border pt-3">
        <div className="mb-2 text-xs font-semibold uppercase tracking-wide text-faint">Werte (erste Lebensphase)</div>
        <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
          <ElementPhaseFields
            element={{ category }}
            context={context}
            pd={pd}
            setP={(patch) => setPd((prev) => ({ ...prev, ...patch }))}
          />
        </div>
      </div>

      {error && <p className="text-sm text-danger">{error}</p>}
      <DialogActions saving={saving} onConfirm={create} onClose={onClose} confirmLabel="Erstellen" />
    </DialogShell>
  );
}

// --- Dialog: neue Lebensphase ---
function AddPhaseDialog({
  maxDurationYears,
  onClose,
  onCreate,
}: {
  maxDurationYears: number | null;
  onClose: () => void;
  onCreate: (payload: { name?: string; durationYears?: number }) => void;
}) {
  const cap = maxDurationYears;
  const [name, setName] = useState("");
  const [durationYears, setDurationYears] = useState(cap ?? 10);
  const [saving, setSaving] = useState(false);

  return (
    <DialogShell title="Neue Lebensphase" onClose={onClose}>
      <div className="grid grid-cols-1 gap-3">
        <div>
          <label className="mb-1 block text-xs font-medium text-muted">Bezeichnung (optional)</label>
          <input
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="automatisch (Erwerb/Pension)"
            className="w-full rounded-lg border border-border bg-input px-3 py-2 text-sm text-fg focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/25"
          />
        </div>
        <div>
          <label className="mb-1 block text-xs font-medium text-muted">
            Dauer (Jahre){cap != null ? ` · max. ${cap}` : ""}
          </label>
          <input
            type="number"
            value={durationYears}
            min={1}
            max={cap ?? undefined}
            onFocus={(e) => e.currentTarget.select()}
            onChange={(e) => {
              const v = e.target.valueAsNumber || 1;
              setDurationYears(cap != null ? Math.min(v, cap) : v);
            }}
            className="w-full rounded-lg border border-border bg-input px-3 py-2 text-sm text-fg focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/25"
          />
        </div>
      </div>
      <DialogActions
        saving={saving}
        onConfirm={() => {
          setSaving(true);
          onCreate({ name: name.trim() || undefined, durationYears });
        }}
        onClose={onClose}
        confirmLabel="Erstellen"
      />
    </DialogShell>
  );
}

// --- Dialog: Plan-Einstellungen (Grundprofil bearbeiten) ---
function PlanSettingsDialog({ plan, onClose, onSaved }: { plan: PlanInput; onClose: () => void; onSaved: () => void }) {
  const [draft, setDraft] = useState<ProfileDraft>({
    householdType: plan.householdType,
    inflationRateDefault: plan.inflationRateDefault,
    persons: plan.persons.map((p) => ({ role: p.role, name: p.name ?? "", age: p.age, retirementAge: p.retirementAge })),
  });
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function save() {
    setSaving(true);
    setError(null);
    try {
      await api.patch(`/api/plans/${plan.id}`, draft);
      onSaved();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Speichern fehlgeschlagen.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <DialogShell title="Plan-Einstellungen" onClose={onClose}>
      <PlanProfileFields draft={draft} onChange={setDraft} />
      {error && <p className="text-sm text-danger">{error}</p>}
      <DialogActions saving={saving} onConfirm={save} onClose={onClose} confirmLabel="Speichern" />
    </DialogShell>
  );
}

// --- Dialog: geführter Übergang ---
function TransitionReviewDialog({
  fromPhase,
  toPhase,
  elements,
  initialCash,
  buildContext,
  isRetirement,
  onClose,
  onSaved,
}: {
  fromPhase: PhaseComputed;
  toPhase: PhaseComputed | undefined;
  elements: ElementInput[];
  initialCash: CashTransitionData;
  buildContext: (el: ElementInput) => CellContext;
  isRetirement: (el: ElementInput) => boolean;
  onClose: () => void;
  onSaved: () => void;
}) {
  const [tds, setTds] = useState<Record<string, TransitionData>>(() =>
    Object.fromEntries(
      elements.map((e) => [e.id, withTransitionDefaults(e.category, isRetirement(e), e.transitionValues[fromPhase.id] ?? {})])
    )
  );
  const [ct, setCt] = useState<CashTransitionData>(() => withCashTransitionDefaults(initialCash));
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function saveAll() {
    setSaving(true);
    setError(null);
    try {
      await api.put(`/api/phases/${fromPhase.id}/cash-transition`, ct);
      for (const e of elements) {
        await api.put(`/api/elements/${e.id}/transition/${fromPhase.id}`, tds[e.id] ?? {});
      }
      onSaved();
    } catch (err) {
      setError(err instanceof Error ? err.message : "Speichern fehlgeschlagen.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <DialogShell title={`Übergang prüfen: ${fromPhase.name} → ${toPhase?.name ?? "Ende"}`} onClose={onClose} wide>
      <p className="text-sm text-muted">
        Gehen Sie die Positionen durch und treffen Sie je Element den Übergangs-Entscheid (Halten, Verkaufen,
        Bezug). Danach werden gehaltene Werte automatisch in die nächste Phase fortgeschrieben.
      </p>
      <div className="flex flex-col gap-3">
        {/* Cash zuerst: einmalige Sonderein-/ausgaben betreffen jeden Uebergang. */}
        <div className="rounded-xl border border-border bg-surface-2 p-3">
          <div className="mb-2 flex items-center gap-2">
            <span className="text-accent">
              <Wallet className="h-4 w-4" />
            </span>
            <span className="text-sm font-semibold text-fg">Cash</span>
            <span className="text-xs text-faint">Einmalige Sonderein-/ausgaben</span>
          </div>
          <p className="mb-2 text-xs text-accent-soft-fg">
            Einmalige Ereignisse wie Erbschaft, Autokauf oder Poolbau werden hier direkt dem Cash-Konto
            gutgeschrieben bzw. belastet.
          </p>
          <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
            <CashTransitionFields
              ct={ct}
              setC={(patch) => setCt((prev) => ({ ...prev, ...patch }))}
              deflatorEnd={fromPhase.cumulativeInflationEnd}
            />
          </div>
        </div>

        {elements.length === 0 && (
          <p className="rounded-lg border border-dashed border-border bg-surface-2 p-3 text-sm text-muted">
            An diesem Übergang gibt es sonst keine zu entscheidenden Positionen.
          </p>
        )}
        {elements.map((el) => {
          const ctx = buildContext(el);
          const retire = isRetirement(el);
          const hint =
            el.category === "AHV" && retire
              ? "Pensionierung: Beitragskarriere prüfen – die Rente hängt vom Durchschnittseinkommen ab."
              : (el.category === "PENSION_FUND" || el.category === "PILLAR_3A") && !retire
                ? "Hier könnten Sie optional Kapital beziehen."
                : el.category === "PENSION_FUND" && retire
                  ? "Pensionierung: Bezugsart wählen (Rente / Kapital / Kombination)."
                  : el.category === "PILLAR_3A" && retire
                    ? "Wird bei Pensionierung vollständig bezogen."
                    : null;
          return (
            <div key={el.id} className="rounded-xl border border-border bg-surface-2 p-3">
              <div className="mb-2 flex items-center gap-2">
                <span className="text-accent">{CATEGORY_ICON[el.category]}</span>
                <span className="text-sm font-semibold text-fg">{el.name}</span>
                <span className="text-xs text-faint">{CATEGORY_LABELS[el.category]}</span>
              </div>
              {hint && <p className="mb-2 text-xs text-accent-soft-fg">{hint}</p>}
              <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
                <ElementTransitionFields
                  element={el}
                  context={ctx}
                  td={tds[el.id] ?? {}}
                  setT={(patch) => setTds((prev) => ({ ...prev, [el.id]: { ...prev[el.id], ...patch } }))}
                />
              </div>
            </div>
          );
        })}
      </div>
      {error && <p className="text-sm text-danger">{error}</p>}
      <DialogActions saving={saving} onConfirm={saveAll} onClose={onClose} confirmLabel="Alle speichern" />
    </DialogShell>
  );
}

// --- Dialog: Cash-Uebergang (einmalige Sonderein-/ausgaben) ---
function CashTransitionDialog({
  fromPhase,
  toPhase,
  initial,
  onClose,
  onSaved,
}: {
  fromPhase: PhaseComputed;
  toPhase: PhaseComputed | undefined;
  initial: CashTransitionData;
  onClose: () => void;
  onSaved: () => void;
}) {
  // Vorbelegung, damit ein blosses "Speichern" den sichtbaren Default (1:1) persistiert.
  const [ct, setCt] = useState<CashTransitionData>(() => withCashTransitionDefaults(initial));
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function save() {
    setSaving(true);
    setError(null);
    try {
      await api.put(`/api/phases/${fromPhase.id}/cash-transition`, ct);
      onSaved();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Speichern fehlgeschlagen.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <DialogShell title="Uebergang: Cash" onClose={onClose} wide>
      <div className="text-xs text-muted">
        Einmalige Sonderein-/ausgaben · {fromPhase.name} → {toPhase?.name ?? "Ende"}
      </div>
      <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
        <CashTransitionFields
          ct={ct}
          setC={(patch) => setCt((prev) => ({ ...prev, ...patch }))}
          deflatorEnd={fromPhase.cumulativeInflationEnd}
        />
      </div>
      {error && <p className="text-sm text-danger">{error}</p>}
      <DialogActions saving={saving} onConfirm={save} onClose={onClose} confirmLabel="Speichern" />
    </DialogShell>
  );
}

// --- Dialog: Cash-Anfangswert (erste Lebensphase) ---
function CashInitialDialog({ plan, onClose, onSaved }: { plan: PlanInput; onClose: () => void; onSaved: () => void }) {
  const [value, setValue] = useState(plan.initialCash);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function save() {
    setSaving(true);
    setError(null);
    try {
      await api.patch(`/api/plans/${plan.id}`, { initialCash: value });
      onSaved();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Speichern fehlgeschlagen.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <DialogShell title="Cash-Anfangswert" onClose={onClose}>
      <p className="text-sm text-muted">Startbestand des Cash-Kontos zu Beginn der ersten Lebensphase.</p>
      <MoneyField label="Anfangswert (CHF)" value={value} onChange={setValue} />
      {error && <p className="text-sm text-danger">{error}</p>}
      <DialogActions saving={saving} onConfirm={save} onClose={onClose} confirmLabel="Speichern" />
    </DialogShell>
  );
}

// --- Dialog: Element-Werte einer Lebensphase (per Klick auf eine Phasenzelle) ---
function PhaseCellDialog({
  element,
  phase,
  context,
  phaseData,
  onClose,
  onSaved,
  onDeleted,
}: {
  element: ElementInput;
  phase: PhaseComputed;
  context: CellContext;
  phaseData: PhaseData;
  onClose: () => void;
  onSaved: () => void;
  onDeleted: () => void;
}) {
  return (
    <DialogShell title={`Lebensphase: ${phase.name}`} onClose={onClose} wide>
      <ElementDetail
        element={element}
        context={context}
        phaseData={phaseData}
        transitionData={{}}
        onSaved={onSaved}
        onDeleteElement={onDeleted}
      />
    </DialogShell>
  );
}

// --- Dialog: einzelner Übergangs-Entscheid (per Klick auf eine Übergangszelle) ---
function TransitionCellDialog({
  element,
  fromPhase,
  toPhase,
  context,
  onClose,
  onSaved,
}: {
  element: ElementInput;
  fromPhase: PhaseComputed;
  toPhase: PhaseComputed | undefined;
  context: CellContext;
  onClose: () => void;
  onSaved: () => void;
}) {
  const [td, setTd] = useState<TransitionData>(() =>
    withTransitionDefaults(element.category, context.isRetirementTransition, element.transitionValues[fromPhase.id] ?? {})
  );
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function save() {
    setSaving(true);
    setError(null);
    try {
      await api.put(`/api/elements/${element.id}/transition/${fromPhase.id}`, td);
      onSaved();
    } catch (e) {
      setError(e instanceof Error ? e.message : "Speichern fehlgeschlagen.");
    } finally {
      setSaving(false);
    }
  }

  return (
    <DialogShell title={`Übergang: ${element.name}`} onClose={onClose}>
      <div className="text-xs text-muted">
        {CATEGORY_LABELS[element.category]} · {fromPhase.name} → {toPhase?.name ?? "Ende"}
      </div>
      <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
        <ElementTransitionFields
          element={element}
          context={context}
          td={td}
          setT={(patch) => setTd((prev) => ({ ...prev, ...patch }))}
        />
      </div>
      {error && <p className="text-sm text-danger">{error}</p>}
      <DialogActions saving={saving} onConfirm={save} onClose={onClose} confirmLabel="Speichern" />
    </DialogShell>
  );
}

// --- gemeinsame Dialog-Bausteine ---
function DialogShell({
  title,
  onClose,
  children,
  wide,
}: {
  title: string;
  onClose: () => void;
  children: React.ReactNode;
  wide?: boolean;
}) {
  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 ${wide ? "max-w-2xl" : "max-w-md"} flex-col gap-3 rounded-2xl border border-border bg-surface p-6 shadow-xl`}
      >
        <div className="flex items-center justify-between">
          <h2 className="text-base font-semibold text-fg">{title}</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>
        {children}
      </div>
    </div>
  );
}

function DialogActions({
  saving,
  onConfirm,
  onClose,
  confirmLabel,
}: {
  saving: boolean;
  onConfirm: () => void;
  onClose: () => void;
  confirmLabel: string;
}) {
  return (
    <div className="flex gap-2 pt-1">
      <button
        type="button"
        disabled={saving}
        onClick={onConfirm}
        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"
      >
        {saving ? "..." : confirmLabel}
      </button>
      <button
        type="button"
        onClick={onClose}
        className="rounded-lg border border-border px-4 py-2 text-sm font-medium text-muted hover:bg-surface-2"
      >
        Abbrechen
      </button>
    </div>
  );
}
