/**
 * Project builder — one-shot copy drafting and full structured-project authoring
 * for the admin's New Project form. Returns JSON to the admin UI (which persists
 * it via the PHP API); never mutates the repo. Owns: /draft, /build-project.
 */

let H;

const MEDIA_RE = /^\/media\/[\w.-]+$/;
const MAX_BUILD_IMAGES = 8;

function stripFence(t) {
  const m = String(t).match(/```(?:json)?\s*([\s\S]*?)```/);
  return (m ? m[1] : t).trim();
}

// Turn a rough draft + prompt into polished project copy. No tools.
async function draftCopy({ name, category, draft, prompt }) {
  const ask = [
    "You are writing copy for Carlos Arias's portfolio (carlosarias.co) — an Agentic AI & Automation Engineer who builds for law firms and service businesses.",
    "Voice: precise, understated, confident. No hype, no buzzwords, no exclamation marks. Write a project case study.",
    "",
    `Project name: ${name}`,
    category ? `Category: ${category}` : "",
    draft ? `The author's rough draft / notes:\n${draft}` : "",
    prompt ? `The author's instructions:\n${prompt}` : "",
    "",
    "Return ONLY a JSON object (no markdown fence, no commentary) with these string keys:",
    '- "summary": one sentence (<=160 chars) for the projects list.',
    '- "lede": one punchy opening line for the top of the project page.',
    '- "body": the case study in Markdown. Use ## for section headings and - for bullets. Open with a short overview, and where it fits include a "## Highlights" bulleted section. 150-350 words.',
  ].filter(Boolean).join("\n");

  const { ok, result } = await H.runClaudeJson({ prompt: ask, tools: "" });
  if (!ok) return { ok: false };
  try { const obj = JSON.parse(stripFence(result)); return { ok: true, summary: obj.summary || "", lede: obj.lede || "", body: obj.body || "" }; }
  catch { return { ok: false }; }
}

// Read the uploaded images and author a COMPLETE cja_projects record. Read-only.
async function buildProject({ name, category, draft, prompt, images, instagram }) {
  const srcs = (Array.isArray(images) ? images : [])
    .map((im) => String(im?.src || ""))
    .filter((s) => MEDIA_RE.test(s));
  const useSrcs = srcs.slice(0, MAX_BUILD_IMAGES);
  if (srcs.length > MAX_BUILD_IMAGES) console.log(`[build-project] capping images ${srcs.length} -> ${MAX_BUILD_IMAGES}`);
  const paths = useSrcs.map((s) => "app/public" + s);
  const imageLines = paths.length ? paths.map((p, i) => `  ${i + 1}. ${p}`).join("\n") : "";

  const ask = [
    "You are the Web Designer for Carlos Arias's portfolio (carlosarias.co) — an Agentic AI & Automation Engineer who builds for law firms and service businesses.",
    "Your job: turn the brief below into a COMPLETE project case study for a fixed, on-brand page template. You author STRUCTURED CONTENT, not HTML or layout.",
    "",
    "Brand (from brand/BRAND.md — honor it): sumi-e, ink on washi paper, a single vermillion seal, restraint. Voice is precise, understated, confident — no hype, no buzzwords, no exclamation marks. Never describe colours or layout; the template owns all styling.",
    "",
    "The page auto-renders these sections from the fields you return — populate the ones the brief supports, leave the rest empty:",
    "- header: title + one-line lede",
    "- facts rail: kind (project type), period (timeline label), role",
    "- metrics: a few outcome stats, each {value, label, note?}",
    "- gallery: your caption for each image below",
    "- body: the case study",
    "- skills: grouped disciplines, each {group, items[]}",
    "- stack: technologies used, as plain strings",
    "- categories: 1-3 short tags",
    "",
    `Project name: ${name}`,
    category ? `Category hint: ${category}` : "",
    instagram ? "There is an Instagram reel for this project." : "",
    draft ? `The author's rough draft / notes:\n${draft}` : "",
    prompt ? `The author's special instructions (follow these):\n${prompt}` : "",
    "",
    paths.length
      ? `Uploaded images — READ each file and caption it from what is ACTUALLY shown. Keep captions short and specific; do not invent UI or content that isn't visible:\n${imageLines}`
      : "No images were uploaded.",
    "",
    "Body rules: Markdown only, using ONLY ## / ### headings, paragraphs, - bullet lists, and **bold**. No images, no HTML, no tables. Open with a short overview; where it fits include a '## Highlights' bulleted section. 150-350 words.",
    "",
    "Return ONLY a JSON object (no markdown fence, no commentary) with these keys:",
    '- "summary": one sentence (<=160 chars) for the projects list.',
    '- "lede": one punchy opening line.',
    '- "body": the Markdown case study.',
    '- "kind": short project type, e.g. "Website" or "SaaS / Media" (or "").',
    '- "period": a timeline label, e.g. "2024 — present" (or "").',
    '- "role": Carlos\'s role on the project (or "").',
    '- "categories": array of 1-3 short strings.',
    '- "stack": array of technology strings.',
    '- "skills": array of {"group": string, "items": [string, ...]}.',
    '- "metrics": array of {"value": string, "label": string, "note"?: string}. Use an empty array if the brief has no real numbers — do NOT invent metrics.',
    `- "captions": array of exactly ${paths.length} strings, one per uploaded image IN ORDER.`,
    `- "alts": array of exactly ${paths.length} short alt-text strings, one per image IN ORDER.`,
  ].filter(Boolean).join("\n");

  const { ok, result } = await H.runClaudeJson({ prompt: ask, tools: "Read" });
  if (!ok) return { ok: false };
  try {
    const obj = JSON.parse(stripFence(result));
    const caps = Array.isArray(obj.captions) ? obj.captions : [];
    const alts = Array.isArray(obj.alts) ? obj.alts : [];
    const gallery = useSrcs.map((src, i) => ({
      src, alt: String(alts[i] || "").slice(0, 200), caption: String(caps[i] || "").slice(0, 120),
    }));
    const strArr = (a) => (Array.isArray(a) ? a.map((x) => String(x)).filter(Boolean) : []);
    return {
      ok: true,
      summary: obj.summary || "", lede: obj.lede || "", body: obj.body || "",
      kind: obj.kind || "", period: obj.period || "", role: obj.role || "",
      categories: strArr(obj.categories), stack: strArr(obj.stack),
      skills: Array.isArray(obj.skills) ? obj.skills : [],
      metrics: Array.isArray(obj.metrics) ? obj.metrics : [],
      gallery,
    };
  } catch { return { ok: false }; }
}

export function register(host) {
  H = host;

  host.route("POST", "/draft", async (req, res) => {
    if (H.state.busy) return H.json(res, 429, { error: "Busy — try again in a moment." });
    const body = await H.readBody(req);
    const name = String(body.name || "").trim();
    if (!name) return H.json(res, 400, { error: "Add a project name first." });
    const env = H.loadEnv();
    if (!env.CLAUDE_CODE_OAUTH_TOKEN) return H.json(res, 401, { error: "No Claude token set." });
    H.state.busy = true;
    const d = await draftCopy(body);
    H.state.busy = false;
    if (!d.ok) return H.json(res, 500, { error: "Couldn't generate a draft — try again." });
    H.json(res, 200, d);
  });

  host.route("POST", "/build-project", async (req, res) => {
    if (H.state.busy) return H.json(res, 429, { error: "Busy — try again in a moment." });
    const body = await H.readBody(req);
    const name = String(body.name || "").trim();
    if (!name) return H.json(res, 400, { error: "Add a project name first." });
    const env = H.loadEnv();
    if (!env.CLAUDE_CODE_OAUTH_TOKEN) return H.json(res, 401, { error: "No Claude token set." });
    H.state.busy = true;
    const d = await buildProject(body);
    H.state.busy = false;
    if (!d.ok) return H.json(res, 500, { error: "The Web Designer couldn't finish — try again." });
    H.json(res, 200, d);
  });
}
