"use client";

import { useMemo, useState } from "react";
import { Bar, BarChart, CartesianGrid, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
import { Tornado, X } from "lucide-react";
import { RequiredNumberField, SelectField } from "@/components/FormField";
import { InfoBubble } from "@/components/InfoBubble";
import { formatChf } from "@/lib/format";
import {
  computeTornado,
  DRIVERS,
  UNIT_SUFFIX,
  type DriverDef,
  type DriverId,
  type TornadoMetric,
  type TornadoResult,
} from "@/lib/sensitivity";
import type { PlanInput } from "@/lib/types";

interface RangeDraft {
  checked: boolean;
  low: string;
  high: string;
}

// Bewusst OHNE Defaults: Die Balkenlänge hängt direkt von der eingegebenen Bandbreite ab.
// Ein stiller Default würde nicht hinterfragt und das Ranking wäre dann eine Aussage über
// unsere Vorgabe statt über den Plan (gleiche Begründung wie bei den Monte-Carlo-Mittelwerten).
const emptyDraft: RangeDraft = { checked: false, low: "", high: "" };

function formatRange(low: number, high: number, unit: DriverDef["unit"]): string {
  const suffix = UNIT_SUFFIX[unit];
  const sign = (v: number) => (unit === "abs_pct" ? `${v}` : v > 0 ? `+${v}` : `${v}`);
  return `${sign(low)} ${suffix} → ${sign(high)} ${suffix}`;
}

export function SensitivityDialog({ plan, onClose }: { plan: PlanInput; onClose: () => void }) {
  const available = useMemo(() => DRIVERS.filter((d) => d.applies(plan)), [plan]);

  const [metric, setMetric] = useState<TornadoMetric>("real");
  const [drafts, setDrafts] = useState<Record<string, RangeDraft>>({});
  const [result, setResult] = useState<TornadoResult | null>(null);

  function draftFor(id: DriverId): RangeDraft {
    return drafts[id] ?? emptyDraft;
  }
  function setDraft(id: DriverId, patch: Partial<RangeDraft>) {
    setDrafts((prev) => ({ ...prev, [id]: { ...draftFor(id), ...patch } }));
    setResult(null);
  }

  const checked = available.filter((d) => draftFor(d.id).checked);
  const incomplete = checked.filter((d) => {
    const dr = draftFor(d.id);
    return dr.low.trim() === "" || dr.high.trim() === "";
  });
  const degenerate = checked.filter((d) => {
    const dr = draftFor(d.id);
    return dr.low.trim() !== "" && dr.high.trim() !== "" && Number(dr.low) === Number(dr.high);
  });

  // Mindestens zwei Treiber: Ein Tornado ist eine RANGLISTE. Mit einem einzigen Balken gibt
  // es nichts zu ordnen und die Grafik würde eine Aussage suggerieren, die sie nicht hat.
  const canRun =
    checked.length >= 2 && incomplete.length === 0 && degenerate.length === 0 && plan.phases.length > 0;

  function run() {
    setResult(
      computeTornado(
        plan,
        metric,
        checked.map((d) => {
          const dr = draftFor(d.id);
          return { id: d.id, low: Number(dr.low), high: Number(dr.high) };
        })
      )
    );
  }

  const chartData = useMemo(() => {
    if (!result) return [];
    // Von unten nach oben gezeichnet -> für die Trichterform (grösster Balken zuoberst)
    // muss die Reihenfolge umgekehrt in die Grafik.
    return [...result.bars].reverse().map((b) => ({
      label: b.shortLabel,
      range: [b.min, b.max] as [number, number],
      swing: b.swing,
    }));
  }, [result]);

  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">
            <Tornado className="h-5 w-5 text-accent" /> Einflussfaktoren (Sensitivitätsanalyse)
          </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> Nicht «wie viel Geld habe ich am Schluss», sondern
            <strong className="text-fg"> «welche meiner Annahmen entscheidet überhaupt über das Ergebnis»</strong>. Bei
            manchen Annahmen ist es egal, ob du sie exakt triffst – bei anderen kippt eine kleine Abweichung den ganzen Plan.
          </p>
          <p className="mb-2">
            <strong className="text-fg">Wie es funktioniert:</strong> Zuerst wird dein Plan wie erfasst gerechnet
            (Basisfall). Dann wird <em>ein einziger</em> Parameter auf seinen tiefen und seinen hohen Wert gesetzt, alle
            übrigen bleiben unverändert – das ergibt zwei Ergebnisse. Das für jeden Parameter wiederholt und nach
            <strong className="text-fg"> Spannweite</strong> sortiert ergibt die Trichterform: längster Balken zuoberst.
          </p>
          <p className="mb-2">
            <strong className="text-fg">Was du davon hast:</strong> Die Reihenfolge ist die Botschaft. Bei den obersten
            Balken lohnt sich Genauigkeit (dort exakte Zahlen beschaffen) – und sie sind meist auch die, die du selbst
            steuern kannst. Bei den untersten darfst du grob schätzen.
          </p>
          <p>
            <strong className="text-fg">Zwei ehrliche Grenzen.</strong> Erstens hängt die Balkenlänge von den
            Bandbreiten ab, die du unten eingibst – wähle sie so, dass sie <em>ähnlich plausibel</em> sind, nicht ähnlich
            gross. Zweitens wird immer nur ein Parameter auf einmal variiert; <em>Kombinationen</em> (schlechte Renditen
            <em> und</em> hohe Ausgaben) treffen härter als die Summe der Einzelbalken – dafür ist die
            Monte-Carlo-Simulation zuständig.
          </p>
        </div>

        {/* Zielgrösse */}
        <div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
          <SelectField
            label="Zielgrösse"
            help="Woran die Wirkung gemessen wird: das Endvermögen der letzten Lebensphase. «Real» ist kaufkraftbereinigt auf den Planbeginn und damit die ehrlichere Grösse."
            value={metric}
            onChange={(v: TornadoMetric) => {
              setMetric(v);
              setResult(null);
            }}
            options={[
              { value: "real", label: "Endvermögen real (kaufkraftbereinigt)" },
              { value: "nominal", label: "Endvermögen nominal" },
            ]}
          />
        </div>

        {/* Parameter */}
        <div>
          <div className="mb-2 flex items-center text-xs font-semibold uppercase tracking-wide text-faint">
            Parameter und Bandbreiten
            <InfoBubble text="Hake an, welche Parameter in die Analyse einfliessen sollen, und gib je Parameter eine tiefe und eine hohe Ausprägung an. Es gibt bewusst keine Vorgabewerte: Die Balkenlänge hängt direkt von diesen Bandbreiten ab, deshalb sollst du sie bewusst setzen. Die Hilfe je Parameter nennt plausible Grössenordnungen." />
          </div>
          <div className="flex flex-col gap-2">
            {available.map((d) => {
              const dr = draftFor(d.id);
              const suffix = UNIT_SUFFIX[d.unit];
              const unitHint =
                d.unit === "abs_pct"
                  ? "absoluter Wert in %"
                  : d.unit === "delta_pp"
                    ? "Verschiebung in Prozentpunkten"
                    : d.unit === "rel_pct"
                      ? "Abweichung vom Planwert in %"
                      : "Verschiebung in Jahren";
              return (
                <div
                  key={d.id}
                  className={`rounded-xl border p-3 ${dr.checked ? "border-accent bg-accent-soft/30" : "border-border bg-surface-2"}`}
                >
                  <label className="flex items-center gap-2 text-sm">
                    <input type="checkbox" checked={dr.checked} onChange={(e) => setDraft(d.id, { checked: e.target.checked })} />
                    <span className="font-medium text-fg">{d.label}</span>
                    <InfoBubble text={d.help} />
                    <span className="text-[11px] text-faint">({unitHint})</span>
                  </label>
                  {dr.checked && (
                    <div className="mt-2 grid grid-cols-1 gap-3 sm:grid-cols-3">
                      <RequiredNumberField
                        label="tief"
                        value={dr.low}
                        suffix={suffix}
                        step={d.unit === "delta_years" ? 1 : 0.1}
                        onChange={(v) => setDraft(d.id, { low: v })}
                      />
                      <RequiredNumberField
                        label="hoch"
                        value={dr.high}
                        suffix={suffix}
                        step={d.unit === "delta_years" ? 1 : 0.1}
                        onChange={(v) => setDraft(d.id, { high: v })}
                      />
                    </div>
                  )}
                </div>
              );
            })}
          </div>

          <p className="mt-2 text-[11px] text-faint">
            Das <strong>Pensionsalter</strong> ist bewusst nicht enthalten: Es lässt sich nicht sinnvoll variieren, ohne
            gleichzeitig die Phasengrenzen mitzuverschieben – sonst arbeitet die Person im Modell unverändert weiter
            bzw. der Pensions-Übergang entfällt ganz. Der verwandte Treiber «Lebensdauer» ist dagegen sauber abgebildet.
          </p>
        </div>

        {checked.length < 2 && (
          <p className="text-xs text-danger">Bitte mindestens zwei Parameter auswählen – der Tornado ist eine Rangliste.</p>
        )}
        {incomplete.length > 0 && (
          <p className="text-xs text-danger">Bitte für jeden ausgewählten Parameter eine tiefe und eine hohe Ausprägung angeben.</p>
        )}
        {degenerate.length > 0 && (
          <p className="text-xs text-danger">
            Tiefer und hoher Wert sind identisch bei: {degenerate.map((d) => d.label).join(", ")}. Damit gibt es keine Spannweite.
          </p>
        )}

        <div>
          <button
            type="button"
            disabled={!canRun}
            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"
          >
            Einflussfaktoren berechnen
          </button>
        </div>

        {result && <TornadoResults result={result} metric={metric} chartData={chartData} />}
      </div>
    </div>
  );
}

function TornadoResults({
  result,
  metric,
  chartData,
}: {
  result: TornadoResult;
  metric: TornadoMetric;
  chartData: { label: string; range: [number, number]; swing: number }[];
}) {
  const metricLabel = metric === "real" ? "Endvermögen real" : "Endvermögen nominal";
  return (
    <div className="flex flex-col gap-4 border-t border-border pt-4">
      <div className="rounded-xl border border-border bg-surface-2 px-4 py-3">
        <div className="text-xs text-muted">Basisfall · {metricLabel}</div>
        <div className="mt-0.5 text-xl font-semibold text-accent">{formatChf(result.base)} CHF</div>
      </div>

      <div>
        <div className="mb-1 text-xs font-semibold text-fg">Einflussfaktoren nach Spannweite</div>
        <p className="mb-2 text-[11px] text-muted">
          Jeder Balken zeigt, zwischen welchen Werten das {metricLabel} schwankt, wenn nur dieser eine Parameter
          innerhalb seiner Bandbreite variiert. Die senkrechte Linie ist der Basisfall.
        </p>
        <div className="w-full" style={{ height: Math.max(200, chartData.length * 46 + 60) }}>
          <ResponsiveContainer width="100%" height="100%">
            <BarChart data={chartData} layout="vertical" margin={{ top: 8, right: 24, left: 8, bottom: 8 }}>
              <CartesianGrid strokeDasharray="3 3" className="stroke-border" />
              <XAxis
                type="number"
                tick={{ fontSize: 11 }}
                tickFormatter={(v) => Intl.NumberFormat("de-CH", { notation: "compact" }).format(v)}
              />
              <YAxis type="category" dataKey="label" tick={{ fontSize: 11 }} width={130} />
              <Tooltip
                formatter={(v) =>
                  Array.isArray(v)
                    ? `${formatChf(Number(v[0]))} – ${formatChf(Number(v[1]))}`
                    : typeof v === "number"
                      ? formatChf(v)
                      : v
                }
              />
              <ReferenceLine x={result.base} stroke="var(--muted)" strokeDasharray="4 3" />
              <Bar dataKey="range" name={metricLabel} fill="var(--accent)" radius={[3, 3, 3, 3]} isAnimationActive={false} />
            </BarChart>
          </ResponsiveContainer>
        </div>
      </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">Parameter</th>
              <th className="px-3 py-2 text-left font-semibold">Bandbreite</th>
              <th className="px-3 py-2 text-right font-semibold">bei «tief»</th>
              <th className="px-3 py-2 text-right font-semibold">bei «hoch»</th>
              <th className="px-3 py-2 text-right font-semibold">Spannweite</th>
            </tr>
          </thead>
          <tbody>
            {result.bars.map((b) => (
              <tr key={b.id} className="border-t border-border">
                <td className="px-3 py-2 font-medium text-fg">
                  {b.label}
                  {b.note && <div className="mt-0.5 text-[11px] font-normal text-muted">{b.note}</div>}
                </td>
                <td className="px-3 py-2 text-muted">{formatRange(b.low, b.high, b.unit)}</td>
                <td className="px-3 py-2 text-right text-muted">{formatChf(b.lowResult)}</td>
                <td className="px-3 py-2 text-right text-muted">{formatChf(b.highResult)}</td>
                <td className="px-3 py-2 text-right font-semibold text-fg">{formatChf(b.swing)}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <p className="text-[11px] text-faint">
        Die Reihenfolge – nicht der absolute Betrag – ist die Aussage. Sie hängt von den eingegebenen Bandbreiten ab.
      </p>
    </div>
  );
}
