/* InkryptSci — main app */
const { useState, useEffect, useRef, useMemo, useCallback } = React;

const TOTAL = window.__INKRYPT_TOTAL_MS;
const SCENES = window.__INKRYPT_SCENES;
const BANNER = window.__INKRYPT_BANNER;

const SPEEDS = { "0.5x": 0.5, "1x": 1, "2x": 2, "4x": 4, "instant": 16 };

// ---------- inline icon ----------
function MarkLogo() {
  return (
    <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
      <path d="M3 18 V14 L7 14 V10 L11 10 V6 L15 6 V14 L19 14 V18 Z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" fill="none"/>
      <circle cx="11" cy="11" r="9.2" stroke="currentColor" strokeWidth="0.8" strokeDasharray="2 2" opacity="0.55"/>
    </svg>
  );
}

// ---------- line renderers ----------
function CmdLine({ text }) {
  // highlight flags --x and "strings" and the leading prompt
  const lines = text.split("\n");
  return (
    <div className="ln cmd">
      {lines.map((ln, i) => {
        const isCont = ln.trimStart().startsWith("--") || ln.endsWith("\\");
        const parts = [];
        if (i === 0) parts.push(<span key="p" className="prompt">▸</span>);
        else parts.push(<span key="p" className="prompt" style={{opacity:0.3}}>  </span>);
        // colorize flags
        const tokens = ln.split(/(\s+--[a-z-]+|\s+0x[a-fA-F0-9]+|\s+\S+\.[a-z,/]+)/);
        tokens.forEach((tok, j) => {
          if (/^\s+--[a-z-]+$/.test(tok)) parts.push(<span key={j} className="flag">{tok}</span>);
          else if (/^\s+0x[a-fA-F0-9]+$/.test(tok)) parts.push(<span key={j} className="str">{tok}</span>);
          else parts.push(<span key={j}>{tok}</span>);
        });
        return <div key={i}>{parts}</div>;
      })}
    </div>
  );
}

function ToolLine({ name, arg, status, note }) {
  const sym = status === "ok" ? "✓" : status === "warn" ? "!" : status === "hit" ? "✕" : "·";
  return (
    <div className="ln tool fade-in">
      <span className="icon">{sym}</span>
      <span><span className="name">{name}</span> <span style={{color:"var(--ink-mute)"}}>{arg}</span> <span style={{color:"var(--ink-ghost)"}}>·</span> <span style={{color:"var(--ink-dim)"}}>{note}</span></span>
      <span className={"status " + status}>{status}</span>
    </div>
  );
}

function PlanBox({ steps, ticks }) {
  return (
    <div className="plan fade-in">
      <div className="plan-head">◢ red-team plan · {steps.length} stages</div>
      {steps.map((s, i) => {
        const status = ticks[i] || "queued";
        const stat = status === "queued" ? "queued"
          : status === "run" ? "running…"
          : status === "done" ? "done"
          : "★ HIT";
        return (
          <div key={i} className={"plan-step " + status}>
            <span className="num">{String(i+1).padStart(2,"0")}</span>
            <span className="label">{s}</span>
            <span className="stat">{stat}</span>
          </div>
        );
      })}
    </div>
  );
}

function FindingCard({ payload }) {
  const sevClass = payload.severity;
  return (
    <div className={"finding pop-in " + sevClass}>
      <div className="finding-head">
        <span className={"chip " + sevClass}>{payload.severity}</span>
        <span className="finding-id">{payload.id}</span>
        <span className="finding-title">
          {payload.glitch
            ? <span className="glitch" data-text={payload.title}>{payload.title}</span>
            : payload.title}
        </span>
        <span className="finding-loc"><span className="file">{payload.file}</span> · {payload.line}</span>
      </div>
      <div className="finding-body">{payload.summary}</div>
      <div style={{display:"flex", gap:6, flexWrap:"wrap"}}>
        {(payload.tags || []).map(t => <span key={t} className="chip info">{t}</span>)}
        {payload.hasPOC && <span className="chip" style={{color:"var(--phosphor)", borderColor:"rgba(74,227,139,0.3)"}}>POC ATTACHED</span>}
      </div>
    </div>
  );
}

function CodeBlock({ payload }) {
  return (
    <div className="code fade-in" style={{margin:"10px 0"}}>
      <div className="code-head">
        <span><span className="lang">{payload.lang}</span> · {payload.file}</span>
        <span>{payload.lines}</span>
      </div>
      {payload.body.map((row, i) => {
        const isHL = row[0] === "hl";
        const parts = isHL ? row.slice(1) : row.slice(row[0] === "c" || row[0] === "n" ? 1 : (row[0] === "" ? 1 : 0));
        // simpler: handle row[0] as class hint
        let cls = null;
        let rest;
        if (row[0] === "hl") { cls = "hl"; rest = row.slice(1); }
        else if (row[0] === "c") { cls = "c"; rest = row.slice(1); }
        else if (row[0] === "n") { cls = "n"; rest = row.slice(1); }
        else if (row[0] === "") { rest = row.slice(1); }
        else { rest = row; }
        const rendered = rest.map((tok, j) => {
          if (typeof tok === "string") return <span key={j}>{tok}</span>;
          return <span key={j} className={tok[0]}>{tok[1]}</span>;
        });
        return <span key={i} className={cls || ""} style={{display:"block"}}>{rendered}</span>;
      })}
    </div>
  );
}

function SummaryTable({ payload }) {
  return (
    <div className="summary fade-in">
      <table>
        <thead>
          <tr>
            <th>Sev</th><th>ID</th><th>Title</th><th style={{textAlign:"right"}}>Location</th>
          </tr>
        </thead>
        <tbody>
          {payload.rows.map((r, i) => (
            <tr key={i}>
              <td className="sev"><span className={"chip " + r.sev}>{r.sev}</span></td>
              <td className="id">{r.id}</td>
              <td>{r.title}</td>
              <td style={{textAlign:"right", color:"var(--ink-mute)"}}>{r.loc}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

// ---------- main app ----------
function App() {
  const [t, setTweak] = useTweaks(window.__TWEAK_DEFAULTS);
  const [elapsed, setElapsed] = useState(0);
  const [playing, setPlaying] = useState(true);
  const rafRef = useRef(null);
  const lastRef = useRef(null);
  const bodyRef = useRef(null);

  const speedMult = SPEEDS[t.speed] || 1;

  // RAF loop
  useEffect(() => {
    if (!playing) return;
    function step(now) {
      if (lastRef.current == null) lastRef.current = now;
      const dt = now - lastRef.current;
      lastRef.current = now;
      setElapsed((e) => {
        const next = Math.min(TOTAL, e + dt * speedMult);
        if (next >= TOTAL) setPlaying(false);
        return next;
      });
      rafRef.current = requestAnimationFrame(step);
    }
    rafRef.current = requestAnimationFrame(step);
    return () => {
      cancelAnimationFrame(rafRef.current);
      lastRef.current = null;
    };
  }, [playing, speedMult]);

  // auto-scroll terminal
  useEffect(() => {
    if (!bodyRef.current) return;
    bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
  }, [elapsed]);

  // visible scenes
  const { visible, ticks, sevCounts, scanning } = useMemo(() => {
    const v = [];
    const tk = {};
    const sc = { crit: 0, high: 0, med: 0, low: 0 };
    let lastToolT = -9999;
    SCENES.forEach((s) => {
      if (s.t > elapsed) return;
      if (s.k === "plan-tick") {
        tk[s.step] = s.status;
        return;
      }
      v.push(s);
      if (s.k === "tool") lastToolT = s.t;
      if (s.k === "finding") sc[s.payload.severity] = (sc[s.payload.severity] || 0) + 1;
    });
    const scanning = (elapsed - lastToolT) < 1200 && elapsed < TOTAL - 1000;
    return { visible: v, ticks: tk, sevCounts: sc, scanning };
  }, [elapsed]);

  // controls
  const restart = useCallback(() => { setElapsed(0); setPlaying(true); }, []);
  const togglePlay = useCallback(() => {
    setPlaying((p) => {
      if (!p && elapsed >= TOTAL) { setElapsed(0); return true; }
      return !p;
    });
  }, [elapsed]);
  const skip = useCallback(() => { setElapsed(TOTAL); setPlaying(false); }, []);

  // keyboard
  useEffect(() => {
    function onKey(e) {
      if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return;
      if (e.code === "Space") { e.preventDefault(); togglePlay(); }
      if (e.code === "KeyR") restart();
      if (e.code === "KeyE") skip();
    }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [togglePlay, restart, skip]);

  const totalFindings = sevCounts.crit + sevCounts.high + sevCounts.med + sevCounts.low;
  const progress = Math.min(100, (elapsed / TOTAL) * 100);

  return (
    <>
      {/* topbar */}
      <div className="topbar">
        <div className="brand">
          <span className="brand-mark"><MarkLogo /></span>
          <span>InkryptSci</span>
          <span className="sub">BlueSci · red-team for Web3</span>
        </div>
        <div className="crumb">
          <span><span className="led"></span> connected · 9 chains</span>
          <span>session 9d4f·c1a2</span>
          <span>{elapsed >= TOTAL ? "complete" : (playing ? "live" : "paused")}</span>
        </div>
      </div>

      <div className="stage">
        {/* TERMINAL */}
        <div className="terminal">
          <div className="term-head">
            <span className="term-dot r"></span>
            <span className="term-dot y"></span>
            <span className="term-dot g"></span>
            <span className="term-title">audit@neuronyx ▸ inkrypt scan</span>
            <span className="term-meta">
              <span><span className="k">chain</span> ethereum</span>
              <span><span className="k">depth</span> aggressive</span>
              <span><span className="k">redteam</span> on</span>
            </span>
          </div>
          <div ref={bodyRef} className={"term-body " + (scanning ? "scanning" : "")} style={{maxHeight: "78vh", overflowY: "auto"}}>
            <div className="scan-beam"></div>

            {visible.map((s, i) => {
              const key = `${i}-${s.t}`;
              switch (s.k) {
                case "banner":
                  return (
                    <div key={key} className="banner-block fade-in">
                      <pre className="ln banner" style={{margin:0, fontSize: "10.5px"}}>{BANNER}</pre>
                      <div className="ln banner-sub" style={{marginTop:"10px"}}>
                        BlueSci · AI red-team for Web3 · v0.7.2-rc
                      </div>
                    </div>
                  );
                case "system":  return <div key={key} className="ln system fade-in">{s.text}</div>;
                case "muted":   return <div key={key} className="ln muted fade-in">{s.text}</div>;
                case "info":    return <div key={key} className="ln info fade-in">{s.text}</div>;
                case "ok":      return <div key={key} className="ln ok fade-in">  <span style={{color:"var(--phosphor-dim)"}}>[ok]</span>   {s.text}</div>;
                case "warn":    return <div key={key} className="ln warn fade-in">[warn] {s.text}</div>;
                case "hit":     return <div key={key} className="ln hit fade-in">[hit]  {s.text}</div>;
                case "agent":   return t.aiVerbosity === "silent" ? null : <div key={key} className="ln agent fade-in">{s.text}</div>;
                case "thought": return t.aiVerbosity !== "detailed" ? null : <div key={key} className="ln thought fade-in">{s.text}</div>;
                case "cmd":     return <CmdLine key={key} text={s.text} />;
                case "cmd-empty": return (
                  <div key={key} className="ln cmd"><span className="prompt">▸</span><span className="cursor"></span></div>
                );
                case "tool":    return <ToolLine key={key} {...s} />;
                case "plan":    return <PlanBox key={key} steps={s.payload} ticks={ticks} />;
                case "finding": return <FindingCard key={key} payload={s.payload} />;
                case "code":    return <CodeBlock key={key} payload={s.payload} />;
                case "summary": return <SummaryTable key={key} payload={s.payload} />;
                case "space":   return <div key={key} style={{height: "8px"}}></div>;
                default: return null;
              }
            })}

            {elapsed < TOTAL && playing && (
              <span className="cursor" style={{verticalAlign:"middle"}}></span>
            )}
          </div>
        </div>

        {/* SIDE RAIL */}
        {t.showSidebar && (
          <div className="siderail">
            <div className="panel">
              <div className="panel-head"><span className="dot"></span> target</div>
              <div className="meta-row"><span className="meta-k">contract</span><span className="meta-v">MetaVault</span></div>
              <div className="meta-row"><span className="meta-k">proxy</span><span className="meta-v">0x1f90…6326</span></div>
              <div className="meta-row"><span className="meta-k">impl</span><span className="meta-v">0x9c8b…f102</span></div>
              <div className="meta-row"><span className="meta-k">chain</span><span className="meta-v cyan">ethereum</span></div>
              <div className="meta-row"><span className="meta-k">tvl</span><span className="meta-v amber">$4.18M</span></div>
              <div className="meta-row"><span className="meta-k">compiler</span><span className="meta-v">solc 0.8.21</span></div>
              <div className="meta-row"><span className="meta-k">verified</span><span className="meta-v green">etherscan · sourcify</span></div>
            </div>

            <div className="panel">
              <div className="panel-head"><span className="dot"></span> findings · {totalFindings}</div>
              <div className="sev-bars">
                <div className="sev-bar crit">
                  <span className="lbl">Critical</span>
                  <div className="trk"><div className="fil" style={{width: `${Math.min(100, sevCounts.crit * 100)}%`}}></div></div>
                  <span className="num">{sevCounts.crit}</span>
                </div>
                <div className="sev-bar high">
                  <span className="lbl">High</span>
                  <div className="trk"><div className="fil" style={{width: `${Math.min(100, sevCounts.high * 50)}%`}}></div></div>
                  <span className="num">{sevCounts.high}</span>
                </div>
                <div className="sev-bar med">
                  <span className="lbl">Medium</span>
                  <div className="trk"><div className="fil" style={{width: `${Math.min(100, sevCounts.med * 33)}%`}}></div></div>
                  <span className="num">{sevCounts.med}</span>
                </div>
                <div className="sev-bar low">
                  <span className="lbl">Low / Info</span>
                  <div className="trk"><div className="fil" style={{width: `${Math.min(100, sevCounts.low * 25)}%`}}></div></div>
                  <span className="num">{sevCounts.low}</span>
                </div>
              </div>
            </div>

            <div className="panel">
              <div className="panel-head"><span className="dot"></span> engine</div>
              <div className="meta-row"><span className="meta-k">recon</span><span className="meta-v green">v1.2</span></div>
              <div className="meta-row"><span className="meta-k">symex</span><span className="meta-v green">v0.9</span></div>
              <div className="meta-row"><span className="meta-k">exploit</span><span className="meta-v green">v0.6</span></div>
              <div className="meta-row"><span className="meta-k">judge</span><span className="meta-v green">v1.0</span></div>
              <div className="meta-row"><span className="meta-k">sig db</span><span className="meta-v">2026.05.11</span></div>
              <div className="meta-row"><span className="meta-k">paths</span><span className="meta-v">1,284</span></div>
              <div className="meta-row"><span className="meta-k">fuzz</span><span className="meta-v">240k cases</span></div>
              <div className="meta-row"><span className="meta-k">credits</span><span className="meta-v amber">−612</span></div>
            </div>

            <div className="panel">
              <div className="panel-head"><span className="dot"></span> chains supported</div>
              <div style={{display:"flex", flexWrap:"wrap", gap:"6px"}}>
                {["ethereum","arbitrum","optimism","base","polygon","bnb","solana","sui","aptos"].map(c =>
                  <span key={c} className="chip">{c}</span>
                )}
              </div>
            </div>

            <div className="panel" style={{borderColor:"rgba(255,77,94,0.2)"}}>
              <div className="panel-head" style={{color:"var(--crimson)"}}>
                <span className="dot" style={{background:"var(--crimson)", boxShadow:"0 0 6px var(--crimson)"}}></span>
                ⚠ predicted impact
              </div>
              <div style={{fontSize:"22px", fontFamily:"var(--display)", color:"var(--crimson)", letterSpacing:"-0.02em"}}>
                ≈ $4.21M
              </div>
              <div style={{fontSize:"11px", color:"var(--ink-mute)", letterSpacing:"0.06em", marginTop:"4px"}}>
                drainable in 1 tx · 1 critical path
              </div>
            </div>
          </div>
        )}
      </div>

      {/* TWEAKS PANEL */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="Playback">
          <TweakRadio label="Speed" value={t.speed} options={["0.5x","1x","2x","4x","instant"]} onChange={(v)=>setTweak("speed", v)} />
          <TweakSelect label="AI verbosity" value={t.aiVerbosity} options={[
            {value:"silent",   label:"Silent · output only"},
            {value:"balanced", label:"Balanced · narrate + plan"},
            {value:"detailed", label:"Detailed · thoughts visible"}
          ]} onChange={(v)=>setTweak("aiVerbosity", v)} />
        </TweakSection>
        <TweakSection label="Display">
          <TweakToggle label="Side rail" value={t.showSidebar} onChange={(v)=>setTweak("showSidebar", v)} />
          <TweakToggle label="Scanlines overlay" value={t.showScanlines} onChange={(v)=>{
            setTweak("showScanlines", v);
            document.body.style.setProperty("--scanline-opacity", v ? "1" : "0");
          }} />
          <TweakRadio label="Severity scheme" value={t.severityScheme} options={["standard","minimal"]} onChange={(v)=>{
            setTweak("severityScheme", v);
            const root = document.documentElement;
            if (v === "minimal") {
              root.style.setProperty("--crimson",  "#D4A537");
              root.style.setProperty("--amber",    "#B88A2F");
              root.style.setProperty("--yellow",   "#8E6B22");
              root.style.setProperty("--electric", "#5A4416");
            } else {
              root.style.removeProperty("--crimson");
              root.style.removeProperty("--amber");
              root.style.removeProperty("--yellow");
              root.style.removeProperty("--electric");
            }
          }} />
        </TweakSection>
        <TweakSection label="Aesthetic">
          <TweakRadio label="Accent" value={t.accent} options={["phosphor","cyan","magenta"]} onChange={(v)=>{
            setTweak("accent", v);
            const root = document.documentElement;
            const map = { phosphor:"#4AE38B", cyan:"#45D4FF", magenta:"#E879F9" };
            root.style.setProperty("--phosphor", map[v]);
          }} />
        </TweakSection>
      </TweaksPanel>

      {/* CONTROLS */}
      <div className="controls">
        <button className={"ctl primary"} onClick={togglePlay}>
          {elapsed >= TOTAL ? "↻ Replay" : (playing ? "❚❚ Pause" : "▶ Play")}
          <span className="key">SPC</span>
        </button>
        <div className="ctl-sep"></div>
        <button className="ctl" onClick={restart}>↺ Restart<span className="key">R</span></button>
        <button className="ctl" onClick={skip}>⤓ End<span className="key">E</span></button>
        <div className="ctl-sep"></div>
        <div className="progress-rail"><div className="progress-fill" style={{width: progress+"%"}}></div></div>
        <span className="ctl" style={{color: progress >= 100 ? "var(--phosphor)" : "var(--ink-dim)"}}>{Math.floor(progress)}%</span>
      </div>
    </>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
