/* CleanDeen — shared components & icons (exported to window) */
const { useState } = React;

/* ---------------- Icons (simple, line-based) ---------------- */
const Ic = {
  leaf: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z"/><path d="M2 21c0-3 1.85-5.36 5.08-6"/></svg>),
  flask: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M9 3h6M10 3v6.34a2 2 0 0 1-.29 1.04L4.4 19.2A1.5 1.5 0 0 0 5.7 21.5h12.6a1.5 1.5 0 0 0 1.3-2.3l-5.31-8.82A2 2 0 0 1 14 9.34V3"/><path d="M7 15h10"/></svg>),
  shield: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z"/><path d="m9 12 2 2 4-4"/></svg>),
  check: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m20 6-11 11-5-5"/></svg>),
  phone: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3 19.5 19.5 0 0 1-6-6 19.8 19.8 0 0 1-3.1-8.6A2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.13.96.36 1.9.7 2.8a2 2 0 0 1-.45 2.1L8.1 9.9a16 16 0 0 0 6 6l1.3-1.3a2 2 0 0 1 2.1-.45c.9.34 1.84.57 2.8.7a2 2 0 0 1 1.7 2Z"/></svg>),
  mail: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 7 10 6 10-6"/></svg>),
  pin: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="2.6"/></svg>),
  cal: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="3" y="4.5" width="18" height="17" rx="2"/><path d="M3 9.5h18M8 2.5v4M16 2.5v4"/></svg>),
  plus: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" {...p}><path d="M12 5v14M5 12h14"/></svg>),
  arrow: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M5 12h14M13 6l6 6-6 6"/></svg>),
  home: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m3 10 9-7 9 7"/><path d="M5 9v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V9"/><path d="M9 21v-7h6v7"/></svg>),
  building: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="4" y="2.5" width="16" height="19" rx="1.5"/><path d="M9 7h2M13 7h2M9 11h2M13 11h2M9 15h2M13 15h2M10 21.5v-3h4v3"/></svg>),
  sparkle: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M8.4 15.6l-2.8 2.8"/></svg>),
  drop: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M12 3s6 6.5 6 11a6 6 0 0 1-12 0c0-4.5 6-11 6-11Z"/></svg>),
  menu: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" {...p}><path d="M3 6h18M3 12h18M3 18h18"/></svg>),
  x: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" {...p}><path d="M18 6 6 18M6 6l12 12"/></svg>),
  chevron: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m9 18 6-6-6-6"/></svg>),
  chevDown: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="m6 9 6 6 6-6"/></svg>),
  lock: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>),
  user: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 4-6 8-6s8 2 8 6"/></svg>),
  grid: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>),
  clip: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M9 4h6a1 1 0 0 1 1 1v1h2a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2V5a1 1 0 0 1 1-1Z"/><path d="M9 4.5h6"/><path d="m9 14 2 2 4-4"/></svg>),
  chart: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3 3v18h18"/><path d="M7 14l3-4 3 2 4-6"/></svg>),
  expand: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>),
  users: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><circle cx="9" cy="8" r="3.4"/><path d="M2.5 20c0-3.3 3-5 6.5-5s6.5 1.7 6.5 5"/><path d="M16 5.2a3.4 3.4 0 0 1 0 5.6M18 15c2.4.5 3.8 2 3.8 4.4"/></svg>),
  star: (p) => (<svg viewBox="0 0 24 24" fill="currentColor" stroke="none" {...p}><path d="m12 2.6 2.9 5.9 6.5.95-4.7 4.6 1.1 6.45L12 18.9l-5.8 3.05 1.1-6.45-4.7-4.6 6.5-.95Z"/></svg>),
  trash: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6M10 11v6M14 11v6"/></svg>),
  cube: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M21 7.5 12 2 3 7.5v9L12 22l9-5.5v-9Z"/><path d="m3 7.5 9 5.5 9-5.5M12 13v9"/></svg>),
  camera: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3 8a2 2 0 0 1 2-2h2l1.5-2h7L19 6h2a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8Z" transform="translate(0 0)"/><circle cx="12" cy="13" r="3.6"/></svg>),
  upload: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M12 3v13M7 8l5-5 5 5"/></svg>),
  eye: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>),
  send: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M22 2 11 13M22 2l-7 20-4-9-9-4 20-7Z"/></svg>),
  refresh: (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3 12a9 9 0 0 1 15-6.7L21 8M21 3v5h-5M21 12a9 9 0 0 1-15 6.7L3 16M3 21v-5h5"/></svg>),
};

/* ---------------- Wordmark ---------------- */
function Wordmark({ size = "", onDark = false, onClick }) {
  return (
    <span className={`wordmark ${size} ${onDark ? "on-dark" : ""}`} onClick={onClick} style={onClick ? { cursor: "pointer" } : null}>
      <span className="wm-text">Clean<span className="deen">Deen</span></span>
    </span>
  );
}

/* ---------------- Severity chip ---------------- */
function SevChip({ level, small }) {
  const s = window.CD.SEVERITY[level];
  return <span className={`chip ${s.cls}`} style={small ? { padding: "3px 9px", fontSize: 11.5 } : null}><span className="dot" />{s.label}</span>;
}

/* ---------------- Score ring ---------------- */
function ScoreRing({ value, size = 64, label }) {
  const r = (size - 8) / 2, c = 2 * Math.PI * r;
  const off = c * (1 - value / 100);
  const col = value >= 92 ? "var(--sev-clean)" : value >= 80 ? "var(--sev-med)" : "var(--sev-high)";
  return (
    <div style={{ position: "relative", width: size, height: size }}>
      <svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="var(--surface-sunk)" strokeWidth="6" />
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={col} strokeWidth="6" strokeLinecap="round" strokeDasharray={c} strokeDashoffset={off} style={{ transition: "stroke-dashoffset .7s ease" }} />
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center", textAlign: "center", lineHeight: 1 }}>
        <div>
          <div style={{ fontFamily: "var(--serif)", fontSize: size * 0.32, fontWeight: 600, color: "var(--forest-900)" }}>{value}</div>
          {label && <div style={{ fontSize: 9, color: "var(--ink-mute)", marginTop: 2 }}>{label}</div>}
        </div>
      </div>
    </div>
  );
}

/* ---------------- Faux-3D isometric room ---------------- */
function IsoRoom({ room, phase, selectedId, onSelect, interactive = false, onInteract, expandable = false, infoOnSelect = false }) {
  const sizeFor = { high: 96, med: 70, low: 50, clean: 28 };
  const [spin, setSpin] = React.useState(-46);
  const [tilt, setTilt] = React.useState(56);
  const [grabbing, setGrabbing] = React.useState(false);
  const [fs, setFs] = React.useState(false);
  const [info, setInfo] = React.useState(null);
  const stageRef = React.useRef(null);
  const drag = React.useRef({ active: false, x: 0, y: 0, spin: -46, tilt: 56, moved: false, capturing: false });
  const engaged = React.useRef(false);

  // Close fullscreen on Esc
  React.useEffect(() => {
    if (!fs) return;
    const onKey = (e) => { if (e.key === "Escape") setFs(false); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [fs]);

  // Auto-rotate turntable until the user interacts (then it stays put)
  React.useEffect(() => {
    if (!interactive) return;
    let last = performance.now(), raf;
    const tick = (now) => {
      const dt = Math.min(now - last, 60); last = now;
      if (!engaged.current && !drag.current.active) setSpin(s => s - dt * 0.011);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [interactive]);

  function onDown(e) {
    if (!interactive) return;
    engaged.current = true; // first touch stops the auto-rotate for good
    drag.current = { active: true, x: e.clientX, y: e.clientY, spin, tilt, moved: false, capturing: false };
  }
  function onMove(e) {
    if (!interactive || !drag.current.active) return;
    const dx = e.clientX - drag.current.x, dy = e.clientY - drag.current.y;
    // Only treat as a drag (and capture the pointer) once it actually moves,
    // so a plain click still reaches the blob underneath.
    if (!drag.current.moved && Math.abs(dx) + Math.abs(dy) > 4) {
      drag.current.moved = true;
      setGrabbing(true);
      try { stageRef.current.setPointerCapture(e.pointerId); drag.current.capturing = true; } catch (_) {}
    }
    if (drag.current.moved) {
      setSpin(drag.current.spin + dx * 0.45);
      setTilt(Math.max(26, Math.min(84, drag.current.tilt - dy * 0.28)));
    }
  }
  function onUp(e) {
    if (!interactive || !drag.current.active) return;
    const moved = drag.current.moved;
    if (drag.current.capturing) { try { stageRef.current.releasePointerCapture(e.pointerId); } catch (_) {} }
    drag.current.active = false; setGrabbing(false);
    if (moved && onInteract) onInteract();
  }

  function handleBlob(key, hs) {
    if (drag.current.moved) return;
    if (infoOnSelect) setInfo({ key, hs });
    if (onSelect) onSelect(key, hs);
  }

  function blob(hs, faceCls) {
    const lvl = phase === "before" ? hs.before : hs.after;
    const d = sizeFor[lvl];
    const key = `${faceCls}-${hs.id}`;
    const sel = selectedId === key || (info && info.key === key);
    const clickable = onSelect || infoOnSelect;
    return (
      <div
        key={key}
        role={clickable ? "button" : undefined}
        className={`iso-blob b-${lvl} ${sel ? "sel" : ""}`}
        style={{ left: `${hs.x}%`, top: `${hs.y}%`, width: d, height: d, cursor: clickable ? "pointer" : "inherit" }}
        onClick={() => handleBlob(key, hs)}
        title={window.CD.MICROBES[hs.microbe].short}
      >
        {lvl === "clean" && <Ic.check style={{ color: "white" }} />}
      </div>
    );
  }
  const floor = room.hotspots.filter(h => h.surface === "floor");
  const wallL = room.hotspots.filter(h => h.surface === "wallL");
  const wallR = room.hotspots.filter(h => h.surface === "wallR");

  const sceneStyle = interactive
    ? { transform: `rotateX(${tilt}deg) rotateZ(${spin}deg)`, transition: "none" }
    : null;

  const pop = info ? window.CD.MICROBES[info.hs.microbe] : null;
  const popLvl = info ? (phase === "before" ? info.hs.before : info.hs.after) : null;

  return (
    <div className={`iso-wrap ${fs ? "is-fs" : ""}`}>
      <div
        className="iso-stage"
        ref={stageRef}
        style={interactive ? { cursor: grabbing ? "grabbing" : "grab", touchAction: "none" } : null}
        onPointerDown={onDown}
        onPointerMove={onMove}
        onPointerUp={onUp}
        onPointerCancel={onUp}
      >
        <div className="iso-scene" style={sceneStyle}>
          <div className="iso-face iso-wall-left">{wallL.map(h => blob(h, "wallL"))}</div>
          <div className="iso-face iso-wall-back">{wallR.map(h => blob(h, "wallR"))}</div>
          <div className="iso-face iso-floor">{floor.map(h => blob(h, "floor"))}</div>
        </div>
        {interactive && <div className="iso-hint"><Ic.grid style={{ width: 13, height: 13 }} />Drag to rotate{infoOnSelect ? " · tap a spot" : ""}</div>}
      </div>

      {expandable && (
        <button className="iso-expand" onClick={() => setFs(f => !f)} title={fs ? "Exit full screen" : "Full screen"}>
          {fs ? <Ic.x style={{ width: 17, height: 17 }} /> : <Ic.expand style={{ width: 17, height: 17 }} />}
        </button>
      )}

      {info && pop && (
        <div className="iso-pop fade-in">
          <button className="iso-pop-x" onClick={() => setInfo(null)} aria-label="Close"><Ic.x style={{ width: 14, height: 14 }} /></button>
          <div className="iso-pop-top">
            <SevChip level={popLvl} small />
            <span className="iso-pop-detail">{info.hs.detail}</span>
          </div>
          <div className="iso-pop-name">{pop.name}</div>
          <p className="iso-pop-harm"><strong>What it does:</strong> {pop.harm}</p>
        </div>
      )}
    </div>
  );
}

function IsoLegend() {
  return (
    <div className="iso-legend">
      <span className="lg"><span className="sw" style={{ background: "var(--sev-high)" }} />High load</span>
      <span className="lg"><span className="sw" style={{ background: "oklch(0.71 0.13 70)" }} />Moderate</span>
      <span className="lg"><span className="sw" style={{ background: "oklch(0.66 0.09 130)" }} />Low</span>
      <span className="lg"><span className="sw" style={{ background: "var(--sev-clean)" }} />Cleared</span>
    </div>
  );
}

Object.assign(window, { Ic, Wordmark, SevChip, ScoreRing, IsoRoom, IsoLegend });
