// WorldGen — network graph (option 5), cleaned up for clarity.
// After the input submit, nodes burst out and edges form, settling into a
// readable world map. Only KEY relations are kept (5 instead of 8). Different
// shapes per type, annotations under each label, colored relation edges.
const { useState, useEffect, useRef } = React;

const PROMPT = {
  en: "Dwarven smiths in the steam-empire of Steamwald.",
  ru: "Дварфийские кузнецы в стимпанк-империи Стимвальд.",
};
const FILE_NAME = "campaign.epub";

// Pre-calculated positions (in % of stage)
const NODES = [
  { id: "root",     x: 50, y: 56, z:   0, size: "lg", sizeScale: 0.9, kind: "world", label: { en: "Steamwald",     ru: "Стимвальд"        }, summary: { en: "your world",         ru: "ваш мир"            }, IconKey: "Planet" },
  { id: "brogan",   x: 14, y: 24, z: 140, size: "md", kind: "char",  label: { en: "Brogan",        ru: "Броган"           }, summary: { en: "Dwarf cleric · L7",  ru: "Дварф-жрец · 7ур." }, img: "assets/portrait-dwarf.png" },
  { id: "ealdrin",  x: 86, y: 26, z:  -3, size: "md", kind: "char",  label: { en: "Ealdrin",       ru: "Элдрин"           }, summary: { en: "Grey mage",          ru: "Серый маг"         }, img: "assets/portrait-wizard.png" },
  { id: "chandos",  x: 8,  y: 64, z: -52, size: "sm", kind: "loc",   label: { en: "Chandos",       ru: "Чандос"           }, summary: { en: "Capital city",       ru: "Столица"           }, IconKey: "Tower" },
  { id: "coraltide", x: 24, y: 86, z:  25, size: "sm", kind: "loc",  label: { en: "Coraltide",     ru: "Коралтайд"        }, summary: { en: "Sea drift",          ru: "Морской дрейф"     }, IconKey: "Tower" },
  { id: "flame",    x: 76, y: 86, z:   8, size: "sm", kind: "fact",  label: { en: "Flame Order",   ru: "Орден Пламени"    }, summary: { en: "Religious order",    ru: "Орден"             }, IconKey: "Shield" },
  { id: "vael",     x: 92, y: 64, z: -45, size: "sm", kind: "fact",  label: { en: "House Vael",    ru: "Дом Вэйль"        }, summary: { en: "Noble house",        ru: "Дом знати"         }, IconKey: "Shield" },
  { id: "rapier",   x: 50, y: 12, z: -20, size: "sm", kind: "quest", label: { en: "Rapier",        ru: "Древняя рапира"   }, summary: { en: "Lost blade",         ru: "Потерянный клинок" }, IconKey: "Paper" },
];

// Edge colors per relation type
const REL_COLORS = {
  bond:     "#88D497",  // mentor / ally
  origin:   "#FFD580",  // native of
  faction:  "#ADDCFF",  // sworn / founded
  artifact: "#BD9FF5",  // wields
};

// 4 key relations between characters and entities
const RELATION_EDGES = [
  { a: "brogan",   b: "ealdrin", type: "bond",     label: { en: "trained by", ru: "обучен"      }, lift: 0.55 },
  { a: "brogan",   b: "chandos", type: "origin",   label: { en: "native of",  ru: "родом из"    } },
  { a: "brogan",   b: "rapier",  type: "artifact", label: { en: "wields",     ru: "владеет"     } },
  { a: "ealdrin",  b: "flame",   type: "faction",  label: { en: "founded",    ru: "основал"     } },
];

const PRIMARY_EDGES = [
  ["root", "brogan"], ["root", "ealdrin"], ["root", "chandos"], ["root", "coraltide"],
  ["root", "flame"],  ["root", "vael"],    ["root", "rapier"],
];

const DURATION = 10500;

// Node radii in % of stage — used to trim edges so they don't pass through nodes
const NODE_R = { lg: 7.5, md: 5.0, sm: 3.9 };

function trimmedArc(A, B, rA, rB, lift = 0.08) {
  const dx = B.x - A.x, dy = B.y - A.y;
  const len = Math.hypot(dx, dy);
  if (len < 1) return `M ${A.x} ${A.y} L ${B.x} ${B.y}`;
  const ux = dx / len, uy = dy / len;
  const gap = 1.8;
  const sx = A.x + ux * (rA + gap);
  const sy = A.y + uy * (rA + gap);
  const ex = B.x - ux * (rB + gap);
  const ey = B.y - uy * (rB + gap);
  const mx = (sx + ex) / 2, my = (sy + ey) / 2;
  const px = -uy, py = ux;
  const newLen = Math.hypot(ex - sx, ey - sy);
  const lf = lift * newLen * 0.5;
  const cx = mx + px * lf;
  const cy = my + py * lf;
  return `M ${sx.toFixed(2)} ${sy.toFixed(2)} Q ${cx.toFixed(2)} ${cy.toFixed(2)}, ${ex.toFixed(2)} ${ey.toFixed(2)}`;
}

function effectiveR(n) {
  const baseR = NODE_R[n.size];
  const scale = (n.sizeScale || 1) * 1.1 * (1 + (n.z || 0) * 0.0035);
  return baseR * scale;
}

function arcPath(x1, y1, x2, y2, lift = 0.08) {
  const mx = (x1 + x2) / 2;
  const my = (y1 + y2) / 2;
  const dx = x2 - x1, dy = y2 - y1;
  const len = Math.hypot(dx, dy);
  if (len < 1) return `M ${x1} ${y1} L ${x2} ${y2}`;
  const px = -dy / len, py = dx / len;
  const lf = lift * len * 0.5;
  const cx = mx + px * lf;
  const cy = my + py * lf;
  return `M ${x1.toFixed(2)} ${y1.toFixed(2)} Q ${cx.toFixed(2)} ${cy.toFixed(2)}, ${x2.toFixed(2)} ${y2.toFixed(2)}`;
}

function WorldGen({ lang, parallaxK = 1 }) {
  const [t, setT] = useState(0);
  const [hover, setHover] = useState(null);
  const startRef = useRef(null);
  const stageRef = useRef(null);
  const [tilt, setTilt] = useState({ x: 0, y: 0 });

  useEffect(() => {
    let raf;
    const tick = (now) => {
      if (!startRef.current) startRef.current = now;
      const elapsed = now - startRef.current;
      const next = Math.min(1, elapsed / DURATION);
      setT(next);
      if (next < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);

  useEffect(() => {
    const handler = (e) => {
      const rect = stageRef.current?.getBoundingClientRect();
      if (!rect) return;
      const cx = rect.left + rect.width / 2;
      const cy = rect.top + rect.height / 2;
      const dx = (e.clientX - cx) / window.innerWidth;
      const dy = (e.clientY - cy) / window.innerHeight;
      setTilt({ x: 0, y: 0 });
    };
    window.addEventListener("mousemove", handler);
    return () => window.removeEventListener("mousemove", handler);
  }, [parallaxK]);

  const prompt = PROMPT[lang];
  const tStart = 0.03, tEnd = 0.16;
  const typed = t < tStart ? 0 : t > tEnd ? prompt.length :
    Math.floor(((t - tStart) / (tEnd - tStart)) * prompt.length);
  const fileDrop = t > 0.18;
  const fileProgress = t < 0.20 ? 0 : t > 0.30 ? 100 : ((t - 0.20) / 0.10) * 100;
  const fileDone = t > 0.30;

  const submitPulse = t > 0.30 && t < 0.46;
  const rootBurst = t > 0.40 && t < 0.50;
  const inputDone = t > 0.42;

  // Burst window: each node animates from center to its target.
  const burstStart = 0.42;
  const burstStagger = 0.024;
  const nodeProgress = (i) => {
    if (i === 0) {
      if (t < 0.40) return 0;
      if (t > 0.50) return 1;
      return (t - 0.40) / 0.10;
    }
    const s = burstStart + (i - 1) * burstStagger;
    const e = s + 0.12;
    if (t < s) return 0;
    if (t > e) return 1;
    return (t - s) / (e - s);
  };
  const easeOut = (p) => 1 - Math.pow(1 - p, 3);
  const nodeState = (n, i) => {
    const p = nodeProgress(i);
    if (p <= 0) return { x: 50, y: 50, scale: 0, opacity: 0 };
    if (p >= 1) return { x: n.x, y: n.y, scale: 1, opacity: 1 };
    const e = easeOut(p);
    return {
      x: 50 + (n.x - 50) * e,
      y: 50 + (n.y - 50) * e,
      scale: 0.4 + 0.6 * e,
      opacity: Math.min(1, p * 2),
    };
  };

  const primaryEdgeActive = (i) => t > burstStart + i * burstStagger + 0.04;
  const relEdgeActive = (i) => t > 0.80 + i * 0.030;
  const relLabelVisible = (i) => t > 0.82 + i * 0.030;
  const statsVisible = t > 0.90;

  const renderIcon = (key) => {
    const I = window.FigmaIcons?.[key];
    return I ? <I /> : null;
  };

  const nodeById = Object.fromEntries(NODES.map((n, i) => [n.id, { ...n, state: nodeState(n, i) }]));

  const adjacentTo = (id) => {
    const adj = new Set();
    PRIMARY_EDGES.forEach(([a, b]) => { if (a === id) adj.add(b); if (b === id) adj.add(a); });
    RELATION_EDGES.forEach(({ a, b }) => { if (a === id) adj.add(b); if (b === id) adj.add(a); });
    return adj;
  };
  const dimmed = hover ? (id) => id !== hover && !adjacentTo(hover).has(id) : () => false;
  const edgeDimmed = (a, b) => hover && a !== hover && b !== hover;
  const edgeHighlighted = (a, b) => hover && (a === hover || b === hover);

  return (
    <div className="wg-stage" ref={stageRef}
      style={{ transform: `rotateX(${tilt.x}deg) rotateY(${tilt.y}deg)` }}>

      <div className="wg-modes">
        <div className={`wg-mode-tab ${t < 0.16 ? "active" : ""}`}>
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round">
            <path d="M4 7h16M4 12h16M4 17h10"/>
          </svg>


          {lang === "ru" ? "Создание" : "Live Building"}
        </div>
        <div className="wg-mode-divider"></div>
        <div className={`wg-mode-tab ${t >= 0.16 && t < 0.42 ? "active" : ""}`}>
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
            <path d="M14 3v6h6"/>
          </svg>
          {lang === "ru" ? "Загрузка" : "Drop a file"}
        </div>
      </div>

      <div className={`wg-bg-pulse ${submitPulse ? "on" : ""}`}></div>
      <div className={`wg-burst ${rootBurst ? "on" : ""}`}></div>

      {/* INPUT */}
      {t < 0.16 ? (
        <div className="wg-input">
          <span className="wg-input-icon">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
              <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
            </svg>
          </span>
          <span className="wg-input-text">
            {prompt.slice(0, typed)}
            {t < tEnd && <span className="wg-caret"></span>}
          </span>
        </div>
      ) : (
        <div className={`wg-input wg-file ${fileDrop ? "in" : ""} ${submitPulse ? "submit" : ""} ${inputDone ? "done" : ""}`}>
          <span className="wg-input-icon">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/>
              <path d="M14 3v6h6"/>
            </svg>
          </span>
          <span className="wg-file-text">
            <span className="wg-file-name">{FILE_NAME}</span>
            {!fileDone && (
              <span className="wg-file-progress">
                <span className="wg-file-bar" style={{ width: `${fileProgress}%` }}></span>
              </span>
            )}
            {fileDone && (
              <span className="wg-file-done">
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.5" strokeLinecap="round">
                  <path d="M5 12.5l5 5 9-11"/>
                </svg>
                {lang === "ru" ? "Прочитано" : "Parsed"}
              </span>
            )}
          </span>
        </div>
      )}

      {/* HIERARCHY GRAPH */}
      <svg className="wg-graph" viewBox="0 0 100 100" preserveAspectRatio="none">
        {/* Faint primary edges — root to entities, "belongs to world" */}
        {PRIMARY_EDGES.map(([a, b], i) => {
          const A = nodeById[a];
          const B = nodeById[b];
          const active = primaryEdgeActive(i);
          const dim = edgeDimmed(a, b);
          const hi = edgeHighlighted(a, b);
          return (
            <path key={`pe-${i}`}
              className={`wg-net-edge ${active ? "on" : ""} ${dim ? "dim" : ""} ${hi ? "hi" : ""}`}
              d={trimmedArc(A.state, B.state, effectiveR(A), effectiveR(B), 0.05)}
              pathLength="1"
              style={{ animationDelay: `${i * 60}ms` }} />
          );
        })}

        {/* Relation edges — colored arcs, trimmed */}
        {RELATION_EDGES.map(({ a, b, type, lift = 0.15 }, i) => {
          const A = nodeById[a];
          const B = nodeById[b];
          const active = relEdgeActive(i);
          const dim = edgeDimmed(a, b);
          const hi = edgeHighlighted(a, b);
          const color = REL_COLORS[type];
          return (
            <path key={`e-${i}`}
              className={`wg-rel-edge ${active ? "on" : ""} ${dim ? "dim" : ""} ${hi ? "hi" : ""}`}
              d={trimmedArc(A.state, B.state, effectiveR(A), effectiveR(B), lift)}
              pathLength="1"
              stroke={color}
              style={{ "--rel-color": color, animationDelay: `${i * 70}ms` }} />
          );
        })}
              
      </svg>
      {/* Relation labels — glass pills at curve midpoint */}
      {RELATION_EDGES.map(({ a, b, type, label, labelDy = 0, lift = 0.15 }, i) => {
        if (!relLabelVisible(i)) return null;
        const A = nodeById[a].state;
        const B = nodeById[b].state;
        // Wait until both endpoint nodes have fully settled, otherwise labels
        // chase mid-burst positions and look misaligned.
        if (A.opacity < 0.95 || B.opacity < 0.95) return null;
        const dx = B.x - A.x, dy = B.y - A.y;
        const len = Math.hypot(dx, dy);
        if (len < 1) return null;
        const ux = dx / len, uy = dy / len;
        // Trim the chord by each node's radius — match trimmedArc()
        const rA = NODE_R[nodeById[a].size] + 1.8;
        const rB = NODE_R[nodeById[b].size] + 1.8;
        const sx = A.x + ux * rA;
        const sy = A.y + uy * rA;
        const ex = B.x - ux * rB;
        const ey = B.y - uy * rB;
        const mx = (sx + ex) / 2;
        const my = (sy + ey) / 2;
        // Perpendicular (same direction the arc bows). Label sits on the curve
        // midpoint = chord midpoint + (lift * 0.5) * 0.5 = lift * 0.25.
        const px = -uy, py = ux;
        const newLen = Math.hypot(ex - sx, ey - sy);
        const lf = lift * newLen * 0.25;
        const lx = mx + px * lf;
        const ly = my + py * lf + labelDy;
        const dim = edgeDimmed(a, b);
        const hi = edgeHighlighted(a, b);
        const color = REL_COLORS[type];
        return (
          <div key={`rl-${i}`}
            className={`wg-rel-label ${hi ? "hi" : ""}`}
            style={{
              left: `${lx}%`,
              top: `${ly}%`,
              opacity: dim ? 0.2 : 1,
              "--rel-color": color,
            }}>
            {label[lang]}
          </div>
        );
      })}

      {/* NODES */}
      {NODES.map((n, i) => {
        const s = n.state = nodeState(n, i);
        const isDim = dimmed(n.id);
        const isHi = hover === n.id;
        return (
          <div key={n.id}
            className={`wg-net-node wg-${n.size} wg-${n.kind} ${s.opacity > 0.95 ? "settled" : ""} ${isDim ? "dim" : ""} ${isHi ? "hi" : ""}`}
            style={{
              left: `${s.x}%`,
              top: `${s.y}%`,
              opacity: s.opacity,
              transform: `translate(-50%, -50%) scale(${(n.sizeScale || 1) * 1.1 * s.scale * (1 + (n.z || 0) * 0.0035)})`,
              animationDelay: `${i * 0.4}s`,
            }}
            onMouseEnter={() => setHover(n.id)}
            onMouseLeave={() => setHover(null)}>
            {n.img ? (
              <div className="wg-net-portrait" style={{ backgroundImage: `url(${n.img})` }}></div>
            ) : (
              <div className="wg-net-icon">{renderIcon(n.IconKey)}</div>
            )}
            <span className="wg-net-label">{n.label[lang]}</span>
            {n.kind === "world" && n.summary && <span className="wg-net-summary">{n.summary[lang]}</span>}
          </div>
        );
      })}

      {/* STATS COUNTER */}
      <div className={`wg-stats ${statsVisible ? "in" : ""}`}>
        <div className="wg-stat">
          <span className="wg-stat-n">{Math.min(8, Math.round((t - 0.88) * 80 + 0.5)) || 0}</span>
          <span className="wg-stat-l">{lang === "ru" ? "сущности" : "nodes"}</span>
        </div>
        <div className="wg-stat-sep"></div>
        <div className="wg-stat">
          <span className="wg-stat-n">{Math.min(12, Math.round((t - 0.88) * 120 + 0.5)) || 0}</span>
          <span className="wg-stat-l">{lang === "ru" ? "связи" : "edges"}</span>
        </div>
        <div className="wg-stat-sep"></div>
        <div className="wg-stat">
          <span className="wg-stat-n">4</span>
          <span className="wg-stat-l">{lang === "ru" ? "категории" : "kinds"}</span>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { WorldGen });
