// ===== Code-Wolke: Alle 3-Letter-Codes nach Häufigkeit =====
// Spiral-Packung: größte Codes in der Mitte, kleinere spiralförmig außen herum.
// Farbcodierung nach Region; Hubs separat in Bernsteingelb.

const { useState: _wcU, useMemo: _wcM, useRef: _wcR } = React;

const REGION_COLORS = {
  HUB: "#f9b000",
  EU:  "#f9b000",
  NA:  "#7cc1ff",
  AS:  "#ff7e6a",
  AF:  "#9be38f",
  SA:  "#d7a7ff",
};

// --- Spiral placement (deterministic) ---
function packWordCloud(items, width, height) {
  if (!items.length) return [];
  const sorted = [...items].sort((a, b) => b.n - a.n);
  const maxN = sorted[0].n;
  const minN = sorted[sorted.length - 1].n;
  const sizeFor = (n) => {
    // sqrt-scaling gibt mehr kontrast in der mitte des wertebereichs: n=10 wirkt
    // deutlich groesser als n=1, ohne dass MUC (n=882) winzig macht
    const t = maxN === minN ? 0.5 : (n - minN) / (maxN - minN);
    const k = Math.sqrt(t);
    return 14 + k * 94; // 14 .. 108 px (vorher 16..88)
  };
  const cx = width / 2;
  const cy = height / 2;
  const placed = [];
  for (let i = 0; i < sorted.length; i++) {
    const item = sorted[i];
    const size = sizeFor(item.n);
    // Monospace width estimate (Chromium JetBrains Mono ~0.65 per char)
    const w = item.code.length * size * 0.65 + 12;
    const h = size * 1.32; // matches JetBrains Mono real bbox

    let placedOK = false;
    let best = null;

    // Spiral search
    const stepR = 4;
    const maxR = Math.max(width, height);
    const baseAngle = (i * 137.5) * (Math.PI / 180); // golden angle for variety
    for (let r = 0; r < maxR && !placedOK; r += stepR) {
      const steps = r === 0 ? 1 : Math.max(8, Math.floor(r / 6));
      for (let s = 0; s < steps && !placedOK; s++) {
        const a = baseAngle + (s / steps) * Math.PI * 2;
        const x = cx + Math.cos(a) * r - w / 2;
        const y = cy + Math.sin(a) * r - h / 2;
        if (x < 6 || y < 6 || x + w > width - 6 || y + h > height - 6) continue;
        const collides = placed.some(
          (p) => !(x + w + 6 < p.x || x > p.x + p.w + 6 || y + h + 6 < p.y || y > p.y + p.h + 6)
        );
        if (collides) continue;
        best = { ...item, x, y, w, h, size };
        placedOK = true;
      }
    }
    if (best) placed.push(best);
  }
  return placed;
}

function WordCloud({ width = 1200, height = 720 }) {
  // Prefer the full logbook dataset when present; fall back to the small partial set.
  const all = useMemo(() => {
    if (window.ALL_FLUGHAEFEN && window.ALL_FLUGHAEFEN.length) {
      // Normalize: tag hub region as HUB for color, keep original region for filter
      return window.ALL_FLUGHAEFEN.map((a) => ({ ...a, _colorRegion: a.kind === "hub" ? "HUB" : a.region }));
    }
    return WORDCLOUD_AIRPORTS.map((a) => ({ ...a, _colorRegion: a.region }));
  }, []);
  const isFullData = !!(window.ALL_FLUGHAEFEN && window.ALL_FLUGHAEFEN.length);
  const [filter, setFilter] = useState("alle");
  const [hovered, setHovered] = useState(null);

  const visible = useMemo(() => {
    if (filter === "alle") return all;
    if (filter === "hub") return all.filter((a) => a.kind === "hub");
    if (filter === "ziel") return all.filter((a) => a.kind === "ziel" || a.kind === "eckpunkt");
    if (filter === "eckpunkt") return all.filter((a) => a.kind === "eckpunkt");
    return all.filter((a) => a.region === filter);
  }, [filter, all]);

  const placed = useMemo(() => packWordCloud(visible, width, height), [visible, width, height]);

  // Sum of frequencies in current view (for context)
  const summe = useMemo(() => visible.reduce((s, a) => s + a.n, 0), [visible]);

  const filters = [
    { k: "alle",    label: "Alle" },
    { k: "hub",     label: "Hubs",       dot: REGION_COLORS.HUB },
    { k: "ziel",    label: "Ziele" },
    { k: "EU",      label: "Europa",     dot: REGION_COLORS.EU },
    { k: "NA",      label: "Nordamerika",dot: REGION_COLORS.NA },
    { k: "AS",      label: "Asien",      dot: REGION_COLORS.AS },
    { k: "AF",      label: "Afrika",     dot: REGION_COLORS.AF },
    { k: "SA",      label: "Südamerika", dot: REGION_COLORS.SA },
  ];

  return (
    <div className="space-y-5">
      {/* Filter chips */}
      <div className="flex flex-wrap gap-2">
        {filters.map((f) => (
          <button
            key={f.k}
            onClick={() => setFilter(f.k)}
            className={[
              "px-3 py-1.5 rounded-sm border text-[11px] font-mono tracking-[0.12em] uppercase transition-colors flex items-center gap-1.5",
              filter === f.k
                ? "bg-amber text-night border-amber"
                : "bg-night/30 text-cream/70 border-cream/15 hover:border-amber/60 hover:text-amber",
            ].join(" ")}
          >
            {f.dot && <span className="w-2 h-2 rounded-full" style={{ background: f.dot }} />}
            {f.label}
          </button>
        ))}
      </div>

      {/* Cloud */}
      <div className="relative w-full ticket-dark border border-cream/15 rounded-sm overflow-hidden">
        <svg viewBox={`0 0 ${width} ${height}`} className="w-full block" preserveAspectRatio="xMidYMid meet">
          {/* faint star backdrop accents */}
          <g opacity="0.15">
            {Array.from({ length: 28 }).map((_, i) => (
              <circle key={i}
                cx={(i * 137) % width}
                cy={(i * 73) % height}
                r={(i % 3) * 0.5 + 0.6}
                fill="#f6f0e2"
              />
            ))}
          </g>

          {placed.map((p) => {
            const isHov = hovered === p.code;
            const color = REGION_COLORS[p._colorRegion || p.region] || "#f6f0e2";
            const dim = hovered && !isHov;
            return (
              <g key={p.code}
                 onMouseEnter={() => setHovered(p.code)}
                 onMouseLeave={() => setHovered(null)}
                 style={{ cursor: "default", transition: "opacity .25s" }}
                 opacity={dim ? 0.3 : 1}>
                <text
                  x={p.x}
                  y={p.y}
                  dominantBaseline="text-before-edge"
                  fontSize={p.size}
                  fontFamily="JetBrains Mono"
                  fontWeight={p.kind === "hub" ? 700 : 500}
                  letterSpacing="0.04em"
                  fill={color}
                  fillOpacity={p.kind === "hub" ? 1 : 0.92}
                >
                  {p.code}
                </text>
                {isHov && (
                  <g>
                    <rect
                      x={p.x - 2}
                      y={p.y + p.h + 4}
                      width={Math.max(p.w, p.stadt.length * 7.5 + 30)}
                      height="38"
                      rx="2"
                      fill="#030c2f"
                      stroke="#f9b000"
                      strokeOpacity="0.5"
                    />
                    <text x={p.x + 4} y={p.y + p.h + 20} fontSize="13" fontFamily="JetBrains Mono" fill="#fbf6ea">
                      {p.stadt}
                    </text>
                    <text x={p.x + 4} y={p.y + p.h + 34} fontSize="10" fontFamily="JetBrains Mono" fill="#f9b000" letterSpacing="0.1em">
                      {p.n}{p.kind === "hub" ? "+ Flüge (Hub)" : " Flüge"} · {p.land}
                    </text>
                  </g>
                )}
              </g>
            );
          })}
        </svg>

        {/* Live readout overlay (left) */}
        <div className="absolute top-3 left-3 font-mono text-[10px] tracking-[0.2em] uppercase text-cream/65">
          <div>{visible.length} Codes · Σ {summe.toLocaleString("de-DE")} Flüge</div>
        </div>

        {/* Note overlay (right) */}
        <div className="absolute top-3 right-3 font-mono text-[10px] tracking-[0.2em] uppercase text-amber/80">
          {isFullData ? "vollständig aus Logbuch + Einsatzplänen" : `${WORDCLOUD_AIRPORTS.length} von ~87 Codes`}
        </div>
      </div>

      {/* Data-gap notice + Claude Code prompt — only when partial */}
      {!isFullData && <DataGapNotice />}
    </div>
  );
}

// --- Hinweis auf fehlende Daten + Claude-Code-Prompt zum Kopieren ---
function DataGapNotice() {
  const [open, setOpen] = useState(false);
  const [copied, setCopied] = useState(false);
  const promptText = CLAUDE_CODE_DATA_PROMPT;

  const copy = async () => {
    try {
      await navigator.clipboard.writeText(promptText);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch (e) {
      // fallback: select textarea
      const ta = document.getElementById("__cc-prompt-ta");
      if (ta) { ta.select(); document.execCommand("copy"); setCopied(true); setTimeout(() => setCopied(false), 1800); }
    }
  };

  return (
    <div className="ticket-dark border border-dashed border-amber/40 rounded-sm">
      <button
        onClick={() => setOpen(!open)}
        className="w-full flex items-center justify-between px-5 py-4 text-left"
      >
        <div className="flex items-start gap-3">
          <div className="mt-0.5 text-amber">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6">
              <circle cx="12" cy="12" r="9.5" />
              <path d="M12 8v4M12 16h0" strokeLinecap="round" />
            </svg>
          </div>
          <div>
            <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-amber">Daten lückenhaft</div>
            <div className="font-display text-lg md:text-xl text-cream mt-0.5">
              Fallback-Wolke aus 16 Top-Codes (für den Fall, dass das Logbuch nicht lädt).
            </div>
            <div className="text-cream/70 text-sm mt-1">
              Normalerweise zeigt die Wolke alle 87 Flughäfen aus dem Logbuch — Prompt für Claude Code unten, falls die vollständige Liste neu generiert werden muss.
            </div>
          </div>
        </div>
        <div className="font-mono text-[10px] tracking-[0.2em] uppercase text-amber whitespace-nowrap">
          {open ? "schließen" : "Prompt anzeigen"}
        </div>
      </button>

      {open && (
        <div className="px-5 pb-5">
          <div className="border-t border-cream/10 pt-4">
            <div className="flex items-center justify-between mb-2">
              <div className="font-mono text-[10px] tracking-[0.25em] uppercase text-cream/55">
                Prompt für Claude Code
              </div>
              <button
                onClick={copy}
                className="font-mono text-[10px] tracking-[0.2em] uppercase px-2.5 py-1 rounded-sm border border-amber text-amber hover:bg-amber hover:text-night transition-colors"
              >
                {copied ? "kopiert ✓" : "kopieren"}
              </button>
            </div>
            <textarea
              id="__cc-prompt-ta"
              readOnly
              value={promptText}
              className="w-full h-72 bg-night/60 text-cream/85 font-mono text-[11.5px] leading-relaxed p-3 rounded-sm border border-cream/10 outline-none resize-y"
            />
            <div className="mt-3 text-[11px] text-cream/55 leading-relaxed">
              So gehst du vor: Logbuch von Moritz (Excel/CSV/PDF) bereitlegen, Claude Code starten,
              den Prompt einfügen, das Logbuch als Anhang angeben. Claude Code schreibt dann
              eine neue Konstante <span className="font-mono text-amber">ALL_FLUGHAEFEN</span> in
              <span className="font-mono text-amber"> data.jsx</span>, die diese Wolke automatisch füllt.
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// --- Der eigentliche Prompt-Text ---
const CLAUDE_CODE_DATA_PROMPT = `# Aufgabe: Vollständige Flughafen-Liste für "Meine Lufthansa Jahre 2001–2011"

## Kontext
Es gibt eine private Erinnerungswebsite für Moritz Schieder (Flugbegleiter → Purser, 2001–2011).
Belegt sind 1.146 Flüge an 87 verschiedenen Flughäfen in 84 Städten und 47 Ländern.
Die Datendatei \`data.jsx\` enthält aktuell nur die Top 12 + 4 geografischen Eckpunkte (= 16 Flughäfen).

Für die "Code-Wolke" auf der Seite werden ALLE 87 angeflogenen Flughäfen mit ihrer jeweiligen Häufigkeit gebraucht.

## Eingabe
Moritz übergibt eine der folgenden Quellen:
- Excel/CSV-Export aus seinem Crew-Logbuch (Spalten i. d. R.: Datum, Flugnummer, Von, Nach, Flugzeit, Muster).
- Eine Liste/Tabelle als PDF.
- Falls nur Aggregate vorliegen: bitte fragen, ob Tabellen existieren — sonst nicht raten.

## Aufgabe
1. Lies die Quelle ein und zähle pro **Zielflughafen-IATA-Code** die Anzahl der Anflüge (also \`Nach\`-Spalte gruppiert).
2. Für jeden Flughafen ergänze: Stadt (deutsch), Land (deutsch), Region (\`EU\` | \`NA\` | \`AS\` | \`AF\` | \`SA\` | \`OC\`).
3. Hubs (MUC, FRA) als separate Einträge mit \`kind: "hub"\` und der Summe ALLER Abflüge von dort.
4. Sortiere die Liste absteigend nach Häufigkeit.
5. Schreibe das Ergebnis in \`data.jsx\` als neue exportierte Konstante:

\`\`\`js
// Auto-generated von Claude Code aus Moritz' Logbuch
const ALL_FLUGHAEFEN = [
  { code: "MUC", stadt: "München",   land: "Deutschland", region: "EU", kind: "hub",  n: 612 },
  { code: "FRA", stadt: "Frankfurt", land: "Deutschland", region: "EU", kind: "hub",  n: 381 },
  { code: "LHR", stadt: "London",    land: "Vereinigtes Königreich", region: "EU", kind: "ziel", n: 30 },
  // ... alle weiteren, absteigend ...
];
window.ALL_FLUGHAEFEN = ALL_FLUGHAEFEN;
\`\`\`

## Konsistenzregeln
- \`code\` ist immer der 3-Buchstaben-IATA-Code (Großbuchstaben).
- \`stadt\` und \`land\` deutsch geschrieben (z. B. "Köln", "Vereinigtes Königreich", nicht "Cologne"/"United Kingdom").
- Region: EU=Europa, NA=Nordamerika, AS=Asien, AF=Afrika, SA=Südamerika, OC=Ozeanien.
- Wenn ein Code mehrfach in der Quelle in unterschiedlicher Schreibweise vorkommt (z. B. "L.H.R.", "lhr"), normalisieren.
- Plausibilitätsprüfung: Summe aller \`kind:"hub"\`-Einträge soll ungefähr 1.146 ergeben (Anzahl Flüge gesamt).
- Plausibilitätsprüfung: Anzahl unterschiedlicher \`code\`-Werte mit \`kind:"ziel"\` soll ungefähr 85 ergeben (87 abzüglich 2 Hubs).

## Edge Cases / Hinweise
- 2005 fehlen Aufzeichnungen — Flüge in diesem Jahr ggf. nicht in der Quelle. Das ist OK, kein Schätzwert eintragen.
- Charter-Codes oder Code-Sharing-Codes ggf. zur regulären Strecke zählen (nachfragen, wenn unklar).
- B747 wurde nur 2001 geflogen; falls in der Quelle Muster-Information da ist, kann das für eine zusätzliche Aufschlüsselung genutzt werden — aber nicht benötigt für die Code-Wolke.

## Ausgabe
- Die neue \`ALL_FLUGHAEFEN\`-Konstante in \`data.jsx\` einfügen, direkt nach \`WORDCLOUD_AIRPORTS\`.
- In \`wordcloud.jsx\` die Zeile \`const all = WORDCLOUD_AIRPORTS;\` ersetzen durch:
  \`const all = window.ALL_FLUGHAEFEN || WORDCLOUD_AIRPORTS;\`
- Kurzbericht (Anzahl Codes, Top 10, ggf. Unstimmigkeiten) in der Chat-Antwort.

## Was NICHT zu tun ist
- Keine Schätzwerte erfinden, wenn das Logbuch Lücken hat.
- Keine Marken-Logos oder geschützten Assets einbinden.
- Keine Änderungen am Visual-Design der Seite — nur die Daten.
`;

window.WordCloud = WordCloud;
