/**
 * In-page console agent — the freeform ▲ editor. The operator describes a change
 * on any page; the agent (Read/Write/Edit/Glob/Grep/WebSearch, no Bash) edits the
 * working tree; the host builds an isolated preview; publish/discard promote or
 * revert. Owns: /run, /stream (SSE), /publish, /rebuild, /discard.
 */

let H; // the host, set in register()

function buildPrompt({ message, page, selections }) {
  const parts = [];
  parts.push(
    "You are the editing agent for the Carlos Arias website (an Astro + Tailwind v4 static site).",
    "Make ONLY the change the operator asks for. Keep everything on-brand:",
    "the brand guide is brand/BRAND.md and the design tokens are in app/src/styles.css",
    "(sumi-e — ink on washi paper, one vermillion seal, restraint, near-square radii).",
    "Do NOT run builds or git commands — the console builds and publishes for you.",
    "",
  );
  if (page && page !== "/") {
    parts.push(`The operator is on the page: ${page}`);
    parts.push("Its source is almost certainly under app/src/pages (find it with Glob/Grep).", "");
  } else if (page === "/") {
    parts.push("The operator is on the homepage (app/src/pages/index.astro).", "");
  }
  if (Array.isArray(selections) && selections.length) {
    parts.push("They selected these element(s) on the page:");
    for (const s of selections) {
      const tag = s.tag || s.selector || "element";
      parts.push(`- <${tag}>${s.text ? ` — "${String(s.text).slice(0, 80)}"` : ""}${s.comment ? ` — note: ${s.comment}` : ""}`);
    }
    parts.push("");
  }
  parts.push("Request:", message);
  return parts.join("\n");
}

function shortPath(p) {
  if (!p) return "";
  return String(p).replace(H.REPO + "/", "");
}
function handleStreamMsg(job, msg) {
  if (msg.session_id) job.sessionId = msg.session_id;
  const content = msg?.message?.content;
  if (Array.isArray(content)) {
    for (const block of content) {
      if (block.type === "text" && block.text) H.emit(job, { kind: "text", text: block.text });
      else if (block.type === "tool_use") {
        const label = block.name === "Edit" || block.name === "Write"
          ? `${block.name} ${shortPath(block.input?.file_path)}`
          : block.name;
        H.emit(job, { kind: "tool", text: label });
      }
    }
  }
  if (msg.type === "result" && typeof msg.result === "string" && msg.result.trim()) {
    H.emit(job, { kind: "text", text: msg.result.trim() });
  }
}

async function buildPreview(job) {
  H.emit(job, { kind: "tool", text: "building preview…" });
  const out = H.join("..", "public-preview", job.jobId);
  const base = `/_preview/${job.jobId}`;
  const { ok } = await H.build({ PREVIEW_OUT: out, PREVIEW_BASE: base });
  if (!ok) {
    H.emit(job, { type: "error", text: "The change broke the build, so it was not applied. Try rephrasing." });
    await H.git(["checkout", "--", ...H.gitScope]);
    H.emit(job, { type: "done" });
    job.done = true;
    return;
  }
  const target = job.page && job.page !== "/" ? job.page.replace(/^\//, "") : "";
  H.emit(job, { type: "preview", url: `${base}/${target}` });
  H.emit(job, { type: "done" });
  job.done = true;
}

function runAgent(job, { message, page, selections, model }) {
  const prompt = buildPrompt({ message, page, selections });
  H.runClaudeStream({
    prompt, tools: H.AGENT_TOOLS, model, resume: job.sessionId,
    onMessage: (msg) => handleStreamMsg(job, msg),
    onStderr: (t) => { const s = t.trim(); if (s) H.emit(job, { kind: "tool", text: s.slice(0, 200) }); },
    onClose: async (code) => {
      if (code !== 0) {
        H.emit(job, { type: "error", text: "The agent stopped unexpectedly. Nothing was changed." });
        H.emit(job, { type: "done" });
        job.done = true;
        H.state.busy = false;
        return;
      }
      await buildPreview(job);
      H.state.busy = false;
    },
    onError: (err) => {
      H.emit(job, { type: "error", text: `Could not start the agent: ${err.message}` });
      H.emit(job, { type: "done" });
      job.done = true;
      H.state.busy = false;
    },
  });
}

export function register(host) {
  H = host;

  host.route("POST", "/run", async (req, res) => {
    if (H.state.busy) return H.json(res, 429, { error: "A change is already in progress — let it finish." });
    const body = await H.readBody(req);
    const message = String(body.message || "").trim();
    if (!message) return H.json(res, 400, { error: "Say what you'd like changed." });
    const env = H.loadEnv();
    if (!env.CLAUDE_CODE_OAUTH_TOKEN) return H.json(res, 401, { error: "No Claude token set. Add one with the 🔑 button." });

    let job = body.conversationId && H.jobs.get(body.conversationId);
    if (job) { job.done = false; job.events = []; job.jobId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`; }
    else job = H.newJob(body.page);

    H.state.busy = true;
    runAgent(job, { message, page: body.page, selections: body.selections, model: body.model });
    H.json(res, 200, { conversationId: job.conversationId });
  });

  host.route("ANY", "/stream", (req, res, { url }) => {
    const id = url.searchParams.get("conversationId");
    const job = id && H.jobs.get(id);
    if (!job) return H.json(res, 404, { error: "unknown conversation" });
    res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
    for (const ev of job.events) res.write(`data: ${JSON.stringify(ev)}\n\n`);
    if (job.done) return res.end();
    job.clients.add(res);
    req.on("close", () => job.clients.delete(res));
  });

  host.route("POST", "/publish", async (req, res) => {
    if (H.state.busy) return H.json(res, 429, { error: "Busy — try again in a moment." });
    const { conversationId } = await H.readBody(req);
    const job = conversationId && H.jobs.get(conversationId);
    if (!job) return H.json(res, 404, { error: "Nothing to publish." });
    H.state.busy = true;
    const built = await H.build({});
    if (!built.ok) { H.state.busy = false; return H.json(res, 500, { error: "Build failed — not published." }); }
    await H.git(["add", "--", ...H.gitScope]);
    const summary = (job.page && job.page !== "/" ? job.page : "homepage");
    await H.git(["commit", "-q", "-m", `console: edit ${summary}\n\n[published via astroagent console]`]);
    H.rmSync(H.join(H.PREVIEW_DIR, job.jobId), { recursive: true, force: true });
    H.state.busy = false;
    H.json(res, 200, { ok: true, live: job.page || "/" });
  });

  // Rebuild the live site after DB-driven content edits (projects gallery, etc.).
  host.route("POST", "/rebuild", async (req, res) => {
    if (H.state.busy) return H.json(res, 429, { error: "Busy — try again in a moment." });
    H.state.busy = true;
    const built = await H.build({});
    if (!built.ok) { H.state.busy = false; return H.json(res, 500, { error: "Build failed." }); }
    await H.git(["add", "--", "app/public/media", ...H.gitScope]);
    await H.git(["commit", "-q", "-m", "console: content update"]); // ok if nothing to commit
    H.state.busy = false;
    H.json(res, 200, { ok: true });
  });

  host.route("POST", "/discard", async (req, res) => {
    if (H.state.busy) return H.json(res, 429, { error: "Busy — try again in a moment." });
    const { conversationId } = await H.readBody(req);
    const job = conversationId && H.jobs.get(conversationId);
    H.state.busy = true;
    await H.git(["checkout", "--", ...H.gitScope]);
    await H.git(["clean", "-fd", ...H.gitScope]);
    if (job) H.rmSync(H.join(H.PREVIEW_DIR, job.jobId), { recursive: true, force: true });
    H.state.busy = false;
    if (job) H.jobs.delete(job.conversationId);
    H.json(res, 200, { ok: true });
  });
}
