/* Terminals: a generic shell that boots an (original-styled) AI agent session via `claude`. */

const wait = (ms) => new Promise((r) => setTimeout(r, ms));
let _lid = 0;
const lid = () => ++_lid;

function Terminal({ kind, seed }) {
  const [lines, setLines] = useState(seed.lines);
  const [mode, setMode] = useState(seed.mode);
  const [input, setInput] = useState("");
  const [busy, setBusy] = useState(false);
  const bodyRef = useRef(null);
  const inputRef = useRef(null);
  const hist = useRef([]);
  const histIx = useRef(-1);

  useEffect(() => {
    const el = bodyRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [lines, busy]);

  useEffect(() => {
    if (kind !== "agent") return;
    const h = (e) => {
      if (mode !== "agent") { pushMany(bootAgent()); setMode("agent"); }
      // bracketed-paste semantics: append as a new, UNSUBMITTED line; leave caret on a fresh line
      setInput((prev) => {
        const base = prev.replace(/\n+$/, "");
        return (base ? base + "\n" : "") + e.detail + "\n";
      });
      requestAnimationFrame(() => {
        const el = inputRef.current;
        if (el) { el.focus(); const v = el.value.length; el.setSelectionRange(v, v); }
      });
    };
    window.addEventListener("agentPaste", h);
    return () => window.removeEventListener("agentPaste", h);
  }, [kind, mode]);

  const push = (line) => setLines((p) => [...p, { id: lid(), ...line }]);
  const pushMany = (arr) => setLines((p) => [...p, ...arr.map((l) => ({ id: lid(), ...l }))]);

  async function runAgent(prompt) {
    setBusy(true);
    push({ kind: "agent-think", html: `<span class="ag">●</span> <span class="dim">thinking…</span>` });
    await wait(520);
    setLines((p) => p.slice(0, -1)); // drop the thinking line
    push({ kind: "t", cls: "", html: `<span class="ag">●</span> I'll take a look at the relevant files first.` });
    await wait(380);
    push({ kind: "t", cls: "dim", html: `  <span class="tool">read</span> <span class="fp">src/Http/Controller/UserController.php</span>` });
    await wait(300);
    push({ kind: "t", cls: "dim", html: `  <span class="tool">grep</span> <span class="dim">"balanceFor" → 2 matches</span>` });
    await wait(420);
    push({ kind: "t", cls: "", html: `<span class="ag">●</span> Adding the field and a fillable allow-list. Editing now.` });
    await wait(360);
    push({
      kind: "card", ch: `edit · src/Http/Controller/UserController.php`,
      rows: [
        { cls: "del", t: "-   'plan' => $user->plan," },
        { cls: "add", t: "+   'plan' => $user->plan->value," },
        { cls: "add", t: "+   'currency' => $user->currency," },
      ],
    });
    await wait(450);
    push({ kind: "t", cls: "ok", html: `<span class="ag">●</span> <span class="ok">Done.</span> Updated <span class="fp">UserController.php</span>. Run tests with <span class="fp">composer test</span>.` });
    setBusy(false);
    requestAnimationFrame(() => inputRef.current && inputRef.current.focus());
  }

  function shell(cmd) {
    const [c, ...rest] = cmd.split(/\s+/);
    const arg = rest.join(" ");
    switch (c) {
      case "": return;
      case "claude":
        pushMany(bootAgent());
        setMode("agent");
        return;
      case "clear": setLines([]); return;
      case "help":
        pushMany([
          { kind: "t", cls: "dim", html: "commands: <span style='color:var(--fg-1)'>claude</span>  ls  pwd  cat &lt;file&gt;  git status  git diff  echo  clear" },
        ]); return;
      case "pwd": push({ kind: "t", html: "/Users/dev/console" }); return;
      case "ls":
        push({ kind: "t", html: "<span class='fp'>config</span>  <span class='fp'>public</span>  <span class='fp'>scripts</span>  <span class='fp'>src</span>  <span class='fp'>templates</span>  composer.json  package.json  README.md" });
        return;
      case "echo": push({ kind: "t", html: HL.escapeHtml(arg) }); return;
      case "git":
        if (rest[0] === "status") {
          pushMany([
            { kind: "t", cls: "dim", html: `On branch <span style='color:var(--fg-1)'>${PROJECT.branch}</span>` },
            { kind: "t", cls: "dim", html: "Changes to be committed:" },
            ...PROJECT.changes.map((ch) => ({
              kind: "t",
              html: `  <span style="color:var(--${ch.status === 'A' ? 'add' : ch.status === 'D' ? 'del' : 'mod'})">${ch.status === 'A' ? 'new file' : ch.status === 'D' ? 'deleted ' : 'modified'}</span>   <span class="fp">${ch.path}</span>`,
            })),
          ]);
        } else if (rest[0] === "diff") {
          pushMany([
            { kind: "t", cls: "dim", html: "diff --git a/public/assets/app.js b/public/assets/app.js" },
            { kind: "t", cls: "err", html: "-  const res = await fetch('/api/session');" },
            { kind: "t", cls: "ok", html: "+  const res = await fetch('/api/session', { credentials: 'include' });" },
          ]);
        } else push({ kind: "t", cls: "dim", html: `git: '${rest[0] || ""}' is not handled in this demo` });
        return;
      case "cat": {
        const f = PROJECT.files[arg] || Object.entries(PROJECT.files).find(([p]) => p.endsWith(arg))?.[1];
        if (!f) { push({ kind: "t", cls: "err", html: `cat: ${HL.escapeHtml(arg)}: No such file` }); return; }
        pushMany(f.replace(/\n$/, "").split("\n").slice(0, 24).map((l) => ({ kind: "t", cls: "dim", html: HL.escapeHtml(l) || "&nbsp;" })));
        return;
      }
      default: push({ kind: "t", cls: "err", html: `${HL.escapeHtml(c)}: command not found` });
    }
  }

  function submit() {
    const cmd = input.trim();
    if (busy) return;
    if (cmd) { hist.current.unshift(cmd); }
    histIx.current = -1;
    if (mode === "agent") {
      if (cmd === "/exit" || cmd === "exit") {
        push({ kind: "t", cls: "dim", html: "<span class='ag'>●</span> Session ended." });
        setMode("shell"); setInput(""); return;
      }
      if (cmd === "/clear" || cmd === "clear") { setLines([]); setInput(""); return; }
      push({ kind: "t", html: `<span class="ip ag">&gt;</span> ${HL.escapeHtml(cmd).replace(/\n/g, "<br>&nbsp;&nbsp;") || "&nbsp;"}` });
      setInput("");
      if (cmd) runAgent(cmd);
      return;
    }
    push({ kind: "t", html: `<span class="pfx">console</span> <span class="dim">%</span> ${HL.escapeHtml(cmd) || "&nbsp;"}` });
    setInput("");
    shell(cmd);
  }

  function onKey(e) {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); submit(); return; }
    if ((e.key === "ArrowUp" || e.key === "ArrowDown") && !input.includes("\n")) {
      e.preventDefault();
      if (e.key === "ArrowUp") { if (hist.current.length) { histIx.current = Math.min(histIx.current + 1, hist.current.length - 1); setInput(hist.current[histIx.current]); } }
      else { if (histIx.current > 0) { histIx.current--; setInput(hist.current[histIx.current]); } else { histIx.current = -1; setInput(""); } }
    }
  }

  const live = mode === "agent";
  return (
    <div className="term-pane" style={{ flex: 1, minHeight: 0 }} onMouseDown={() => inputRef.current && inputRef.current.focus()}>
      <div className="term-head">
        <span className={"dot" + (live ? " live" : "")}></span>
        <span className="lbl">{live ? "claude" : kind === "agent" ? "claude" : "zsh"}</span>
        <span className="tag">{live ? "agent session" : "— bash · ~/console"}</span>
      </div>
      <div className="term-body" ref={bodyRef}>
        {lines.map((l) => {
          if (l.kind === "card") {
            return (
              <div key={l.id} className="term-card">
                <div className="ch">{Icon.diff({ width: 11, height: 11 })}<span>{l.ch}</span></div>
                {l.rows.map((r, i) => (<div key={i} className={r.cls}>{r.t}</div>))}
              </div>
            );
          }
          if (l.kind === "welcome") {
            return (
              <div key={l.id} className="term-card" style={{ borderColor: "var(--accent-line)" }}>
                <div style={{ color: "var(--accent)", fontWeight: 600 }}>◇ agent session</div>
                <div className="dim" style={{ color: "var(--fg-2)", marginTop: 3 }}>{l.text}</div>
              </div>
            );
          }
          return <div key={l.id} className={"tline " + (l.cls || "")} dangerouslySetInnerHTML={{ __html: l.html }} />;
        })}
        {busy && <div className="tline dim"><span className="ag" style={{ color: "#c98bdb" }}>●</span> working<span className="cursor-blink" /></div>}
        {!busy && (
          <div className="term-input">
            <span className={"ip" + (live ? " ag" : "")}>{live ? ">" : <span className="pfx">console <span style={{ color: "var(--fg-3)" }}>%</span></span>}</span>
            <textarea ref={inputRef} className="term-ta" value={input} spellCheck={false} autoComplete="off"
              rows={Math.min(8, Math.max(1, input.split("\n").length))}
              placeholder={live ? "Ask the agent to change something…" : kind === "agent" ? "type 'claude' to start a session" : ""}
              onChange={(e) => setInput(e.target.value)} onKeyDown={onKey} />
          </div>
        )}
      </div>
    </div>
  );
}

function bootAgent() {
  return [
    { kind: "welcome", text: "model: opus · cwd: ~/console · branch: " + PROJECT.branch + " · type /exit to leave" },
    { kind: "t", cls: "dim", html: "  I can read, search and edit files in this project. Describe a change to get started." },
  ];
}

function agentSeed() {
  return {
    mode: "agent",
    lines: [
      { id: lid(), kind: "t", html: `<span class="pfx">console</span> <span class="dim">%</span> claude` },
      { id: lid(), kind: "welcome", text: "model: opus · cwd: ~/console · branch: " + PROJECT.branch + " · type /exit to leave" },
      { id: lid(), kind: "t", html: `<span class="ip ag">&gt;</span> add currency + name to the user payload and gate the fillable fields` },
      { id: lid(), kind: "t", html: `<span class="ag">●</span> Read <span class="fp">UserController.php</span>, edited the response array and <span class="fp">onlyFillable()</span>.` },
      {
        id: lid(), kind: "card", ch: "edit · src/Http/Controller/UserController.php",
        rows: [
          { cls: "del", t: "-   'plan' => $user->plan," },
          { cls: "add", t: "+   'plan' => $user->plan->value," },
          { cls: "add", t: "+   'name' => $user->name," },
        ],
      },
      { id: lid(), kind: "t", cls: "ok", html: `<span class="ag">●</span> <span class="ok">Done</span> — updated <span class="fp">UserController.php</span>. Anything else?` },
    ],
  };
}

function shellSeed() {
  return {
    mode: "shell",
    lines: [
      { id: lid(), kind: "t", html: `<span class="pfx">console</span> <span class="dim">%</span> git status -s` },
      ...PROJECT.changes.map((c) => ({
        id: lid(), kind: "t",
        html: `<span style="color:var(--${c.status === 'A' ? 'add' : c.status === 'D' ? 'del' : 'mod'})">${c.status} </span> <span class="fp">${c.path}</span>`,
      })),
    ],
  };
}

Object.assign(window, { Terminal, agentSeed, shellSeed });
