/* ================================================================
   Model Answer Authoring — entire feature module
   ----------------------------------------------------------------
   Backed by the Bengali pipeline at
     POST /api/questions/:id/answer-key
     GET  /api/questions/:id/answer-key
     POST /api/questions/:id/answer-key/approve
     POST /api/tests/:id/answer-keys
     GET  /api/tests/:id/answer-keys

   The screen is self-contained — pick a test, list its questions on the
   left, generate / view / approve the 4-stage answer-key on the right.
   ================================================================ */

const AK_Icon = window.AppUI.Icon;
const AK_Api  = window.AppApi;

/* ---------- shared helpers ---------- */

const AK_LANG_LABEL = { bn: "বাংলা", hi: "हिन्दी", en: "English" };

function ak_useTests() {
  const [tests, setTests] = useState([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    let alive = true;
    AK_Api.get("/tests")
      .then(d => { if (alive) { setTests((d || []).filter(t => Number(t.questions) > 0)); setLoading(false); } })
      .catch(() => { if (alive) setLoading(false); });
    return () => { alive = false; };
  }, []);
  return { tests, loading };
}

function ak_useQuestions(testDisplayId) {
  const [questions, setQuestions] = useState([]);
  const [loading, setLoading] = useState(false);
  useEffect(() => {
    if (!testDisplayId) { setQuestions([]); return; }
    let alive = true;
    setLoading(true);
    AK_Api.get(`/tests/${testDisplayId}/questions`)
      .then(d => { if (alive) { setQuestions(d || []); setLoading(false); } })
      .catch(() => { if (alive) setLoading(false); });
    return () => { alive = false; };
  }, [testDisplayId]);
  return { questions, loading };
}

/* Hash a question id to a pleasant accent for chips. */
const AK_PALETTE = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)", "var(--chart-6)"];
const ak_colorFor = (qid, all) => {
  const idx = Math.max(0, all.findIndex(q => q.id === qid));
  return AK_PALETTE[idx % AK_PALETTE.length];
};

/* ================================================================
   ROOT — orchestrates test pick, question list, right-pane viewer
   ================================================================ */
const AnswerKeyAuthoring = ({ setScreen }) => {
  const { tests, loading: testsLoading } = ak_useTests();
  const [testId, setTestId] = useState(null);
  const test = tests.find(t => t.id === testId) || null;

  // Auto-pick first test once the list loads (skip if URL hash has a hint).
  useEffect(() => {
    if (!testId && tests.length) {
      const fromHash = decodeURIComponent((location.hash.match(/test=([^&]+)/) || [])[1] || "");
      const found = fromHash && tests.find(t => t.id === fromHash);
      setTestId(found ? found.id : tests[0].id);
    }
  }, [tests, testId]);

  const { questions, loading: questionsLoading } = ak_useQuestions(test?.id);

  // answer keys keyed by question_id
  const [keys, setKeys] = useState({});          // { [qid]: AnswerKey }
  const [running, setRunning] = useState({});    // { [qid]: 'running'|'error' }
  const [bulkProgress, setBulkProgress] = useState(null);  // null | { running, total, succeeded, failed }
  const [activeQid, setActiveQid] = useState(null);
  const [language, setLanguage] = useState("en");
  const [printable, setPrintable] = useState(false);

  // Hydrate any persisted answer-keys when test/questions arrive.
  useEffect(() => {
    if (!test) return;
    let alive = true;
    AK_Api.get(`/tests/${test.id}/answer-keys`)
      .then(rows => {
        if (!alive) return;
        const byId = {};
        for (const r of rows || []) byId[r.question_id] = r;
        setKeys(byId);
      })
      .catch(() => {});
    return () => { alive = false; };
  }, [test?.id]);

  useEffect(() => {
    if (!activeQid && questions.length) setActiveQid(questions[0].id);
  }, [questions, activeQid]);

  const activeQ = questions.find(q => q.id === activeQid) || null;
  const activeKey = activeQid ? keys[activeQid] : null;

  const runForQuestion = async (qid) => {
    setRunning(r => ({ ...r, [qid]: "running" }));
    try {
      const ak = await AK_Api.post(`/questions/${qid}/answer-key?lang=${language}`);
      setKeys(prev => ({ ...prev, [qid]: ak }));
      setRunning(r => { const c = { ...r }; delete c[qid]; return c; });
    } catch (e) {
      console.error("[answer-key]", e);
      setRunning(r => ({ ...r, [qid]: "error" }));
    }
  };

  const approveQuestion = async (qid) => {
    try {
      await AK_Api.post(`/questions/${qid}/answer-key/approve`);
      setKeys(prev => prev[qid]
        ? { ...prev, [qid]: { ...prev[qid], status: "approved" } }
        : prev);
    } catch (e) { console.error(e); }
  };

  const runForAll = async (skipExisting) => {
    if (!test) return;
    const todo = questions
      .map(q => q.id)
      .filter(id => !skipExisting || !keys[id]);
    if (!todo.length) return;
    setBulkProgress({ running: 0, total: todo.length, succeeded: 0, failed: 0 });
    // Run sequentially client-side so the user sees per-question updates; the
    // backend is the rate-limiter (it caps concurrency anyway).
    let succeeded = 0, failed = 0;
    for (let i = 0; i < todo.length; i++) {
      const qid = todo[i];
      setBulkProgress({ running: i + 1, total: todo.length, succeeded, failed });
      try {
        await runForQuestion(qid);
        succeeded++;
      } catch { failed++; }
    }
    setBulkProgress({ running: todo.length, total: todo.length, succeeded, failed });
    setTimeout(() => setBulkProgress(null), 3500);
  };

  return (
    <div style={{ display:"flex", flexDirection:"column", height:"100%", background:"var(--bg-0)" }}>
      <AK_Header
        tests={tests} testId={testId} setTestId={setTestId} testsLoading={testsLoading}
        language={language} setLanguage={setLanguage}
        printable={printable} setPrintable={setPrintable}
        questions={questions} keys={keys}
        onRunAll={() => runForAll(true)}
        onRegenerateAll={() => runForAll(false)}
        bulkProgress={bulkProgress}
      />

      {!test ? (
        <div style={{ flex:1, display:"grid", placeItems:"center", color:"var(--ink-3)" }}>
          {testsLoading ? "Loading tests…" : (
            <window.AppUI.EmptyState icon="library" title="No tests with parsed questions yet"
              hint="Upload one from the Test Library first."/>
          )}
        </div>
      ) : printable ? (
        <AK_PrintView test={test} questions={questions} keys={keys}/>
      ) : (
        <div className="cx-catalog-split flush" style={{ flex:1, minHeight: 0 }}>
          <AK_QuestionSidebar
            questions={questions}
            keys={keys}
            running={running}
            activeQid={activeQid}
            onPick={setActiveQid}
            loading={questionsLoading}
          />
          <AK_RightPane
            question={activeQ}
            answerKey={activeKey}
            running={!!activeQid && running[activeQid] === "running"}
            error={!!activeQid && running[activeQid] === "error"}
            onRun={() => activeQid && runForQuestion(activeQid)}
            onApprove={() => activeQid && approveQuestion(activeQid)}
            language={language}
          />
        </div>
      )}
    </div>
  );
};

/* ================================================================
   HEADER — test picker + bulk actions + progress strip
   ================================================================ */
const AK_Header = ({
  tests, testId, setTestId, testsLoading,
  language, setLanguage,
  printable, setPrintable,
  questions, keys, onRunAll, onRegenerateAll, bulkProgress,
}) => {
  const total = questions.length;
  const generated = questions.filter(q => keys[q.id]).length;
  const approved  = questions.filter(q => keys[q.id]?.status === "approved").length;

  return (
    <div style={{
      padding:"20px 28px 16px", borderBottom:"1px solid var(--line)",
      background:"var(--bg-0)",
    }}>
      <div style={{ display:"flex", alignItems:"flex-start", gap: 16, flexWrap:"wrap" }}>
        <div style={{ minWidth: 0, flex: "1 1 280px" }}>
          <div className="cx-eyebrow">scope · perfect answer · logic · deduction-proofing</div>
          <h1 className="cx-h1" style={{ fontSize: 24 }}>Model Answer Keys</h1>
        </div>

        {/* Counters and buttons wrap — without flexWrap this row measured
            553px, which on a phone widened the whole layout viewport. */}
        <div style={{ display:"flex", gap: 8, alignItems:"center",
                       flexWrap: "wrap", minWidth: 0, justifyContent: "flex-end" }}>
          <button className="btn" onClick={onRunAll} disabled={!total || generated === total || !!bulkProgress}
            title="Run the pipeline for every question that does not yet have an answer key">
            <AK_Icon name="sparkle" size={15}/>Generate missing
          </button>
          <button className="btn" onClick={onRegenerateAll} disabled={!total || !!bulkProgress}
            title="Re-run the pipeline for ALL questions, overwriting existing drafts">
            <AK_Icon name="refresh" size={15}/>Regenerate all
          </button>
          <button className={`btn ${printable ? "primary" : ""}`} onClick={() => setPrintable(p => !p)}
            title="Toggle printable A4 view">
            <AK_Icon name={printable ? "arrowLeft" : "printer"} size={15}/>
            {printable ? "Back to authoring" : "Print view"}
          </button>
        </div>
      </div>

      <div className="ui-toolbar" style={{ marginTop: 14, alignItems: "center" }}>
        <select
          className="select"
          value={testId || ""}
          onChange={e => setTestId(e.target.value)}
          disabled={testsLoading || !tests.length}
          style={{ width: "min(380px, 100%)" }}
          title="Pick a test"
        >
          {testsLoading
            ? <option value="">Loading tests…</option>
            : tests.length
              ? tests.map(t => (
                <option key={t.id} value={t.id}>
                  {t.id} · {(t.title || "Untitled").slice(0, 52)} · {t.questions} qs
                </option>
              ))
              : <option value="">No tests with questions</option>}
        </select>

        <window.AppUI.Tabs value={language} onChange={setLanguage} size="sm" ariaLabel="Output language for the pipeline"
          items={["bn", "hi", "en"].map(l => ({ value: l, label: AK_LANG_LABEL[l] }))}/>

        <span style={{ marginLeft: "auto", display: "flex", gap: 6, flexWrap: "wrap" }}>
          <window.AppUI.StatusBadge tone="blue" icon="sparkle"><span className="mono">{generated}/{total}</span>&nbsp;generated</window.AppUI.StatusBadge>
          <window.AppUI.StatusBadge tone="green" icon="checkCircle"><span className="mono">{approved}/{total}</span>&nbsp;approved</window.AppUI.StatusBadge>
        </span>
      </div>

      {bulkProgress && (
        <div style={{ marginTop: 14 }}>
          <div className="progress" style={{ height: 6, borderRadius: 999 }}>
            <div style={{ width: `${(bulkProgress.running / Math.max(1, bulkProgress.total)) * 100}%` }}/>
          </div>
          <div style={{ marginTop: 6, fontSize: 12.5, color:"var(--ink-3)", display:"flex", justifyContent:"space-between" }}>
            <span>Running pipeline · {bulkProgress.running} of {bulkProgress.total}</span>
            <span style={{ fontVariantNumeric: "tabular-nums" }}><span style={{ color:"var(--green)" }}>✓ {bulkProgress.succeeded}</span> · <span style={{ color:"var(--red)" }}>✕ {bulkProgress.failed}</span></span>
          </div>
        </div>
      )}
    </div>
  );
};

/* ================================================================
   QUESTION SIDEBAR — sectioned list with status dots
   ================================================================ */
const AK_QuestionSidebar = ({ questions, keys, running, activeQid, onPick, loading }) => {
  // Group by inferred section letter (first non-digit prefix in q_no) — falls
  // back to a single "Questions" bucket if numbering is plain.
  const groups = useMemo(() => {
    const m = new Map();
    questions.forEach(q => {
      const key = String(q.num ?? "").match(/^[A-Z]/) ? String(q.num)[0] : "•";
      if (!m.has(key)) m.set(key, []);
      m.get(key).push(q);
    });
    return [...m.entries()].map(([k, items]) => ({ k, items }));
  }, [questions]);

  return (
    <aside style={{ borderRight:"1px solid var(--line)", background:"var(--bg-1)", overflow:"auto" }}>
      <div style={{ padding:"14px 16px", borderBottom:"1px solid var(--line)", position:"sticky", top: 0, background:"var(--bg-1)", zIndex: 1,
                    display: "flex", alignItems: "baseline", justifyContent: "space-between" }}>
        <div className="cx-card-title">Questions</div>
        <div className="ui-id">{questions.length} total</div>
      </div>

      {loading && <div className="muted" style={{ padding: 16, fontSize: 13 }}>Loading…</div>}

      {!loading && groups.length === 0 && (
        <window.AppUI.EmptyState compact icon="list" title="No questions in this test"/>
      )}

      <div style={{ padding: 8, display: "flex", flexDirection: "column", gap: 2 }}>
      {groups.map(({ k, items }) => (
        <div key={k}>
          {groups.length > 1 && (
            <div className="ui-section-label" style={{ padding:"10px 10px 6px" }}>
              {k === "•" ? "Questions" : `Section ${k}`}
            </div>
          )}
          {items.map(q => {
            const ak = keys[q.id];
            const r = running[q.id];
            const active = q.id === activeQid;
            const color = ak_colorFor(q.id, questions);
            return (
              <div key={q.id} onClick={() => onPick(q.id)} role="button" tabIndex={0}
                onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onPick(q.id); } }}
                style={{
                  padding:"10px 12px",
                  cursor:"pointer",
                  borderRadius: "var(--radius-ctl)",
                  background: active ? "var(--surface-active)" : "transparent",
                  marginBottom: 2,
                }}>
                <div style={{ display:"flex", alignItems:"center", gap:8, marginBottom: 4 }}>
                  <span style={{ width: 6, height: 6, borderRadius: "50%", background: color, flexShrink: 0 }}/>
                  <span style={{ color: "var(--ink-0)", fontSize: 13, fontWeight: 600 }}>Q{q.num}</span>
                  <span className="ui-id" style={{ fontSize: 11.5 }}>{q.type} · {q.maxMarks ?? "—"}m</span>
                  <span style={{ marginLeft:"auto", display: "inline-flex" }}>
                    <AK_StatusBadge ak={ak} running={r}/>
                  </span>
                </div>
                <div style={{ fontSize: 12.5, color: active ? "var(--ink-1)" : "var(--ink-3)", lineHeight: 1.4,
                  fontFamily:"var(--font-sans)", paddingLeft: 14,
                  display:"-webkit-box", WebkitLineClamp:2, WebkitBoxOrient:"vertical", overflow:"hidden",
                }}>{q.text}</div>
              </div>
            );
          })}
        </div>
      ))}
      </div>
    </aside>
  );
};

const AK_StatusBadge = ({ ak, running }) => {
  if (running === "running") {
    return <span title="Running" style={{ width: 8, height: 8, borderRadius: "50%", background:"var(--accent)", animation:"pulse 1.2s ease-in-out infinite", display:"inline-block" }}/>;
  }
  if (running === "error") {
    return <span title="Failed" style={{ color:"var(--red)", display: "inline-flex" }}><AK_Icon name="xCircle" size={14}/></span>;
  }
  if (!ak)             return <span title="Not generated" style={{ color:"var(--ink-4)", display: "inline-flex" }}><AK_Icon name="dashedCircle" size={14}/></span>;
  if (ak.status === "approved") return <span title="Approved" style={{ color:"var(--green)", display: "inline-flex" }}><AK_Icon name="checkCircle" size={14}/></span>;
  return <span title="Draft" style={{ color:"var(--blue)", display: "inline-flex" }}><AK_Icon name="clock" size={14}/></span>;
};

/* ================================================================
   RIGHT PANE — 4 stacked cards or empty state
   ================================================================ */
const AK_RightPane = ({ question, answerKey, running, error, onRun, onApprove, language }) => {
  if (!question) {
    return (
      <div style={{ display:"grid", placeItems:"center" }}>
        <window.AppUI.EmptyState icon="list" title="No question selected" hint="Pick a question on the left."/>
      </div>
    );
  }
  return (
    <main style={{ overflow:"auto", padding: "24px 28px", display:"flex", flexDirection:"column", gap: 16, background: "var(--bg-0)" }}>
      <AK_QuestionHeader q={question} ak={answerKey} running={running} error={error} onRun={onRun} onApprove={onApprove} language={language}/>

      {!answerKey && !running && !error && (
        <window.AppUI.EmptyState card icon="sparkle" title="No model answer yet"
          hint={<>Click <b>Generate</b> above. The pipeline runs scope → perfect answer → logic → deduction-proofing (~50s on Gemini Pro).</>}/>
      )}

      {running && <AK_RunningCard q={question} language={language}/>}
      {error && !running && (
        <div className="ui-alert red">
          The pipeline failed. Open the eavesdropper (<span className="mono">/api/_eavesdrop/stream</span>) to see why, then retry.
        </div>
      )}

      {answerKey && <AK_ScopeCard ak={answerKey}/>}
      {answerKey && <AK_PerfectCard ak={answerKey} q={question}/>}
      {answerKey && <AK_LogicCard ak={answerKey}/>}
      {answerKey && <AK_DeductionCard ak={answerKey}/>}
    </main>
  );
};

const AK_QuestionHeader = ({ q, ak, running, error, onRun, onApprove, language }) => (
  <div className="cx-card">
    <div style={{ display:"flex", alignItems:"flex-start", gap: 16, flexWrap: "wrap" }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div className="ui-pagehead-titlerow" style={{ marginBottom: 6 }}>
          <span style={{ fontSize: 20, fontWeight: 600, letterSpacing: "-0.3px", color: "var(--ink-0)" }}>Question {q.num}</span>
          {ak && (
            <window.AppUI.StatusBadge variant="outline" tone={ak.status === "approved" ? "green" : "amber"}
              icon={ak.status === "approved" ? "checkCircle" : "penLine"}>
              {ak.status === "approved" ? "Approved" : "Draft"}
            </window.AppUI.StatusBadge>
          )}
        </div>
        <div className="ui-pagehead-meta">
          <span className="mono">{q.type}</span>
          <span>{q.maxMarks} marks</span>
          {ak && <span>{AK_LANG_LABEL[ak.language] || ak.language}</span>}
        </div>
      </div>
      <div style={{ display:"flex", gap: 8, flexShrink: 0 }}>
        <button className="btn" onClick={onRun} disabled={running}>
          <AK_Icon name={ak ? "refresh" : "sparkle"} size={15}/>
          {ak ? "Regenerate" : `Generate (${AK_LANG_LABEL[language] || language})`}
        </button>
        {ak && ak.status !== "approved" && (
          <button className="btn primary" onClick={onApprove}>
            <AK_Icon name="check" size={15}/>Approve
          </button>
        )}
      </div>
    </div>
    <div style={{ color:"var(--ink-0)", fontSize: 14, lineHeight: 1.55, fontFamily:"var(--font-sans)",
      marginTop: 16, paddingTop: 16, borderTop: "1px solid var(--line)" }}>
      {q.text}
    </div>
  </div>
);

/* ---------- Running indicator ---------- */
const AK_RunningCard = ({ q, language }) => {
  const stages = [
    "Identifying scope & syllabus anchor",
    "Composing the perfect answer + flow",
    "Explaining the chemistry / logic per criterion",
    "Generating deduction-proof variants + rebuttals",
  ];
  // No real per-stage SSE yet — animate sequentially for honest UX.
  const [now] = useState(Date.now());
  const elapsed = (Date.now() - now) / 1000;
  return (
    <div className="cx-card">
      <div className="cx-card-head" style={{ alignItems: "center" }}>
        <div style={{ display:"flex", alignItems:"center", gap: 10 }}>
          <span style={{ width:14, height:14, border:"2px solid var(--line-strong)", borderTopColor:"var(--accent)", borderRadius:"50%", animation:"spin 1s linear infinite", display:"inline-block" }}/>
          <span className="cx-card-title">Pipeline running for Q{q.num} ({AK_LANG_LABEL[language] || language})</span>
        </div>
        <window.AppUI.StatusBadge tone="blue" icon="clock">~50s typical</window.AppUI.StatusBadge>
      </div>
      <div className="ui-list">
        {stages.map((s, i) => (
          <div key={i} className="ui-row" style={{ minHeight: 40 }}>
            <span style={{ color:"var(--ink-3)" }}><AK_Icon name="dashedCircle" size={15}/></span>
            <div className="ui-row-main"><div className="ui-row-title" style={{ fontWeight: 400, fontSize: 13 }}>{s}</div></div>
          </div>
        ))}
      </div>
      <style>{`@keyframes spin { to { transform: rotate(360deg); } }
        @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
      `}</style>
    </div>
  );
};

/* ---------- Lightweight Bengali-friendly markdown renderer ----------
   The pipeline prompts produce text like:
     - paragraphs separated by blank lines
     - **bold** spans
     - "* " bullet list rows
     - "1." / "১." numbered list rows
     - indented "    " continuation rows (inline reactions, etc.)
   We parse those into proper block elements so long answers flow naturally
   without `<pre>` quirks or fixed heights cutting content off. */
const AK_BN_NUM = /^([0-9]+|[০-৯]+)[.)]\s+/;
const AK_INLINE_BOLD = /(\*\*[^*]+\*\*)/g;

function ak_renderInline(s) {
  if (s == null) return null;
  const parts = String(s).split(AK_INLINE_BOLD);
  return parts.map((p, i) =>
    p.startsWith("**") && p.endsWith("**")
      ? <strong key={i} style={{ color: "var(--accent)" }}>{p.slice(2, -2)}</strong>
      : <React.Fragment key={i}>{p}</React.Fragment>
  );
}

function ak_renderMarkdown(text, opts = {}) {
  if (!text) return null;
  const className = opts.className || "bn";
  const baseStyle = {
    fontSize: opts.fontSize || 13.5,
    lineHeight: 1.6,
    color: opts.color || "var(--ink-0)",
    fontFamily: opts.font || "var(--font-serif)",
    overflowWrap: "anywhere",
    wordBreak: "break-word",
  };
  const lines = String(text).replace(/\r\n/g, "\n").split("\n");
  const blocks = [];
  let i = 0;
  let key = 0;
  while (i < lines.length) {
    const raw = lines[i];
    const trimmed = raw.trim();

    // Blank line → paragraph break (rendered as gap between blocks).
    if (!trimmed) { i++; continue; }

    // Bullet block — collect consecutive "* " (or "- ") items, treating
    // indented continuation lines as part of the previous item.
    if (/^[*\-]\s+/.test(trimmed)) {
      const items = [];
      while (i < lines.length) {
        const r = lines[i];
        const t = r.trim();
        if (/^[*\-]\s+/.test(t)) {
          items.push(t.replace(/^[*\-]\s+/, ""));
          i++;
        } else if (/^\s{2,}\S/.test(r) && items.length) {
          // Continuation of the last bullet (indented).
          items[items.length - 1] += "\n" + t;
          i++;
        } else {
          break;
        }
      }
      blocks.push(
        <ul key={key++} className={className} style={{ ...baseStyle, margin: "4px 0 10px", paddingLeft: 22 }}>
          {items.map((it, j) => (
            <li key={j} style={{ marginBottom: 4, whiteSpace: "pre-wrap" }}>
              {ak_renderInline(it)}
            </li>
          ))}
        </ul>
      );
      continue;
    }

    // Numbered block (English or Bengali digits).
    if (AK_BN_NUM.test(trimmed)) {
      const items = [];
      while (i < lines.length) {
        const r = lines[i];
        const t = r.trim();
        const m = t.match(AK_BN_NUM);
        if (m) {
          items.push(t.slice(m[0].length));
          i++;
        } else if (/^\s{2,}\S/.test(r) && items.length) {
          items[items.length - 1] += "\n" + t;
          i++;
        } else {
          break;
        }
      }
      blocks.push(
        <ol key={key++} className={className} style={{ ...baseStyle, margin: "4px 0 10px", paddingLeft: 22 }}>
          {items.map((it, j) => (
            <li key={j} style={{ marginBottom: 4, whiteSpace: "pre-wrap" }}>
              {ak_renderInline(it)}
            </li>
          ))}
        </ol>
      );
      continue;
    }

    // Plain paragraph — keep going until we hit a blank line or a list marker.
    const para = [trimmed];
    i++;
    while (i < lines.length) {
      const t = lines[i].trim();
      if (!t) break;
      if (/^[*\-]\s+/.test(t) || AK_BN_NUM.test(t)) break;
      para.push(t);
      i++;
    }
    blocks.push(
      <p key={key++} className={className}
         style={{ ...baseStyle, margin: "0 0 10px", whiteSpace: "pre-wrap" }}>
        {ak_renderInline(para.join("\n"))}
      </p>
    );
  }
  return blocks;
}

/* ---------- Card 1: Scope ---------- */
const AK_ScopeCard = ({ ak }) => {
  const s = ak.scope;
  if (!s) return null;
  return (
    <AkSection title="Scope & Syllabus" accent="var(--accent-2)" id="scope">
      <div style={{ marginBottom: 4 }}>{ak_renderMarkdown(s.scope_summary)}</div>
      <dl className="ui-details" style={{ "--cols": 3, margin: "6px 0 16px" }}>
        <KV label="Syllabus anchor" value={s.syllabus_anchor}/>
        <KV label="Bloom level" value={s.bloom_level}/>
        <KV label="Scope tags" value={s.scope_keywords?.length
          ? <span style={{ display: "inline-flex", flexWrap: "wrap", gap: 4 }}>{s.scope_keywords.map((k, i) => <window.AppUI.StatusBadge key={i} tone="grey" icon={false}>{k}</window.AppUI.StatusBadge>)}</span>
          : "—"}/>
      </dl>
      <ListBlock title="Sub-skills" items={s.sub_skills}/>
      <ListBlock title="Prerequisites" items={s.prerequisites}/>
      <ListBlock title="Common pitfalls" items={s.common_pitfalls} accent="amber"/>
    </AkSection>
  );
};

/* ---------- Card 2: Perfect answer + flow + key points ---------- */
const AK_PerfectCard = ({ ak, q }) => {
  const p = ak.perfect;
  if (!p) return null;
  return (
    <AkSection title="The Perfect Answer" accent="var(--accent)" id="perfect">
      <div className="bn" style={{
        margin:"0 0 16px", background:"var(--bg-1)", padding:"16px 18px",
        border:"1px solid var(--line)", boxShadow: "inset 3px 0 0 var(--accent)",
        borderRadius: "var(--radius-tile)", color:"var(--ink-0)",
        overflowWrap: "anywhere", wordBreak: "break-word",
      }}>
        {ak_renderMarkdown(p.perfect_answer, { fontSize: 14 })}
      </div>
      {p.answer_flow?.length > 0 && (
        <ListBlock title="Answer flow" items={p.answer_flow} numbered/>
      )}
      {p.diagram_hint && (
        <div className="ui-alert amber" style={{ marginBottom: 12, display: "flex", gap: 8, alignItems: "flex-start" }}>
          <AK_Icon name="penLine" size={14}/><span>{p.diagram_hint}</span>
        </div>
      )}
      {p.key_points?.length > 0 && (
        <div>
          <div className="ui-section-label" style={{ marginBottom: 8 }}>
            Key points · rubric mapping
          </div>
          <div className="cx-tablecard"><div className="cx-tablescroll">
          <table className="cx-table compact">
            <thead>
              <tr>
                <th style={th}>Criterion</th>
                <th style={th}>What the answer says</th>
                <th className="num" style={{ width: 60 }}>Marks</th>
              </tr>
            </thead>
            <tbody>
              {p.key_points.map((kp, i) => (
                <tr key={i}>
                  <td style={tdMono}>{kp.criterion_id}</td>
                  <td style={td} className="bn">{kp.point}</td>
                  <td style={td} className="num">{kp.mark_weight}</td>
                </tr>
              ))}
            </tbody>
          </table>
          </div></div>
          <div style={{ fontSize: 12.5, color:"var(--ink-3)", marginTop: 8, textAlign: "right", fontVariantNumeric: "tabular-nums" }}>
            Total <span style={{ color: "var(--ink-0)", fontWeight: 600 }}>{p.key_points.reduce((a, k) => a + Number(k.mark_weight || 0), 0)}</span> / {q.maxMarks}
          </div>
        </div>
      )}
    </AkSection>
  );
};

/* ---------- Card 3: Logic behind perfection ---------- */
const AK_LogicCard = ({ ak }) => {
  const l = ak.logic;
  if (!l) return null;
  return (
    <AkSection title="Logic — why this answer is correct" accent="var(--green)" id="logic">
      <div style={{ marginBottom: 4 }}>{ak_renderMarkdown(l.logic_summary)}</div>
      {l.per_criterion_logic?.length > 0 && (
        <div className="ui-list" style={{ marginBottom: 14, border: "1px solid var(--line)", borderRadius: "var(--radius-tile)", overflow: "hidden" }}>
          {l.per_criterion_logic.map((c, i) => (
            <div key={i} style={{ padding:"12px 14px", borderTop: i ? "1px solid var(--line)" : 0 }}>
              <div style={{ display:"flex", gap: 8, alignItems:"center", marginBottom: 6, flexWrap: "wrap" }}>
                <window.AppUI.StatusBadge tone="grey" icon={false}><span className="mono">{c.criterion_id}</span></window.AppUI.StatusBadge>
                <span className="bn" style={{ fontSize: 13, fontWeight: 500, color:"var(--ink-1)" }}>{c.principle}</span>
                {c.evidence_in_answer && c.evidence_in_answer !== "(implied)" && (
                  <span style={{ marginLeft:"auto", fontSize: 12 }} className="muted">
                    cites: <span style={{ color:"var(--accent)" }}>“{c.evidence_in_answer}”</span>
                  </span>
                )}
              </div>
              <div style={{ fontSize: 13, color:"var(--ink-0)", lineHeight: 1.5 }}>
                {ak_renderMarkdown(c.reasoning, { fontSize: 13 })}
              </div>
            </div>
          ))}
        </div>
      )}
      {l.citations?.length > 0 && (
        <div>
          <div className="ui-section-label" style={{ marginBottom: 8 }}>Citations</div>
          <div style={{ display:"flex", flexWrap:"wrap", gap: 6, marginBottom: 12 }}>
            {l.citations.map((c, i) => (
              <window.AppUI.StatusBadge key={i} tone="grey" icon="book">
                <b style={{ marginRight: 4 }}>{c.kind}</b>{c.label}{c.page ? ` · p.${c.page}` : ""}
              </window.AppUI.StatusBadge>
            ))}
          </div>
        </div>
      )}
      {l.alternative_correct_explanations?.length > 0 && (
        <ListBlock title="Alternative correct paths" items={l.alternative_correct_explanations}/>
      )}
    </AkSection>
  );
};

/* ---------- Card 4: Deduction proofing ---------- */
const AK_DeductionCard = ({ ak }) => {
  const d = ak.deduction_proof;
  if (!d) return null;
  return (
    <AkSection title="Deduction-proofing" accent="var(--violet)" id="proof">
      <div style={{ marginBottom: 4 }}>{ak_renderMarkdown(d.deduction_proof_summary)}</div>

      <AK_TwoCol>
        <Sub title="Accept also" color="var(--green)">
          {d.accept_also?.map((a, i) => (
            <div key={i} style={proofCard("var(--green)")}>
              <div style={{ display:"flex", alignItems:"baseline", gap: 6, marginBottom: 4 }}>
                <span style={{ color:"var(--green)", display: "inline-flex", alignSelf: "center" }}><AK_Icon name="checkCircle" size={14}/></span>
                <span style={{ flex: 1, color:"var(--ink-0)", fontSize: 13, fontWeight: 500 }}>{a.variant}</span>
                <window.AppUI.StatusBadge tone="green" icon={false}>{a.max_credit}m</window.AppUI.StatusBadge>
              </div>
              <div style={{ fontSize: 12, color:"var(--ink-2)", lineHeight: 1.45 }}>
                {ak_renderMarkdown(a.why_acceptable, { fontSize: 12, color: "var(--ink-2)" })}
              </div>
            </div>
          ))}
        </Sub>

        <Sub title="Reject also" color="var(--red)">
          {d.reject_also?.map((a, i) => (
            <div key={i} style={proofCard("var(--red)")}>
              <div style={{ display:"flex", alignItems:"baseline", gap: 6, marginBottom: 4 }}>
                <span style={{ color:"var(--red)", display: "inline-flex", alignSelf: "center" }}><AK_Icon name="xCircle" size={14}/></span>
                <span style={{ flex: 1, color:"var(--ink-0)", fontSize: 13, fontWeight: 500 }}>{a.variant}</span>
                {a.credit_if_partial > 0 && (
                  <window.AppUI.StatusBadge tone="amber" icon={false}>+{a.credit_if_partial}m</window.AppUI.StatusBadge>
                )}
              </div>
              <div style={{ fontSize: 12, color:"var(--ink-2)", lineHeight: 1.45 }}>
                {ak_renderMarkdown(a.why_rejected, { fontSize: 12, color: "var(--ink-2)" })}
              </div>
            </div>
          ))}
        </Sub>
      </AK_TwoCol>

      {d.marking_discontinuities?.length > 0 && (
        <Sub title="Marking discontinuities">
          {d.marking_discontinuities.map((m, i) => (
            <div key={i} style={proofCard("var(--amber)")}>
              <div style={{ display:"flex", alignItems:"baseline", gap: 6, marginBottom: 4 }}>
                <span className="bn" style={{ flex: 1, color:"var(--ink-0)", fontSize: 13, fontWeight: 500 }}>{m.trigger}</span>
                <window.AppUI.StatusBadge tone="amber" icon={false}>−{m.deduction}m</window.AppUI.StatusBadge>
              </div>
              <div style={{ fontSize: 12, color:"var(--ink-2)" }}>
                {ak_renderMarkdown(m.rationale, { fontSize: 12, color: "var(--ink-2)" })}
              </div>
            </div>
          ))}
        </Sub>
      )}

      {d.common_objections?.length > 0 && (
        <Sub title="Common objections + rebuttals">
          {d.common_objections.map((o, i) => (
            <div key={i} style={proofCard("var(--blue)")}>
              <div className="bn" style={{ fontSize: 12.5, color:"var(--ink-1)", marginBottom: 4, fontStyle:"italic", overflowWrap:"anywhere" }}>
                <span style={{ color:"var(--blue)", marginRight: 4 }}>›</span>“{o.objection}”
              </div>
              <div style={{ fontSize: 12.5, color:"var(--ink-0)", lineHeight: 1.5 }}>
                {ak_renderMarkdown(o.rebuttal, { fontSize: 12.5 })}
              </div>
            </div>
          ))}
        </Sub>
      )}

      {d.minimum_acceptable_representation && (
        <Sub title="Minimum acceptable representation">
          <div className="bn cx-tile" style={{ fontSize: 13, color:"var(--ink-0)" }}>
            {d.minimum_acceptable_representation}
          </div>
        </Sub>
      )}

      {d.examiner_checklist?.length > 0 && (
        <Sub title="Examiner checklist">
          <ol style={{ margin: 0, paddingLeft: 18, color:"var(--ink-1)", fontSize: 13, lineHeight: 1.5 }} className="bn">
            {d.examiner_checklist.map((c, i) => <li key={i} style={{ marginBottom: 3 }}>{c}</li>)}
          </ol>
        </Sub>
      )}
    </AkSection>
  );
};

/* ================================================================
   Layout primitives
   ================================================================ */
const AkSection = ({ title, accent, id, children }) => (
  <section id={id} className="cx-card" style={{
    flexShrink: 0,           // never let the parent flex compress a card
  }}>
    <div className="cx-card-head" style={{ alignItems: "center" }}>
      <div style={{ display:"flex", alignItems:"center", gap: 10 }}>
        <span style={{ width: 8, height: 8, borderRadius: "50%", background: accent || "var(--accent)", flexShrink: 0 }}/>
        <span className="cx-card-title">{title}</span>
      </div>
    </div>
    <div style={{ overflowWrap: "anywhere", wordBreak: "break-word" }}>
      {children}
    </div>
  </section>
);

const Sub = ({ title, color, children }) => (
  <div style={{ marginTop: 14 }}>
    <div className="ui-section-label" style={{ marginBottom: 8, color: color || undefined }}>
      {title}
    </div>
    <div style={{ display:"flex", flexDirection:"column", gap: 8 }}>{children}</div>
  </div>
);

const KV = ({ label, value }) => (
  <div className="ui-detail">
    <div className="ui-detail-label">{label}</div>
    <div className="ui-detail-value bn">{value || "—"}</div>
  </div>
);

const ListBlock = ({ title, items, numbered, accent }) => {
  if (!items || items.length === 0) return null;
  const color = accent === "amber" ? "var(--amber)" : undefined;
  const Tag = numbered ? "ol" : "ul";
  return (
    <div style={{ marginBottom: 12 }}>
      <div className="ui-section-label" style={{ marginBottom: 6, color }}>{title}</div>
      <Tag className="bn" style={{ margin: 0, paddingLeft: 18, color:"var(--ink-1)", fontSize: 13, lineHeight: 1.5 }}>
        {items.map((it, i) => <li key={i} style={{ marginBottom: 3 }}>{it}</li>)}
      </Tag>
    </div>
  );
};

const AK_TwoCol = ({ children }) => (
  <div style={{ display:"grid", gridTemplateColumns:"repeat(auto-fit, minmax(260px, 1fr))", gap: 16 }}>{children}</div>
);

const th = { textAlign:"left" };
const td = { verticalAlign:"top" };
const tdMono = { ...td, fontFamily:"var(--font-mono)", fontSize: 12, color:"var(--ink-2)", whiteSpace:"nowrap" };

const proofCard = (col) => ({
  padding:"10px 12px",
  background:"var(--surface)",
  border:"1px solid var(--line)",
  boxShadow: `inset 3px 0 0 ${col}`,
  borderRadius: "var(--radius-ctl)",
});

/* ================================================================
   Print view — flatten every answer-key onto one scrollable A4-styled column
   ================================================================ */
const AK_PrintView = ({ test, questions, keys }) => {
  const generated = questions.filter(q => keys[q.id]);
  return (
    <div style={{ flex: 1, overflow:"auto", padding: "16px 0", background:"var(--line)" }}>
      {/* A4 on paper, but 210mm is 794px — on a phone that forced the whole
          layout viewport wider than the screen. Cap it for the screen; the
          @page rule in print CSS is what actually governs the printout. */}
      <div className="ak-a4" style={{ width: "210mm", maxWidth: "100%", margin:"0 auto", padding: "16mm 14mm",
        background:"var(--paper)", color:"var(--ink-0)",
        fontFamily:"var(--font-serif)", fontSize: "10.5pt", lineHeight: 1.5,
        boxShadow:"0 6px 22px color-mix(in srgb, var(--ink-0) 12%, transparent)",
      }}>
        <div style={{ borderBottom:"2pt solid var(--ink-0)", paddingBottom:"6mm", marginBottom:"6mm" }}>
          <div style={{ fontSize: 8.5, letterSpacing: ".18em", textTransform:"uppercase", color:"var(--ink-2)" }}>
            {(window.APP.CURRENT_SCHOOL?.name || window.BRAND.orgName)} · Model Answer Key · v1 · {new Date().toISOString().slice(0, 10)}
          </div>
          <h1 style={{ margin: "2mm 0 0", fontSize: 20, fontFamily:"var(--font-serif)", fontWeight: 600 }}>
            {test.title}
            <span style={{ fontSize: 11, color:"var(--ink-2)", display:"block", marginTop: 2, fontWeight: 400 }}>
              {test.id} · {test.subject || "—"} · {test.className || "—"} · {generated.length} / {questions.length} questions
            </span>
          </h1>
        </div>

        {generated.length === 0 && (
          <p style={{ color:"var(--ink-2)" }}>No answer keys generated yet. Go back, click <b>Generate missing</b>, then return here.</p>
        )}

        {generated.map(q => {
          const ak = keys[q.id];
          return (
            <article key={q.id} style={{ borderTop:"0.5pt solid var(--line-strong)", padding:"4mm 0 6mm", pageBreakInside:"avoid" }}>
              <h2 style={{ margin:"0 0 2mm", fontSize: 13, fontWeight: 600 }}>
                <span style={{ color:"var(--amber)", fontFamily:"var(--font-mono)", marginRight: 6 }}>Q{q.num}</span>
                <span style={{ fontSize: 9, color:"var(--ink-2)" }}>· {q.type} · {q.maxMarks} m · {ak.status}</span>
              </h2>
              <p style={{ margin:"0 0 3mm", fontFamily:"var(--font-sans)", fontSize: 11.5, color:"var(--ink-0)" }}>{q.text}</p>

              {ak.scope && (
                <div style={{ margin:"2mm 0" }}>
                  <PrintLabel>Scope</PrintLabel>
                  <div className="bn" style={{ fontSize: 10.5 }}>{ak.scope.scope_summary}</div>
                  <div style={{ fontSize: 9, color:"var(--ink-2)", marginTop: 1 }}>{ak.scope.syllabus_anchor}</div>
                </div>
              )}

              {ak.perfect && (
                <div style={{ margin:"2mm 0" }}>
                  <PrintLabel>Perfect answer</PrintLabel>
                  <div className="bn" style={{ fontSize: 11, whiteSpace:"pre-wrap", background:"var(--paper)", padding:"2mm 3mm", borderLeft:"2pt solid var(--amber-solid)" }}>
                    {ak.perfect.perfect_answer}
                  </div>
                </div>
              )}

              {ak.logic?.logic_summary && (
                <div style={{ margin:"2mm 0" }}>
                  <PrintLabel>Logic</PrintLabel>
                  <div className="bn" style={{ fontSize: 10.5 }}>{ak.logic.logic_summary}</div>
                </div>
              )}

              {ak.deduction_proof?.deduction_proof_summary && (
                <div style={{ margin:"2mm 0" }}>
                  <PrintLabel>Deduction-proofing</PrintLabel>
                  <div className="bn" style={{ fontSize: 10.5 }}>{ak.deduction_proof.deduction_proof_summary}</div>
                </div>
              )}
            </article>
          );
        })}
      </div>
    </div>
  );
};

const PrintLabel = ({ children }) => (
  <div style={{ fontSize: 8.2, color:"var(--ink-2)", letterSpacing:".12em", textTransform:"uppercase", margin:"0 0 1mm" }}>
    {children}
  </div>
);

window.AnswerKeyAuthoring = AnswerKeyAuthoring;
