document.addEventListener("DOMContentLoaded", function () {
    const form = document.getElementById("generator-form");
    const results = document.getElementById("question-results");
    const aiReviewPanel = document.getElementById("ai-review-panel");
    const progress = document.getElementById("generation-progress");
    const progressCompact = document.getElementById("gen-generation-status");
    const editPanelBody = document.getElementById("edit-panel-body");
    const editPanelWrapper = document.getElementById("edit-panel-wrapper");
    const closeEditPanelBtn = document.getElementById("close-edit-panel");
    const cancelEditPanelBtn = document.getElementById("cancel-edit-panel-btn");
    const saveQuestionBtn = document.getElementById("save-question-btn");

    const prevBtn = document.getElementById("prev-question-btn");
    const nextBtn = document.getElementById("next-question-btn");
    const nextDraftBtn = document.getElementById("next-draft-btn");
    const counter = document.getElementById("question-counter");
    const jumpSelect = document.getElementById("jump-select");

    const modalOverlay = document.getElementById("app-modal-overlay");
    const modalTitle = document.getElementById("app-modal-title");
    const modalMessage = document.getElementById("app-modal-message");
    const modalClose = document.getElementById("app-modal-close");
    const modalCancel = document.getElementById("app-modal-cancel");
    const modalConfirm = document.getElementById("app-modal-confirm");
    const modalOk = document.getElementById("app-modal-ok");

    const exportModal = document.getElementById("export-modal-overlay");
    const openExportBtn = document.getElementById("open-export-modal-btn");
    const closeExportModalBtn = document.getElementById("export-modal-close");
    const exportOptions = document.querySelectorAll(".export-option");

    let questions = [];
    let currentIndex = 0;
    let currentFilter = "All";
    let filteredQuestions = [];
    let activeGenerationRun = null;
    let generationSummaryPhase = "idle";

    const genContextCompact = document.getElementById("gen-context-compact");

    let autosaveTimer = null;
    let currentEditQuestionId = null;
    let modalConfirmAction = null;
    const tutorReviewRequestsInFlight = new Set();

    const TUTOR_REVIEW_FINGERPRINT_FIELDS = [
        "title",
        "stem",
        "lead_in",
        "option_a",
        "option_b",
        "option_c",
        "option_d",
        "option_e",
        "correct_answer",
        "correct_answer_letter",
        "justification",
        "skill",
        "condition_name",
        "presentation"
    ];

function ensureWorkspace() {
    let workspaceId = localStorage.getItem("workspace_id");

    if (workspaceId) {
        return Promise.resolve(workspaceId);
    }

    return fetch("../app/api/create_workspace.php")
        .then(res => res.text())
        .then(text => {
            const match = text.match(/Workspace created:\s*(\w+)/);

            if (!match) {
                throw new Error("Could not create workspace");
            }

            const newId = match[1];
            localStorage.setItem("workspace_id", newId);
            return newId;
        });
}

    function escapeHtml(value) {
        if (value === null || value === undefined) {
            return "";
        }

        return String(value)
            .replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#039;");
    }

    function getSelectedSkillCheckboxes() {
        if (!form) {
            return [];
        }
        return Array.from(form.querySelectorAll('input[name="skills[]"]:checked'));
    }

    function getSelectedSkillCount() {
        return getSelectedSkillCheckboxes().length;
    }

    const SKILLS_REQUIRED_MESSAGE = "Select at least one skill to generate questions.";

    function clearSkillsValidationError() {
        const errorEl = document.getElementById("skills-validation-error");
        if (!errorEl) {
            return;
        }
        errorEl.textContent = "";
        errorEl.classList.add("is-hidden");
        errorEl.hidden = true;
    }

    function showSkillsValidationError(message) {
        const errorEl = document.getElementById("skills-validation-error");
        if (!errorEl) {
            return;
        }
        errorEl.textContent = message;
        errorEl.classList.remove("is-hidden");
        errorEl.hidden = false;
        errorEl.scrollIntoView({ block: "nearest", behavior: "smooth" });
    }

    function updateSkillQuestionCountDisplay() {
        const countEl = document.getElementById("skill-question-count");
        const count = getSelectedSkillCount();

        if (countEl) {
            if (count === 0) {
                countEl.textContent = "";
                countEl.classList.add("is-hidden");
                countEl.hidden = true;
            } else {
                countEl.textContent = "Number of questions requested: " + count;
                countEl.classList.remove("is-hidden");
                countEl.hidden = false;
                clearSkillsValidationError();
            }
        }
    }

    function getGeneratorFormContext() {
        if (!form) {
            return {
                specialty: "",
                condition: "",
                presentation: "",
                year: "",
                summative: "",
                skillCount: 0,
                skills: []
            };
        }

        const skillChecks = getSelectedSkillCheckboxes();
        return {
            specialty: form.querySelector('input[name="specialty"]')?.value?.trim() || "",
            condition: form.querySelector('input[name="core_condition"]')?.value?.trim() || "",
            presentation: form.querySelector('input[name="presentation"]')?.value?.trim() || "",
            year: form.querySelector('select[name="year"]')?.value?.trim() || "",
            summative: form.querySelector('select[name="summative_formative"]')?.value?.trim() || "",
            skillCount: skillChecks.length,
            skills: skillChecks.map((el) => el.value)
        };
    }

    function isGeneratorSetupEmpty(ctx) {
        return (
            !ctx.specialty &&
            !ctx.condition &&
            !ctx.presentation &&
            !ctx.year &&
            !ctx.summative &&
            ctx.skillCount === 0
        );
    }

    function isGeneratorSetupComplete(ctx) {
        return Boolean(
            ctx.specialty &&
                ctx.condition &&
                ctx.presentation &&
                ctx.year &&
                ctx.summative &&
                ctx.skillCount > 0
        );
    }

    function formatQuestionCountLabel(count) {
        const n = Number(count);
        if (!Number.isFinite(n) || n < 1) {
            return "";
        }
        return n === 1 ? "1 question" : `${n} questions`;
    }

    function buildPlannedGenerationSummary(ctx) {
        const countLabel = formatQuestionCountLabel(ctx.skillCount);
        const parts = [];
        if (countLabel) {
            parts.push(`${countLabel} planned`);
        }
        if (ctx.condition) {
            parts.push(ctx.condition);
        }
        if (ctx.presentation) {
            parts.push(ctx.presentation);
        }
        if (parts.length > 0) {
            return parts.join(" · ");
        }
        if (ctx.skillCount > 0) {
            return `Ready to generate ${ctx.skillCount} question${ctx.skillCount === 1 ? "" : "s"}.`;
        }
        return "";
    }

    function updateGenContextCompactLine() {
        if (!genContextCompact || generationSummaryPhase !== "idle") {
            return;
        }

        if (questions.length > 0) {
            const n = questions.length;
            genContextCompact.textContent =
                n === 1 ? "1 question generated" : `${n} questions generated`;
            return;
        }

        const ctx = getGeneratorFormContext();
        if (isGeneratorSetupEmpty(ctx)) {
            genContextCompact.textContent = "No questions generated yet.";
            return;
        }
        if (!isGeneratorSetupComplete(ctx)) {
            genContextCompact.textContent = "Complete question setup to generate drafts.";
            return;
        }

        const planned = buildPlannedGenerationSummary(ctx);
        genContextCompact.textContent =
            planned || `Ready to generate ${ctx.skillCount} question${ctx.skillCount === 1 ? "" : "s"}.`;
    }

    function syncGenContextCompactVisibility() {
        if (!genContextCompact) {
            return;
        }
        const hideSetupSummary =
            generationSummaryPhase !== "idle" &&
            progressCompact &&
            !progressCompact.classList.contains("hidden");
        genContextCompact.classList.toggle("hidden", hideSetupSummary);
        if (!hideSetupSummary) {
            updateGenContextCompactLine();
        }
    }

    function updateGenerationContextFromForm() {
        if (!form) {
            return;
        }

        const ctx = getGeneratorFormContext();
        const specialtyEl = document.getElementById("context-specialty");
        const conditionEl = document.getElementById("context-condition");
        const presentationEl = document.getElementById("context-presentation");
        const numEl = document.getElementById("context-num");
        const skillsEl = document.getElementById("context-skills");

        if (specialtyEl) {
            specialtyEl.innerText = ctx.specialty
                ? "Specialty: " + ctx.specialty
                : "Specialty: Not set";
        }
        if (conditionEl) {
            conditionEl.innerText = ctx.condition
                ? "Condition: " + ctx.condition
                : "Condition: Not set";
        }
        if (presentationEl) {
            presentationEl.innerText = ctx.presentation
                ? "Presentation: " + ctx.presentation
                : "Presentation: Not set";
        }
        if (numEl) {
            numEl.innerText =
                ctx.skillCount > 0
                    ? "Questions requested: " + ctx.skillCount
                    : "Questions requested: Not set";
        }
        if (skillsEl) {
            skillsEl.innerText =
                ctx.skills.length > 0
                    ? "Skills: " + ctx.skills.join(", ")
                    : "Skills: Not set";
        }

        syncGenContextCompactVisibility();
        updateSkillQuestionCountDisplay();
    }

    function validateGeneratorFormBeforeSubmit() {
        if (!form) {
            return null;
        }

        const specialty = form.querySelector('input[name="specialty"]')?.value?.trim() || "";
        const condition = form.querySelector('input[name="core_condition"]')?.value?.trim() || "";
        const presentation = form.querySelector('input[name="presentation"]')?.value?.trim() || "";
        const year = form.querySelector('select[name="year"]')?.value?.trim() || "";
        const summative = form.querySelector('select[name="summative_formative"]')?.value?.trim() || "";

        if (!specialty) {
            return "Specialty is required.";
        }
        if (!condition) {
            return "Core condition is required.";
        }
        if (!presentation) {
            return "Presentation is required.";
        }
        if (!year) {
            return "Year level is required.";
        }
        if (!summative) {
            return "Summative/Formative is required.";
        }
        if (getSelectedSkillCount() === 0) {
            return SKILLS_REQUIRED_MESSAGE;
        }

        return null;
    }

    function getPreservedSelectionQuestionId() {
        if (editPanelWrapper && editPanelWrapper.classList.contains("open") && currentEditQuestionId) {
            return currentEditQuestionId;
        }
        if (
            filteredQuestions.length > 0 &&
            currentIndex >= 0 &&
            currentIndex < filteredQuestions.length &&
            filteredQuestions[currentIndex]
        ) {
            return filteredQuestions[currentIndex].id;
        }
        if (activeGenerationRun && activeGenerationRun.preserveSelectionId) {
            return activeGenerationRun.preserveSelectionId;
        }
        return null;
    }

    function getLatestGeneratedQuestionId() {
        if (activeGenerationRun && activeGenerationRun.lastGeneratedQuestionId) {
            return activeGenerationRun.lastGeneratedQuestionId;
        }
        if (questions.length > 0) {
            return questions[questions.length - 1].id;
        }
        return null;
    }

    function resolveGenerationProgressState(message, isRunning, options) {
        const opts = options || {};
        let phase = opts.phase;
        if (!phase) {
            if (isRunning) {
                phase = "running";
            } else if (/generation complete/i.test(message)) {
                phase = "complete";
            } else if (
                /generation stopped|could not be started|timed out|failed to generate/i.test(message)
            ) {
                phase = "error";
            } else {
                phase = "idle";
            }
        }

        let completed = opts.completed;
        let total = opts.total;
        if ((completed == null || total == null) && activeGenerationRun) {
            if (completed == null && Number.isFinite(Number(activeGenerationRun.completed))) {
                completed = Number(activeGenerationRun.completed);
            }
            if (total == null && Number.isFinite(Number(activeGenerationRun.total))) {
                total = Number(activeGenerationRun.total);
            }
        }

        const countMatch = String(message).match(/(\d+)\s+of\s+(\d+)/);
        if (countMatch) {
            if (completed == null) {
                completed = Number(countMatch[1]);
            }
            if (total == null) {
                total = Number(countMatch[2]);
            }
        }

        return { phase, message, completed, total, opts };
    }

    function buildCompactGenerationStatusText(state) {
        const phase = state.phase;
        const message = state.message;
        const completed = state.completed;
        const total = state.total;
        const hasCounts =
            completed != null &&
            total != null &&
            Number.isFinite(Number(completed)) &&
            Number.isFinite(Number(total)) &&
            Number(total) > 0;

        if (hasCounts) {
            if (phase === "running") {
                return `Generating: ${completed} of ${total} questions generated`;
            }
            if (phase === "complete") {
                return `Generation complete: ${completed} of ${total} questions generated`;
            }
            if (phase === "error") {
                return `Generation stopped: ${completed} of ${total} questions generated.`;
            }
        }

        if (phase === "running" && /starting/i.test(message)) {
            return "Starting generation...";
        }
        if (phase === "error") {
            const short = String(message).trim();
            return short.length > 96 ? short.slice(0, 93) + "…" : short;
        }
        return message;
    }

    function renderGenerationProgressPanels(message, isRunning, options) {
        const state = resolveGenerationProgressState(message, isRunning, options);
        const opts = state.opts;
        const phase = state.phase;
        const compactText = buildCompactGenerationStatusText(state);

        generationSummaryPhase = phase;

        if (progressCompact) {
            if (phase === "idle") {
                progressCompact.innerHTML = "";
                progressCompact.classList.add("hidden");
            } else {
                const spinner =
                    phase === "running"
                        ? '<span class="progress-spinner progress-spinner--small" aria-hidden="true"></span>'
                        : "";
                const toneClass =
                    phase === "error"
                        ? " gen-status-compact--error"
                        : phase === "complete"
                          ? " gen-status-compact--complete"
                          : " gen-status-compact--running";
                progressCompact.innerHTML = `<div class="gen-status-compact${toneClass}">${spinner}<span class="gen-status-compact-text">${escapeHtml(
                    compactText
                )}</span></div>`;
                progressCompact.classList.remove("hidden");
            }
        }

        syncGenContextCompactVisibility();

        if (!progress) {
            return;
        }

        const latestId = opts.latestQuestionId || getLatestGeneratedQuestionId();
        const showLatestLink = opts.showLatestLink === true && latestId;
        const latestLinkHtml = showLatestLink
            ? `<button type="button" class="btn btn-quiet btn--small go-to-latest-draft-btn" data-question-id="${escapeHtml(
                  String(latestId)
              )}">Go to latest draft</button>`
            : "";
        const newDraftHtml = opts.newDraftGenerated
            ? `<span class="progress-new-draft">New draft generated</span>`
            : "";

        if (phase === "running") {
            progress.innerHTML = `
                <div class="progress-status">
                    <span class="progress-spinner" aria-hidden="true"></span>
                    <span class="progress-status-text">${escapeHtml(message)}</span>
                </div>
                ${newDraftHtml}
                ${latestLinkHtml}
            `;
        } else if (phase === "idle") {
            progress.innerHTML = `<p>${escapeHtml(message)}</p>`;
        } else {
            progress.innerHTML = `
                <p class="progress-complete-message">${escapeHtml(message)}</p>
                ${latestLinkHtml}
            `;
        }
    }

    function setGenerationProgress(message, isRunning = false, options = {}) {
        renderGenerationProgressPanels(message, isRunning, options);
    }

    function refreshQuestionListAfterGeneration(newQuestion, hadNoQuestionsBefore) {
        let targetId = getPreservedSelectionQuestionId();

        if (hadNoQuestionsBefore && questions.length > 0) {
            targetId = questions[0].id;
        } else if (targetId === null && questions.length > 0) {
            targetId = questions[0].id;
        }

        if (activeGenerationRun && newQuestion && newQuestion.id != null) {
            activeGenerationRun.lastGeneratedQuestionId = String(newQuestion.id);
        }

        applyFilter(targetId);
    }

    function showInfoModal(title, message) {
        modalTitle.textContent = title;
        modalMessage.textContent = message;

        modalCancel.classList.add("hidden");
        modalConfirm.classList.add("hidden");
        modalOk.classList.remove("hidden");

        modalOverlay.classList.remove("hidden");
    }

    function showConfirmModal(title, message, onConfirm) {
        modalTitle.textContent = title;
        modalMessage.textContent = message;

        modalOk.classList.add("hidden");
        modalCancel.classList.remove("hidden");
        modalConfirm.classList.remove("hidden");

        modalConfirmAction = onConfirm;
        modalOverlay.classList.remove("hidden");
    }

    function closeModal() {
        modalOverlay.classList.add("hidden");
        modalConfirmAction = null;
    }

    function openExportModal() {
        exportModal.classList.remove("hidden");
    }

    function closeExportModal() {
        exportModal.classList.add("hidden");
    }

    function openEditPanel() {
        editPanelWrapper.classList.add("open");
        document.body.classList.add("edit-panel-open");
        if (editPanelBody) {
            editPanelBody.scrollTop = 0;
        }
    }

    function closeEditPanel() {
        editPanelWrapper.classList.remove("open");
        document.body.classList.remove("edit-panel-open");
        currentEditQuestionId = null;

        if (autosaveTimer) {
            clearTimeout(autosaveTimer);
            autosaveTimer = null;
        }
    }

    function formatDateTimeForHeader() {
        const now = new Date();
        return now.toLocaleString("en-GB", {
            day: "2-digit",
            month: "2-digit",
            year: "numeric",
            hour: "2-digit",
            minute: "2-digit"
        });
    }

    function buildTagsString(q) {
        const tags = [
            q.skill,
            q.specialty,
            q.condition_name,
            q.presentation
        ].filter(Boolean);

        return tags.length > 0 ? tags.join(" | ") : "None";
    }

    /**
     * Main generator stem observation grammar (deterministic clean-up for Create SBA Questions).
     * @returns {{ stem: string, changed: boolean, needsReview: boolean }}
     */
    function normalizeMainStemObservationGrammar(stem) {
        let s = String(stem || "");
        const original = s;

        if (s === "") {
            return { stem: s, changed: false, needsReview: false };
        }

        s = repairMainStandaloneRespiratoryRate(s);
        s = repairMainBareLeadingPulse(s);
        s = repairMainMalformedNounPhrases(s);
        s = repairMainCombinedExaminationAndVitals(s);
        s = repairMainMixedSentenceVitals(s);

        const hasObservationIssues =
            hasMainClippedObservationGrammar(s) || hasMainMalformedObservationPhrase(s);
        if (isMainListStyleObservationPhrase(s) && !hasObservationIssues) {
            return { stem: s, changed: s !== original, needsReview: false };
        }

        const inTemperatureIsSentence = /\b(?:His|Her|Their)\s+(?:temperature|temp)\s+is\s+/i.test(s);

        s = s.replace(/\b(His|Her|Their|The)\s+pulse\s+(\d+)/gi, "$1 pulse is $2");
        s = s.replace(/,\s*pulse\s+(?!is\s|of\s)(\d+)\s+bpm/gi, ", pulse is $1 bpm");
        s = s.replace(/\band\s+pulse\s+(?!is\s|of\s)(\d+)\s+bpm/gi, "and pulse is $1 bpm");
        s = s.replace(/\b(His|Her|Their)\s+BP\s+(\d+\s*\/\s*\d+)/gi, "$1 blood pressure is $2");
        s = s.replace(/\b(His|Her|Their)\s+blood\s+pressure\s+(\d+\s*\/\s*\d+)/gi, "$1 blood pressure is $2");
        s = s.replace(/,\s*BP\s+(\d+\s*\/\s*\d+)/gi, ", blood pressure is $1");
        s = s.replace(/,\s*blood\s+pressure\s+(?!is\s)(\d+\s*\/\s*\d+)/gi, ", blood pressure is $1");
        s = s.replace(/\band\s+BP\s+(\d+\s*\/\s*\d+)/gi, "and blood pressure is $1");
        s = s.replace(/\band\s+blood\s+pressure\s+(?!is\s)(\d+\s*\/\s*\d+)/gi, "and blood pressure is $1");
        if (inTemperatureIsSentence) {
            s = s.replace(
                /,\s*(?:a\s+)?respiratory\s+rate\s+(?:of\s+)?(?!is\s)(\d+)\s+breaths/gi,
                ", respiratory rate is $1 breaths"
            );
            s = s.replace(
                /\band\s+(?:a\s+)?respiratory\s+rate\s+(?:of\s+)?(?!is\s)(\d+)\s+breaths/gi,
                "and respiratory rate is $1 breaths"
            );
        } else {
            s = s.replace(/,\s*respiratory\s+rate\s+(?!is\s|of\s)(\d+)\s+breaths/gi, ", a respiratory rate of $1 breaths");
            s = s.replace(
                /\band\s+respiratory\s+rate\s+(?!is\s|of\s)(\d+)\s+breaths/gi,
                "and a respiratory rate of $1 breaths"
            );
        }
        s = s.replace(/\b(His|Her|Their)\s+(heart\s+rate|HR)\s+(?!is\s)(\d+)/gi, "$1 $2 is $3");
        s = s.replace(/\b(His|Her|Their)\s+(temperature|temp)\s+(?!is\s)(\d+)/gi, "$1 $2 is $3");
        s = s.replace(/\b(His|Her|Their)\s+(respiratory\s+rate|RR)\s+(?!is\s)(\d+)/gi, "$1 $2 is $3");
        s = s.replace(/\b(His|Her|Their)\s+oxygen\s+saturation\s+(?!is\s|of\s)(\d+)/gi, "$1 oxygen saturation is $2");
        if (/\b(?:His|Her|Their)\s+/i.test(s)) {
            s = s.replace(/,\s*respiratory\s+rate\s+(?!is\s|of\s)(\d+)\s+breaths/gi, ", respiratory rate is $1 breaths");
        }

        return {
            stem: s,
            changed: s !== original,
            needsReview: hasMainClippedObservationGrammar(s) || hasMainMalformedObservationPhrase(s)
        };
    }

    function repairMainStandaloneRespiratoryRate(stem) {
        return String(stem || "").replace(
            /(^|[.!?]\s+)(Respiratory rate)\s+(?!is\s)(\d+)(\s+breaths per minute)/gi,
            "$1$2 is $3$4"
        );
    }

    function repairMainBareLeadingPulse(stem) {
        return String(stem || "").replace(
            /(^|[.!?]\s+)(Pulse)\s+(?!is\s|of\s)(\d+)(\s+bpm)/gi,
            "$1$2 is $3$4"
        );
    }

    function repairMainMixedSentenceVitals(stem) {
        let s = String(stem || "");
        const inPossessiveVitalsChain =
            /\b(?:His|Her|Their)\s+(?:temperature|temp)\s+is\s+/i.test(s) ||
            /\b(?:His|Her|Their)\s+pulse\s/i.test(s);
        if (!inPossessiveVitalsChain) {
            return s;
        }
        s = s.replace(/,\s*pulse\s+(?!is\s|of\s)(\d+)\s+bpm/gi, ", pulse is $1 bpm");
        s = s.replace(/,\s*BP\s+(\d+\s*\/\s*\d+\s*mmHg)/gi, ", blood pressure is $1");
        s = s.replace(
            /,\s*(?:a\s+)?respiratory\s+rate\s+(?:of\s+)?(?!is\s)(\d+)\s+breaths per minute/gi,
            ", respiratory rate is $1 breaths per minute"
        );
        return s;
    }

    function repairMainMalformedNounPhrases(stem) {
        let s = String(stem || "");
        s = s.replace(/\ba respiratory rate\s+(?!of\s|is\s)(\d+)/gi, "a respiratory rate of $1");
        s = s.replace(/\ban oxygen saturation\s+(?!of\s|is\s)(\d+)/gi, "an oxygen saturation of $1");
        s = s.replace(/\ba pulse\s+(?!of\s|is\s)(\d+)/gi, "a pulse of $1");
        s = s.replace(/\ba BP\s+(\d+\s*\/\s*\d+)/gi, "a blood pressure of $1");
        s = s.replace(/\ba blood pressure\s+(?!of\s|is\s)(\d+\s*\/\s*\d+)/gi, "a blood pressure of $1");
        if (/\b(?:She|He) has\b/i.test(s) && /\ba respiratory rate of\b/i.test(s)) {
            s = s.replace(/\band\s+oxygen saturation\s+is\s+(\d+)/gi, "and oxygen saturation of $1");
        }
        return s;
    }

    function repairMainCombinedExaminationAndVitals(stem) {
        return String(stem || "").replace(
            /\b(She|He) has ([^.,;]+?),\s*respiratory rate\s+(?!is\s|of\s)(\d+)\s+breaths per minute(?:\s+and\s+oxygen saturation\s+is\s+([^.;]+))?/gi,
            function (_match, pronoun, finding, rr, o2) {
                const possessive = pronoun.toLowerCase() === "she" ? "Her" : "His";
                let findingText = String(finding || "").trim();
                if (
                    findingText !== "" &&
                    !/\bon auscultation\b/i.test(findingText) &&
                    !/\bon examination\b/i.test(findingText)
                ) {
                    findingText += " on auscultation";
                }
                let tail = possessive + " respiratory rate is " + rr + " breaths per minute";
                if (o2) {
                    tail += " and oxygen saturation is " + String(o2).trim();
                }
                return pronoun + " has " + findingText + ". " + tail;
            }
        );
    }

    function startsWithMainPossessiveObservationSentence(text) {
        return /^(?:On examination[,:]?\s+)?(?:His|Her|Their)\s+/i.test(String(text || "").trim());
    }

    function hasMainClippedPossessivePulse(text) {
        return /\b(?:His|Her|Their|The)\s+pulse\s+(?!is\s)\d+/i.test(text);
    }

    function hasMainClippedPossessiveBp(text) {
        return /\b(?:His|Her|Their)\s+BP\s+\d+\s*\/\s*\d+/i.test(text);
    }

    function hasMainClippedPossessiveBloodPressure(text) {
        return /\b(?:His|Her|Their)\s+blood\s+pressure\s+(?!is\s)\d+/i.test(text);
    }

    function hasMainBareBloodPressureOrBpInPossessiveContext(text) {
        if (!/\b(?:His|Her|Their)\s+/i.test(text)) {
            return false;
        }
        if (
            /\b(?:His|Her|Their)\s+BP\s+is\s+\d/i.test(text) ||
            /\b(?:His|Her|Their)\s+blood\s+pressure\s+is\s+\d/i.test(text) ||
            /\bblood\s+pressure\s+is\s+\d/i.test(text) ||
            /\bBP\s+is\s+\d/i.test(text)
        ) {
            return false;
        }
        return (
            /,\s*(?:blood pressure|BP)\s+\d/i.test(text) ||
            /\band\s+BP\s+\d/i.test(text) ||
            /\band\s+blood\s+pressure\s+(?!is\s)\d/i.test(text)
        );
    }

    function hasMainBarePulseInPossessiveContext(text) {
        if (!/\b(?:His|Her|Their)\s+/i.test(text)) {
            return false;
        }
        if (/\b(?:His|Her|Their)\s+pulse\s+is\s+\d/i.test(text) || /,\s*pulse\s+is\s+\d/i.test(text)) {
            return false;
        }
        return /,\s*pulse\s+(?!is\s)\d+\s+bpm/i.test(text) || /\band\s+pulse\s+(?!is\s)\d+\s+bpm/i.test(text);
    }

    function hasMainClippedCommaRespiratoryRate(text) {
        if (/\b(?:She|He) has [^.,;]+,\s*(?:a\s+)?respiratory rate\s+(?!(?:is|of)\s)\d+/i.test(text)) {
            return true;
        }
        if (!/\b(?:His|Her|Their|She|He)\s+/i.test(text)) {
            return false;
        }
        return /,\s*(?:a\s+)?respiratory\s+rate\s+(?!(?:is|of)\s)\d+\s+breaths/i.test(text);
    }

    function hasMainStandaloneClippedRespiratoryRate(text) {
        return /(?:^|[.!?]\s+)Respiratory rate\s+(?!is\s)\d+\s+breaths/i.test(String(text || ""));
    }

    function hasMainBareLeadingPulse(text) {
        return /(?:^|[.!?]\s+)Pulse\s+(?!is\s|of\s)\d+\s+bpm/i.test(String(text || ""));
    }

    function hasMainMixedSentenceObservationMarkers(text) {
        const t = String(text || "");
        if (/\b(?:pulse|blood pressure|BP|respiratory rate)\s+is\s+/i.test(t)) {
            return true;
        }
        if (/\b(?:His|Her|Their)\s+(?:pulse|respiratory rate|RR|BP|blood pressure)\s+(?!is\s|of\s)\d+/i.test(t)) {
            return true;
        }
        return hasMainBareLeadingPulse(t) || hasMainStandaloneClippedRespiratoryRate(t);
    }

    function hasMainClippedObservationGrammar(text) {
        const t = String(text || "");
        if (t === "") {
            return false;
        }
        if (hasMainStandaloneClippedRespiratoryRate(t)) {
            return true;
        }
        if (hasMainBareLeadingPulse(t)) {
            return true;
        }
        if (hasMainClippedPossessivePulse(t)) {
            return true;
        }
        if (hasMainClippedPossessiveBp(t)) {
            return true;
        }
        if (hasMainClippedPossessiveBloodPressure(t)) {
            return true;
        }
        if (hasMainBareBloodPressureOrBpInPossessiveContext(t)) {
            return true;
        }
        if (hasMainBarePulseInPossessiveContext(t)) {
            return true;
        }
        if (hasMainClippedCommaRespiratoryRate(t)) {
            return true;
        }
        const otherClipped = [
            /\b(?:His|Her|Their)\s+(?:heart\s+rate|HR)\s+(?!is\s)\d+/i,
            /\b(?:His|Her|Their)\s+(?:temperature|temp)\s+(?!is\s)\d+/i,
            /\b(?:His|Her|Their)\s+(?:respiratory\s+rate|RR)\s+(?!is\s)\d+/i,
            /\b(?:His|Her|Their)\s+oxygen\s+saturation\s+(?!is\s|of\s)\d+/i
        ];
        return otherClipped.some(function (pattern) {
            return pattern.test(t);
        });
    }

    function isMainListStyleObservationPhrase(text) {
        const t = String(text || "").trim();
        if (
            t === "" ||
            startsWithMainPossessiveObservationSentence(t) ||
            hasMainClippedObservationGrammar(t) ||
            hasMainMixedSentenceObservationMarkers(t)
        ) {
            return false;
        }
        const segments = t.split(/[.;]\s+/).filter(function (part) {
            return part.trim() !== "";
        });
        for (let i = 0; i < segments.length; i++) {
            const listMatches = segments[i].match(
                /(?:^|,\s*)(?:temperature|temp|pulse|heart rate|HR|BP|blood pressure|respiratory rate|RR|oxygen saturation)\s+[\d./%°]+/gi
            );
            if (listMatches && listMatches.length >= 2) {
                return true;
            }
        }
        return false;
    }

    function applyMainStemObservationGrammarToQuestion(question) {
        if (!question || typeof question !== "object") {
            return;
        }
        const norm = normalizeMainStemObservationGrammar(question.stem || "");
        question.stem = norm.stem;
    }

    function prepareMainStemForSave(stem) {
        let norm = normalizeMainStemObservationGrammar(stem);
        if (hasMainClippedObservationGrammar(norm.stem) || hasMainMalformedObservationPhrase(norm.stem)) {
            norm = normalizeMainStemObservationGrammar(norm.stem);
        }
        return norm;
    }

    const MAIN_STEM_OBSERVATION_SAVE_BLOCKED =
        "Please correct observation wording before saving.";
    const MAIN_CORRECT_ANSWER_SAVE_BLOCKED =
        "Please select a valid correct answer before saving.";
    const MAIN_STEM_OBSERVATION_ACCEPT_BLOCKED_MSG =
        "Suggestion not offered for one-click acceptance because it may introduce observation wording issues. Please edit manually if required.";
    const MAIN_STEM_OBSERVATION_SUGGESTION_NOTE =
        "Suggestion not offered for one-click acceptance because it may introduce observation wording issues. Please edit manually if required.";
    const MAIN_CORRECT_ANSWER_SUGGESTION_NOTE =
        "Suggestion not offered for one-click acceptance because the suggested correct answer is incomplete. Please edit manually if required.";
    const MAIN_CORRECT_ANSWER_ACCEPT_BLOCKED_MSG = MAIN_CORRECT_ANSWER_SUGGESTION_NOTE;

    function hasMainMalformedObservationPhrase(text) {
        const t = String(text || "");
        if (t === "") {
            return false;
        }
        if (/\ba respiratory rate\s+(?!of\s)\d+/i.test(t)) {
            return true;
        }
        if (/\ban oxygen saturation\s+(?!of\s|is\s)\d+/i.test(t)) {
            return true;
        }
        if (/,\s*oxygen saturation\s+(?!is\s|of\s)\d+/i.test(t)) {
            return true;
        }
        if (/\band\s+oxygen saturation\s+(?!is\s|of\s)\d+/i.test(t)) {
            return true;
        }
        if (/\ba pulse\s+(?!of\s|is\s)\d+/i.test(t)) {
            return true;
        }
        if (/\ba BP\s+(?!of\s)\d/i.test(t)) {
            return true;
        }
        return false;
    }

    function hasMainUnsafeObservationGrammar(text) {
        return hasMainClippedObservationGrammar(text) || hasMainMalformedObservationPhrase(text);
    }

    function regressesMainObservationGrammar(current, suggested) {
        const cur = String(current || "").trim();
        const sug = String(suggested || "").trim();
        if (cur === "" || sug === "") {
            return false;
        }
        if (hasMainMalformedObservationPhrase(cur) || hasMainClippedObservationGrammar(cur)) {
            return false;
        }
        if (/\b(?:His|Her|Their)\s+pulse\s+is\s+\d+/i.test(cur) && hasMainClippedPossessivePulse(sug)) {
            return true;
        }
        if (/\bblood\s+pressure\s+is\s+\d+/i.test(cur) && hasMainClippedPossessiveBloodPressure(sug)) {
            return true;
        }
        if (/\b(?:His|Her|Their)\s+(?:respiratory\s+rate|RR)\s+is\s+\d+/i.test(cur) && /\b(?:His|Her|Their)\s+(?:respiratory\s+rate|RR)\s+(?!is\s)\d+/i.test(sug)) {
            return true;
        }
        return hasMainMalformedObservationPhrase(sug) || hasMainClippedObservationGrammar(sug);
    }

    function validateMainStemSuggestionAcceptance(suggested, current) {
        const sug = String(suggested || "").trim();
        const cur = String(current || "").trim();
        if (sug === "") {
            return { valid: false, stem: sug };
        }
        if (isMainListStyleObservationPhrase(sug) && !hasMainMalformedObservationPhrase(sug)) {
            return { valid: true, stem: sug };
        }
        let norm = normalizeMainStemObservationGrammar(sug);
        if (hasMainUnsafeObservationGrammar(norm.stem)) {
            norm = normalizeMainStemObservationGrammar(norm.stem);
        }
        if (hasMainUnsafeObservationGrammar(norm.stem)) {
            return { valid: false, stem: norm.stem };
        }
        if (regressesMainObservationGrammar(cur, norm.stem)) {
            return { valid: false, stem: norm.stem };
        }
        return { valid: true, stem: norm.stem };
    }

    function isMainStemSuggestionAcceptable(question, suggestedText) {
        const current = String((question && question.stem) || "").trim();
        return validateMainStemSuggestionAcceptance(suggestedText, current).valid;
    }

    function upsertQuestionInArray(question) {
        if (!isMainGeneratorWorkspaceQuestion(question)) {
            return;
        }
        const existingIndex = questions.findIndex(q => String(q.id) === String(question.id));
        const merged = {
            ...question,
            source_tool: MAIN_GENERATOR_SOURCE_TOOL
        };
        if (!Object.prototype.hasOwnProperty.call(merged, "style_warnings") && existingIndex !== -1) {
            const prev = questions[existingIndex].style_warnings;
            if (Array.isArray(prev)) {
                merged.style_warnings = prev;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "ai_review") && existingIndex !== -1) {
            const prevReview = questions[existingIndex].ai_review;
            if (prevReview && typeof prevReview === "object") {
                merged.ai_review = prevReview;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "clinical_safety_review") && existingIndex !== -1) {
            const prevSafety = questions[existingIndex].clinical_safety_review;
            if (prevSafety && typeof prevSafety === "object") {
                merged.clinical_safety_review = prevSafety;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "ai_repair") && existingIndex !== -1) {
            const prevRepair = questions[existingIndex].ai_repair;
            if (prevRepair && typeof prevRepair === "object") {
                merged.ai_repair = prevRepair;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "tutor_review") && existingIndex !== -1) {
            const prevTutorReview = questions[existingIndex].tutor_review;
            if (prevTutorReview && typeof prevTutorReview === "object") {
                merged.tutor_review = prevTutorReview;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "tutor_review_content_hash") && existingIndex !== -1) {
            const prevHash = questions[existingIndex].tutor_review_content_hash;
            if (prevHash) {
                merged.tutor_review_content_hash = prevHash;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "tutor_review_loading") && existingIndex !== -1) {
            if (questions[existingIndex].tutor_review_loading) {
                merged.tutor_review_loading = true;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "tutor_review_error") && existingIndex !== -1) {
            const prevErr = questions[existingIndex].tutor_review_error;
            if (prevErr) {
                merged.tutor_review_error = prevErr;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "suggestion_ignored_fields") && existingIndex !== -1) {
            const prevIgnored = questions[existingIndex].suggestion_ignored_fields;
            if (prevIgnored && typeof prevIgnored === "object") {
                merged.suggestion_ignored_fields = prevIgnored;
            }
        }
        if (!Object.prototype.hasOwnProperty.call(merged, "validator_after_repair") && existingIndex !== -1) {
            const prevVal = questions[existingIndex].validator_after_repair;
            if (Array.isArray(prevVal)) {
                merged.validator_after_repair = prevVal;
            }
        }

        applyMainStemObservationGrammarToQuestion(merged);

        if (existingIndex !== -1) {
            questions[existingIndex] = merged;
        } else {
            questions.push(merged);
        }
    }

    function styleWarningSeverityRank(severity) {
        const s = String(severity || "warning").toLowerCase();
        if (s === "high") {
            return 0;
        }
        if (s === "error") {
            return 1;
        }
        if (s === "warning") {
            return 2;
        }
        if (s === "info") {
            return 3;
        }
        return 2;
    }

    function styleWarningSeverityClass(severity) {
        const s = String(severity || "warning").toLowerCase();
        if (s === "high") {
            return "style-severity-high";
        }
        if (s === "error") {
            return "style-severity-error";
        }
        if (s === "info") {
            return "style-severity-info";
        }

        return "style-severity-warning";
    }

    const STYLE_WARNING_CODE_LABELS = {
        "options.correct_answer_much_longer_than_distractors":
            "The correct answer is noticeably longer than the other options.",
        "options.mixed_categories":
            "The options may mix different types of answer (for example diagnosis and treatment in the same list).",
        "options.high_similarity": "Two or more options look very similar and may overlap in meaning.",
        "stem.history_of_phrase": "The stem uses wording that could be stated more directly.",
        "batch_repetition.management_emergency_same_treatment_family":
            "Management and Emergency Management in this batch may repeat the same treatment approach.",
        "batch_repetition.management_emergency_same_answer":
            "Management and Emergency Management in this batch may share the same correct answer.",
        "batch_repetition.same_correct_answer_concept":
            "Another question in this batch may already use the same correct-answer concept.",
        "skill_alignment.emergency_management_investigation_option":
            "An emergency-management item includes an investigation option where treatment options are expected."
    };

    function humanizeStyleWarning(w) {
        if (!w || typeof w !== "object") {
            return "";
        }
        const msg = String(w.message || "").trim();
        if (msg !== "") {
            return msg;
        }
        const code = String(w.code || "").trim();
        if (code === "") {
            return "A formatting or structure issue was detected.";
        }
        if (Object.prototype.hasOwnProperty.call(STYLE_WARNING_CODE_LABELS, code)) {
            return STYLE_WARNING_CODE_LABELS[code];
        }
        return code
            .split(".")
            .map(function (part) {
                return part.replace(/_/g, " ");
            })
            .join(" — ");
    }

    function statusChip(label, value, tone) {
        const toneClass = tone ? ` question-status-chip--${tone}` : "";
        const labelPart =
            label && String(label).trim() !== ""
                ? `<span class="question-status-chip-label">${escapeHtml(label)}:</span> `
                : "";
        return `<span class="question-status-chip${toneClass}">${labelPart}${escapeHtml(value)}</span>`;
    }

    function getQuestionDraftStatusLabel(q) {
        const status = String(q.status || "Draft");
        if (status === "Rejected") {
            return { value: "Rejected", tone: "danger" };
        }
        if (status === "Done") {
            return { value: "Approved", tone: "success" };
        }
        return { value: "Draft", tone: "neutral" };
    }

    function getSuggestionsStatusLabel(q) {
        const repair = q.ai_repair;
        if (repair && repair.repair_blocked) {
            return { value: "Blocked", tone: "danger" };
        }
        if (repair && repair.repaired_question) {
            return { value: "Available", tone: "info" };
        }
        return { value: "None", tone: "neutral" };
    }

    function getClinicalSafetyStatusLabel(q) {
        if (!q.clinical_safety_review || typeof q.clinical_safety_review !== "object") {
            return { value: "Not checked", tone: "neutral" };
        }
        const rating = String(q.clinical_safety_review.clinical_safety_rating || "");
        if (rating === "unsafe") {
            return { value: "Unsafe", tone: "danger" };
        }
        if (rating === "needs_faculty_review") {
            return { value: "Needs review", tone: "warn" };
        }
        return { value: "No major concerns", tone: "success" };
    }

    function tutorFriendlyClinicalSafetyRating(rating) {
        const r = String(rating || "");
        if (r === "unsafe") {
            return "Do not use without editing";
        }
        if (r === "needs_faculty_review") {
            return "Needs clinical review";
        }
        if (r === "no_major_concerns") {
            return "No major clinical concerns";
        }
        return aiReviewRatingLabel(r);
    }

    function getTutorReviewStatusLabel(q) {
        const status = String(q.status || "Draft");
        if (status === "Done" || status === "Rejected") {
            return null;
        }
        return { value: "Tutor review required", tone: "warn" };
    }

    function countFormattingNotices(q) {
        const draft = Array.isArray(q.style_warnings) ? q.style_warnings.length : 0;
        return draft;
    }

    function getRepairChangeCounts(q) {
        const repair = q.ai_repair;
        if (!repair || !Array.isArray(repair.change_log)) {
            return { wording: 0, content: 0 };
        }
        let wording = 0;
        let content = 0;
        repair.change_log.forEach(function (e) {
            const isLow =
                (e.auto_apply_eligible === true || String(e.repair_risk || e.risk) === "low") &&
                (e.change_type === "style" || e.change_type === "structure") &&
                !e.tutor_review_required;
            if (isLow) {
                wording += 1;
            } else {
                content += 1;
            }
        });
        return { wording: wording, content: content };
    }

    function firstSummarySentence(text) {
        const t = String(text || "").trim();
        if (t === "") {
            return "";
        }
        const match = t.match(/^[^.!?]+[.!?]?/);
        return match ? match[0].trim() : t.length > 220 ? t.slice(0, 217) + "…" : t;
    }

    function renderQuestionMetadata(q) {
        const parts = [
            q.skill ? `Skill: ${q.skill}` : "",
            q.specialty ? `Specialty: ${q.specialty}` : "",
            q.condition_name ? `Condition: ${q.condition_name}` : "",
            q.presentation ? `Presentation: ${q.presentation}` : ""
        ].filter(Boolean);
        if (parts.length === 0) {
            return "";
        }
        const items = parts
            .map((part) => `<span class="question-meta-item">${escapeHtml(part)}</span>`)
            .join("");
        return `<div class="question-card-meta" role="group" aria-label="Question metadata">${items}</div>`;
    }

    function renderQuestionStatusBar(q) {
        const mainStatus = getQuestionDraftStatusLabel(q);

        return `<div class="question-status-bar" role="status" aria-label="Question status">
            ${statusChip("", mainStatus.value, mainStatus.tone)}
        </div>`;
    }

    function renderAiSupportTutorReviewAdvisory(q) {
        const tutor = getTutorReviewStatusLabel(q);
        if (!tutor) {
            return "";
        }
        return `<p class="ai-support-advisory" role="note">${escapeHtml(
            tutor.value
        )}. Review and edit this AI-generated draft before approval.</p>`;
    }

    function formatCompactSuggestionCounts(q) {
        const repair = q.ai_repair;
        if (!repair || repair.repair_blocked || !repair.repaired_question) {
            return "";
        }
        const counts = getRepairChangeCounts(q);
        const parts = [];
        if (counts.wording > 0) {
            parts.push(counts.wording === 1 ? "1 wording change" : `${counts.wording} wording changes`);
        }
        if (counts.content > 0) {
            parts.push(
                counts.content === 1 ? "1 content change to review" : `${counts.content} content changes to review`
            );
        }
        if (parts.length === 0) {
            return "";
        }
        return parts.length === 2 ? `${parts[0]} and ${parts[1]}` : parts[0];
    }

    function getTutorReviewJudgementLabel(judgement) {
        const j = String(judgement || "");
        if (j === "ready_for_tutor_review") {
            return "Ready for tutor review";
        }
        if (j === "do_not_approve_yet") {
            return "Do not approve yet";
        }
        return "Needs edits";
    }

    function getTutorReviewJudgementTone(judgement, clinicalSafety) {
        if (String(clinicalSafety || "") === "unsafe" || judgement === "do_not_approve_yet") {
            return "danger";
        }
        if (judgement === "needs_edits") {
            return "warn";
        }
        if (judgement === "ready_for_tutor_review") {
            return "success";
        }
        return "neutral";
    }

    function normalizeFingerprintText(value) {
        return String(value ?? "")
            .replace(/\r\n/g, "\n")
            .trim();
    }

    function hashStringDjb2(str) {
        let hash = 5381;
        for (let i = 0; i < str.length; i++) {
            hash = ((hash << 5) + hash) ^ str.charCodeAt(i);
        }
        return (hash >>> 0).toString(16);
    }

    function getQuestionContentFingerprint(q) {
        if (!q || typeof q !== "object") {
            return "";
        }
        const parts = TUTOR_REVIEW_FINGERPRINT_FIELDS.map(function (field) {
            return normalizeFingerprintText(q[field]);
        });
        return hashStringDjb2(parts.join("\u001f"));
    }

    const MAIN_GENERATOR_SOURCE_TOOL = "question_generator";
    const LEGACY_TUTOR_REVIEW_KEY_PREFIX = "sba_tutor_reviews_";
    const MAIN_GENERATOR_TUTOR_REVIEW_KEY_PREFIX = "mla_question_generator_tutor_reviews_";

    function isVariantWorkspaceQuestion(question) {
        return String((question && question.source_type) || "").toLowerCase() === "variant";
    }

    function isMainGeneratorWorkspaceQuestion(question) {
        if (!question || typeof question !== "object") {
            return false;
        }
        if (isVariantWorkspaceQuestion(question)) {
            return false;
        }
        const markedTool = String(question.source_tool || "").toLowerCase();
        if (markedTool === "variant_generator") {
            return false;
        }
        return true;
    }

    function filterQuestionsForMainGenerator(questionList) {
        return (Array.isArray(questionList) ? questionList : []).filter(isMainGeneratorWorkspaceQuestion);
    }

    function tutorReviewStorageKey() {
        const workspaceId = localStorage.getItem("workspace_id");
        return workspaceId ? `${MAIN_GENERATOR_TUTOR_REVIEW_KEY_PREFIX}${workspaceId}` : null;
    }

    function legacyTutorReviewStorageKey() {
        const workspaceId = localStorage.getItem("workspace_id");
        return workspaceId ? `${LEGACY_TUTOR_REVIEW_KEY_PREFIX}${workspaceId}` : null;
    }

    function migrateLegacyTutorReviewCacheIfNeeded() {
        const newKey = tutorReviewStorageKey();
        const legacyKey = legacyTutorReviewStorageKey();
        if (!newKey || !legacyKey || localStorage.getItem(newKey)) {
            return;
        }
        const legacyRaw = localStorage.getItem(legacyKey);
        if (!legacyRaw) {
            return;
        }
        try {
            localStorage.setItem(newKey, legacyRaw);
        } catch (error) {
            console.error("Could not migrate legacy tutor review cache.", error);
        }
    }

    function pruneTutorReviewCacheToMainGeneratorQuestions() {
        const key = tutorReviewStorageKey();
        if (!key) {
            return;
        }
        const cache = loadTutorReviewCache();
        const allowedIds = new Set(questions.map(function (q) {
            return String(q.id);
        }));
        let changed = false;
        Object.keys(cache).forEach(function (id) {
            if (!allowedIds.has(id)) {
                delete cache[id];
                changed = true;
            }
        });
        if (changed) {
            try {
                localStorage.setItem(key, JSON.stringify(cache));
            } catch (error) {
                console.error(error);
            }
        }
    }

    function loadTutorReviewCache() {
        const key = tutorReviewStorageKey();
        if (!key) {
            return {};
        }
        try {
            const raw = localStorage.getItem(key);
            if (!raw) {
                return {};
            }
            const parsed = JSON.parse(raw);
            return parsed && typeof parsed === "object" ? parsed : {};
        } catch (error) {
            console.error(error);
            return {};
        }
    }

    function persistTutorReviewForQuestion(q) {
        if (!isMainGeneratorWorkspaceQuestion(q)) {
            return;
        }
        const key = tutorReviewStorageKey();
        if (!key || !q || !q.id) {
            return;
        }
        const cache = loadTutorReviewCache();
        const id = String(q.id);
        if (!q.tutor_review || typeof q.tutor_review !== "object") {
            if (cache[id]) {
                delete cache[id];
                localStorage.setItem(key, JSON.stringify(cache));
            }
            return;
        }
        cache[id] = {
            tutor_review: q.tutor_review,
            tutor_review_content_hash: q.tutor_review_content_hash || "",
            suggestion_ignored_fields: q.suggestion_ignored_fields || {}
        };
        localStorage.setItem(key, JSON.stringify(cache));
    }

    function clearTutorReviewCache() {
        const key = tutorReviewStorageKey();
        if (key) {
            localStorage.removeItem(key);
        }
    }

    function applyTutorReviewCacheToQuestions() {
        const cache = loadTutorReviewCache();
        if (!cache || typeof cache !== "object") {
            return;
        }
        questions = questions.map(function (q) {
            if (!isMainGeneratorWorkspaceQuestion(q)) {
                return q;
            }
            const entry = cache[String(q.id)];
            if (!entry || !entry.tutor_review || typeof entry.tutor_review !== "object") {
                return q;
            }
            return {
                ...q,
                tutor_review: entry.tutor_review,
                tutor_review_content_hash: entry.tutor_review_content_hash || "",
                suggestion_ignored_fields: entry.suggestion_ignored_fields || {}
            };
        });
    }

    function ensureTutorReviewContentHashOnQuestion(q) {
        if (
            q.tutor_review &&
            typeof q.tutor_review === "object" &&
            !q.tutor_review_content_hash &&
            !q.tutor_review_loading &&
            !q.tutor_review_error
        ) {
            q.tutor_review_content_hash = getQuestionContentFingerprint(q);
            persistTutorReviewForQuestion(q);
        }
    }

    function isTutorReviewStale(q) {
        if (!q.tutor_review || typeof q.tutor_review !== "object") {
            return false;
        }
        if (!q.tutor_review_content_hash) {
            return false;
        }
        return q.tutor_review_content_hash !== getQuestionContentFingerprint(q);
    }

    function isTutorReviewCompleteForCurrentVersion(q) {
        if (!q.tutor_review || typeof q.tutor_review !== "object" || q.tutor_review_loading || q.tutor_review_error) {
            return false;
        }
        ensureTutorReviewContentHashOnQuestion(q);
        if (!q.tutor_review_content_hash) {
            return false;
        }
        return q.tutor_review_content_hash === getQuestionContentFingerprint(q);
    }

    function canRequestTutorQuestionReview(q) {
        if (!q) {
            return false;
        }
        if (q.tutor_review_loading || tutorReviewRequestsInFlight.has(String(q.id))) {
            return false;
        }
        if (q.tutor_review_error) {
            return true;
        }
        if (!q.tutor_review || typeof q.tutor_review !== "object") {
            return true;
        }
        return isTutorReviewStale(q);
    }

    function getTutorReviewRunButtonLabel(q) {
        if (isTutorReviewStale(q)) {
            return "Review updated question";
        }
        return "Review question and suggest edits";
    }

    function tutorReviewSuggestionAppliesToSavedQuestion(q, edit) {
        if (!edit || typeof edit !== "object" || !tutorReviewFieldSuggestionDiffers(q, edit)) {
            return false;
        }
        const panelField = mapTutorReviewFieldToEditPanel(edit.field);
        if (!panelField) {
            return false;
        }
        const reviewCurrent = normalizeFingerprintText(edit.current);
        if (reviewCurrent === "") {
            return true;
        }
        const saved = normalizeFingerprintText(getQuestionDraftFieldValue(q, panelField));
        return reviewCurrent === saved;
    }

    function countTutorReviewSuggestions(q) {
        const review = q.tutor_review;
        if (!review || !Array.isArray(review.suggested_edits) || !isTutorReviewSuggestedEditsUsable(q)) {
            return 0;
        }
        const ignored = q.suggestion_ignored_fields || {};
        const stale = isTutorReviewStale(q);
        let count = 0;
        review.suggested_edits.forEach(function (edit) {
            const panelField = mapTutorReviewFieldToEditPanel(edit && edit.field);
            if (!panelField || ignored[panelField]) {
                return;
            }
            if (stale) {
                if (tutorReviewSuggestionAppliesToSavedQuestion(q, edit)) {
                    count += 1;
                }
                return;
            }
            if (tutorReviewFieldSuggestionDiffers(q, edit)) {
                count += 1;
            }
        });
        return count;
    }

    function mapTutorReviewFieldToEditPanel(field) {
        const f = String(field || "");
        if (f === "correct_answer") {
            return "correct_answer_letter";
        }
        if (
            f === "title" ||
            f === "stem" ||
            f === "lead_in" ||
            f === "option_a" ||
            f === "option_b" ||
            f === "option_c" ||
            f === "option_d" ||
            f === "option_e" ||
            f === "justification"
        ) {
            return f;
        }
        return "";
    }

    function getTutorReviewEditForPanelField(q, panelField) {
        const review = q.tutor_review;
        if (!review || !Array.isArray(review.suggested_edits)) {
            return null;
        }
        for (let i = 0; i < review.suggested_edits.length; i++) {
            const edit = review.suggested_edits[i];
            if (!edit || typeof edit !== "object") {
                continue;
            }
            if (mapTutorReviewFieldToEditPanel(edit.field) === panelField) {
                return edit;
            }
        }
        return null;
    }

    function tutorReviewFieldSuggestionDiffers(q, edit) {
        if (!edit || typeof edit !== "object") {
            return false;
        }
        const panelField = mapTutorReviewFieldToEditPanel(edit.field);
        if (!panelField) {
            return false;
        }
        const suggested = String(edit.suggested ?? "").trim();
        if (suggested === "") {
            return false;
        }
        const current = getQuestionDraftFieldValue(q, panelField);
        if (panelField === "correct_answer_letter") {
            const letter = suggested.match(/^[A-E]$/i) ? suggested.toUpperCase() : "";
            if (letter !== "") {
                return normalizeEditFieldText(current) !== letter;
            }
        }
        return normalizeEditFieldText(current) !== normalizeEditFieldText(suggested);
    }

    function getQualityTabStatus(q) {
        if (q.ai_review_loading) {
            return { label: "Checking…", tone: "neutral" };
        }
        const review = q.ai_review;
        if (!review || typeof review !== "object") {
            return { label: "Not checked", tone: "neutral" };
        }
        if (review.do_not_use_without_faculty_review) {
            return { label: "Needs review", tone: "danger" };
        }
        const clinical = String(review.clinical_accuracy_rating || "");
        if (clinical === "unsafe_or_inaccurate" || clinical === "borderline") {
            return { label: "Needs review", tone: "danger" };
        }
        const readiness = Number(review.overall_readiness_percent);
        if (!Number.isNaN(readiness) && readiness < 65) {
            return { label: "Needs review", tone: "warn" };
        }
        if (clinical === "acceptable_with_minor_edits") {
            return { label: "Needs minor edits", tone: "warn" };
        }
        if (!Number.isNaN(readiness) && readiness < 80) {
            return { label: "Needs minor edits", tone: "warn" };
        }
        return { label: "Looks good", tone: "success" };
    }

    function getSuggestionsTabStatus(q) {
        if (q.ai_repair_loading) {
            return { label: "Generating…", tone: "neutral" };
        }
        const repair = q.ai_repair;
        if (repair && repair.repair_blocked) {
            return { label: "Suggestions blocked", tone: "danger" };
        }
        if (repair && repair.repaired_question) {
            return { label: "Suggestions available", tone: "info" };
        }
        return { label: "No suggestions", tone: "neutral" };
    }

    function getClinicalSafetyTabStatus(q) {
        if (q.clinical_safety_review_loading) {
            return { label: "Checking…", tone: "neutral" };
        }
        if (!q.clinical_safety_review || typeof q.clinical_safety_review !== "object") {
            return { label: "Not checked", tone: "neutral" };
        }
        const rating = String(q.clinical_safety_review.clinical_safety_rating || "");
        if (rating === "unsafe") {
            return { label: "Unsafe", tone: "danger" };
        }
        if (rating === "needs_faculty_review") {
            return { label: "Needs clinical review", tone: "warn" };
        }
        return { label: "No major clinical concerns", tone: "success" };
    }

    function renderAiSupportStatusLine(status) {
        const tone = status.tone || "neutral";
        return `<p class="ai-support-status-line"><strong>Status:</strong> <span class="ai-support-status ai-support-status--${tone}">${escapeHtml(
            status.label
        )}</span></p>`;
    }

    function renderAiSupportSpinner(label) {
        return `<p class="ai-support-loading ai-support-loading--with-spinner" aria-busy="true" role="status">
            <span class="ai-support-loading-row">
                <span class="progress-spinner progress-spinner--small progress-spinner--subtle" aria-hidden="true"></span>
                <span class="ai-support-loading-label">${escapeHtml(label)}</span>
            </span>
        </p>`;
    }

    function renderAiSupportQualityTab(q) {
        if (q.ai_review_loading) {
            return renderAiSupportSpinner("Running quality check…");
        }
        if (q.ai_review_error) {
            return `<p class="ai-support-error">${escapeHtml(q.ai_review_error)}</p>
                <button type="button" class="ai-review-tab-run-btn btn btn-secondary btn--subtle" data-id="${q.id}">Run quality check</button>`;
        }
        const review = q.ai_review;
        if (!review || typeof review !== "object") {
            return `${renderAiSupportStatusLine(getQualityTabStatus(q))}
                <p class="ai-support-empty">Question quality has not been checked yet.</p>
                <button type="button" class="ai-review-tab-run-btn btn btn-secondary btn--subtle" data-id="${q.id}">Run quality check</button>`;
        }

        const status = getQualityTabStatus(q);
        const summary = review.short_tutor_summary
            ? `<p class="ai-support-summary">${escapeHtml(firstSummarySentence(review.short_tutor_summary))}</p>`
            : "";
        const readiness = Number(review.overall_readiness_percent);
        const mla = Number(review.mla_compliance_percent);
        const scores = [];
        if (!Number.isNaN(readiness)) {
            scores.push(`<strong>Readiness:</strong> ${escapeHtml(String(readiness))}%`);
        }
        if (!Number.isNaN(mla)) {
            scores.push(`<strong>MLA compliance:</strong> ${escapeHtml(String(mla))}%`);
        }
        const scoresHtml =
            scores.length > 0 ? `<p class="ai-support-scores">${scores.join(" · ")}</p>` : "";

        return `${renderAiSupportStatusLine(status)}
            ${summary}
            ${scoresHtml}
            <details class="ai-support-nested-details">
                <summary class="btn-link-style">Show details</summary>
                <div class="ai-support-nested-body">${renderGeneratorAiReviewDetails(q)}</div>
            </details>`;
    }

    function renderAiSupportSuggestionsTab(q) {
        if (q.ai_repair_loading) {
            return renderAiSupportSpinner("Generating suggestions…");
        }
        if (q.ai_repair_error) {
            return `<p class="ai-support-error">${escapeHtml(q.ai_repair_error)}</p>
                <button type="button" class="ai-repair-tab-run-btn btn btn-secondary btn--subtle" data-id="${q.id}"${
                !q.ai_review ? " disabled title=\"Run a quality check first (Question quality tab)\"" : ""
            }>Generate suggestions</button>`;
        }

        const repair = q.ai_repair;
        if (!repair || typeof repair !== "object") {
            return `${renderAiSupportStatusLine(getSuggestionsTabStatus(q))}
                <p class="ai-support-empty">No suggested improvements have been generated yet.</p>
                <button type="button" class="ai-repair-tab-run-btn btn btn-secondary btn--subtle" data-id="${q.id}"${
                !q.ai_review ? " disabled title=\"Run a quality check first (Question quality tab)\"" : ""
            }>Generate suggestions</button>`;
        }

        if (repair.repair_blocked) {
            return `${renderAiSupportStatusLine(getSuggestionsTabStatus(q))}
                <p class="ai-support-alert ai-support-alert--warn">Suggested improvements were blocked because a possible clinical safety issue was detected. Please edit manually or run clinical safety review.</p>
                <button type="button" class="discard-ai-repair-btn btn btn-secondary btn--subtle" data-id="${q.id}">Discard notice</button>`;
        }

        if (!repair.repaired_question) {
            return `${renderAiSupportStatusLine(getSuggestionsTabStatus(q))}
                <p class="ai-support-empty">No suggested improvements have been generated yet.</p>
                <button type="button" class="ai-repair-tab-run-btn btn btn-secondary btn--subtle" data-id="${q.id}">Generate suggestions</button>`;
        }

        const status = getSuggestionsTabStatus(q);
        const counts = getRepairChangeCounts(q);
        const countLine = formatCompactSuggestionCounts(q);
        const countSummary = countLine
            ? `<p class="ai-support-summary">${escapeHtml(countLine)}</p>`
            : "";
        const contentWarning =
            counts.content > 0
                ? `<p class="ai-support-alert ai-support-alert--warn">Some changes may alter clinical or educational meaning. Tutor review is required.</p>`
                : "";

        return `${renderAiSupportStatusLine(status)}
            ${countSummary}
            ${contentWarning}
            <div class="ai-support-tab-actions">
                <button type="button" class="open-edit-suggestions-btn btn btn-secondary btn--subtle" data-id="${q.id}">Open edit panel with suggestions</button>
                <button type="button" class="apply-ai-repair-btn btn btn-secondary btn--subtle" data-id="${q.id}">Apply suggestions</button>
                <button type="button" class="discard-ai-repair-btn btn btn-quiet btn--small" data-id="${q.id}">Discard suggestions</button>
            </div>
            <details class="ai-support-nested-details ai-support-suggested-changes-details">
                <summary class="btn-link-style">Show details</summary>
                <div class="ai-support-nested-body">${renderGeneratorAiRepairDetails(q)}</div>
            </details>`;
    }

    function renderAiSupportClinicalSafetyTab(q) {
        if (q.clinical_safety_review_loading) {
            return renderAiSupportSpinner("Running clinical safety check…");
        }
        if (q.clinical_safety_review_error) {
            return `<p class="ai-support-error">${escapeHtml(q.clinical_safety_review_error)}</p>
                <button type="button" class="clinical-safety-tab-run-btn btn btn-secondary btn--subtle" data-id="${q.id}">Run clinical safety check</button>`;
        }

        const review = q.clinical_safety_review;
        if (!review || typeof review !== "object") {
            return `${renderAiSupportStatusLine(getClinicalSafetyTabStatus(q))}
                <p class="ai-support-empty">Clinical safety has not been checked yet.</p>
                <button type="button" class="clinical-safety-tab-run-btn btn btn-secondary btn--subtle" data-id="${q.id}">Run clinical safety check</button>`;
        }

        const status = getClinicalSafetyTabStatus(q);
        const rating = String(review.clinical_safety_rating || "");
        let summary = "";
        if (rating === "no_major_concerns") {
            summary =
                '<p class="ai-support-summary">No major clinical concerns identified. Tutor review is still required.</p>';
        } else if (review.short_summary) {
            summary = `<p class="ai-support-summary">${escapeHtml(firstSummarySentence(review.short_summary))}</p>`;
        }
        const unsafeBanner =
            rating === "unsafe"
                ? `<p class="ai-support-alert ai-support-alert--danger"><strong>Do not approve this question</strong> until it has been edited and reviewed.</p>`
                : "";
        const needsReviewBanner =
            rating === "needs_faculty_review"
                ? `<p class="ai-support-alert ai-support-alert--warn"><strong>Needs clinical review.</strong> Edit and re-check before approval.</p>`
                : "";
        const reminder =
            rating === "no_major_concerns"
                ? ""
                : rating === "unsafe" || rating === "needs_faculty_review"
                  ? ""
                  : '<p class="ai-support-reminder">Tutor review is still required.</p>';

        return `${renderAiSupportStatusLine(status)}
            ${summary}
            ${reminder}
            ${unsafeBanner}
            ${needsReviewBanner}
            <details class="ai-support-nested-details">
                <summary class="btn-link-style">Show details</summary>
                <div class="ai-support-nested-body">${renderGeneratorClinicalSafetyDetails(q)}</div>
            </details>`;
    }

    function renderTutorReviewSummaryBlock(q) {
        const review = q.tutor_review;
        if (!review || typeof review !== "object") {
            return "";
        }

        const judgement = String(review.overall_judgement || "");
        const clinicalSafety = review.status && review.status.clinical_safety ? review.status.clinical_safety : "";
        const judgementLabel = getTutorReviewJudgementLabel(judgement);
        const judgementTone = getTutorReviewJudgementTone(judgement, clinicalSafety);
        const scores = review.scores && typeof review.scores === "object" ? review.scores : {};
        const suggestionCount = countTutorReviewSuggestions(q);

        const scoreParts = [];
        if (!Number.isNaN(Number(scores.clinical_correctness))) {
            scoreParts.push(`<strong>Clinical correctness:</strong> ${escapeHtml(String(scores.clinical_correctness))}%`);
        }
        if (!Number.isNaN(Number(scores.one_best_answer))) {
            scoreParts.push(`<strong>One-best-answer:</strong> ${escapeHtml(String(scores.one_best_answer))}%`);
        }
        if (!Number.isNaN(Number(scores.ms_akt_style))) {
            scoreParts.push(`<strong>MS AKT style:</strong> ${escapeHtml(String(scores.ms_akt_style))}%`);
        }
        if (!Number.isNaN(Number(scores.skill_alignment))) {
            scoreParts.push(`<strong>Skill alignment:</strong> ${escapeHtml(String(scores.skill_alignment))}%`);
        }

        const summary = review.summary
            ? `<p class="ai-support-summary">${escapeHtml(firstSummarySentence(review.summary))}</p>`
            : "";

        const keyConcerns = renderTutorReviewKeyConcerns(q);
        const safetyBanner =
            clinicalSafety === "unsafe" || judgement === "do_not_approve_yet"
                ? `<p class="ai-support-alert ai-support-alert--danger"><strong>Clinical concern identified.</strong> Do not approve until edited and reviewed.</p>`
                : clinicalSafety === "needs_review"
                  ? `<p class="ai-support-alert ai-support-alert--warn">Needs clinical review before approval. Tutor review is still required.</p>`
                  : clinicalSafety === "no_major_concerns" || judgement === "ready_for_tutor_review"
                    ? `<p class="ai-support-reminder">No major clinical concerns identified. Tutor review is still required.</p>`
                    : "";

        const suggestionsLine =
            suggestionCount > 0
                ? `<p class="ai-support-summary">Suggested edits available: <strong>${suggestionCount}</strong> field${
                      suggestionCount === 1 ? "" : "s"
                  }.</p>`
                : "";

        return `<p class="ai-support-status-line"><strong>Overall judgement:</strong> <span class="ai-support-status ai-support-status--${judgementTone}">${escapeHtml(
            judgementLabel
        )}</span></p>
            ${scoreParts.length > 0 ? `<p class="ai-support-scores">${scoreParts.join(" · ")}</p>` : ""}
            ${summary}
            ${safetyBanner}
            ${keyConcerns}
            ${suggestionsLine}`;
    }

    function renderAiReviewAndSuggestionsContent(q) {
        const noticeCount = countFormattingNotices(q);
        const technicalHint = noticeCount > 0 ? ` (${noticeCount} formatting notices)` : "";

        if (q.tutor_review_loading) {
            return `<div class="ai-review-panel-content" aria-live="polite">
                ${renderAiSupportSpinner("Reviewing question…")}
            </div>`;
        }

        if (q.tutor_review_error) {
            const canRetry = canRequestTutorQuestionReview(q);
            const errorMessage = escapeHtml(getTutorReviewErrorDisplayMessage(q));
            return `<div class="ai-review-panel-content">
                <p class="ai-support-error">${errorMessage}</p>
                <button type="button" class="tutor-review-run-btn btn btn-secondary" data-id="${q.id}"${
                canRetry ? "" : " disabled"
            }>${escapeHtml(getTutorReviewRunButtonLabel(q))}</button>
            </div>`;
        }

        const review = q.tutor_review;
        if (!review || typeof review !== "object") {
            const canRun = canRequestTutorQuestionReview(q);
            return `<div class="ai-review-panel-content">
                <p class="ai-support-empty">AI review has not been run for this question.</p>
                <button type="button" class="tutor-review-run-btn btn btn-secondary" data-id="${q.id}"${
                canRun ? "" : " disabled"
            }>${escapeHtml(getTutorReviewRunButtonLabel(q))}</button>
            </div>`;
        }

        const stale = isTutorReviewStale(q);
        const complete = isTutorReviewCompleteForCurrentVersion(q);
        const suggestionCount = countTutorReviewSuggestions(q);
        const canRun = canRequestTutorQuestionReview(q);

        let versionStatusHtml = "";
        if (stale) {
            versionStatusHtml = `<p class="ai-review-version-status ai-review-version-status--stale">Question has changed since AI review.</p>
                <p class="ai-review-version-note">Previous suggestions may no longer match the current saved question.</p>`;
        } else if (complete) {
            versionStatusHtml = `<p class="ai-review-version-status ai-review-version-status--complete">AI review completed for this version.</p>`;
        }

        const blockedBanner = renderTutorReviewSuggestedEditsBlockedBanner(review);
        const summaryBlock = renderTutorReviewSummaryBlock(q);

        const actionsHtml = [];
        if (canRun) {
            actionsHtml.push(
                `<button type="button" class="tutor-review-run-btn btn btn-secondary" data-id="${q.id}">${escapeHtml(
                    getTutorReviewRunButtonLabel(q)
                )}</button>`
            );
        }
        if (!stale && suggestionCount > 0) {
            actionsHtml.push(
                `<button type="button" class="open-edit-suggestions-btn btn btn-secondary" data-id="${q.id}">Open edit panel with suggestions</button>`
            );
        }

        const actionsRow =
            actionsHtml.length > 0
                ? `<div class="ai-support-tab-actions">${actionsHtml.join("")}</div>`
                : "";

        return `<div class="ai-review-panel-content">
            ${blockedBanner}
            ${versionStatusHtml}
            ${summaryBlock}
            ${actionsRow}
            <details class="ai-support-nested-details">
                <summary class="btn-link-style">Show review details</summary>
                <div class="ai-support-nested-body">${renderTutorReviewDetails(q)}</div>
            </details>
            <details class="ai-support-technical-details">
                <summary class="btn-link-style">Show technical details${escapeHtml(technicalHint)}</summary>
                <div class="ai-support-technical-body">${renderTechnicalDetailsSection(q)}</div>
            </details>
        </div>`;
    }

    function renderTutorReviewKeyConcerns(q) {
        const review = q && q.tutor_review;
        if (!review || typeof review !== "object") {
            return "";
        }
        const concerns = review.concerns && typeof review.concerns === "object" ? review.concerns : {};
        const buckets = [
            ["Clinical", concerns.clinical],
            ["One-best-answer", concerns.one_best_answer],
            ["Style", concerns.style],
            ["Skill alignment", concerns.skill_alignment],
            ["Tutor comments", concerns.tutor_comments],
            ["Safety", concerns.safety]
        ];
        const items = [];
        buckets.forEach(function (pair) {
            const label = pair[0];
            const list = filterTutorReviewConcernLines(Array.isArray(pair[1]) ? pair[1] : [], review);
            list.slice(0, 2).forEach(function (line) {
                const t = String(line || "").trim();
                if (t !== "") {
                    items.push(`<li><strong>${escapeHtml(label)}:</strong> ${escapeHtml(t)}</li>`);
                }
            });
        });
        if (items.length === 0) {
            return "";
        }
        return `<div class="ai-support-key-concerns"><p class="ai-support-key-concerns-heading"><strong>Key concerns</strong></p><ul class="generator-ai-review-bullets">${items.join(
            ""
        )}</ul></div>`;
    }

    function renderTutorReviewDetails(q) {
        const review = q.tutor_review;
        if (!review || typeof review !== "object") {
            return `<p class="generator-collapsed-muted">No review details.</p>`;
        }
        const concerns = review.concerns && typeof review.concerns === "object" ? review.concerns : {};
        const blocks = [];
        [
            ["Clinical", concerns.clinical],
            ["One-best-answer", concerns.one_best_answer],
            ["Style", concerns.style],
            ["Skill alignment", concerns.skill_alignment],
            ["Tutor comments", concerns.tutor_comments],
            ["Safety", concerns.safety]
        ].forEach(function (pair) {
            const list = filterTutorReviewConcernLines(Array.isArray(pair[1]) ? pair[1] : [], review);
            if (list.length === 0) {
                return;
            }
            blocks.push(
                `<h4 class="generator-ai-review-h4">${escapeHtml(pair[0])}</h4><ul class="generator-ai-review-bullets">${list
                    .map((line) => `<li>${escapeHtml(String(line))}</li>`)
                    .join("")}</ul>`
            );
        });

        const edits =
            isTutorReviewSuggestedEditsUsable(q) && Array.isArray(review.suggested_edits) ? review.suggested_edits : [];
        if (edits.length > 0) {
            blocks.push(
                `<h4 class="generator-ai-review-h4">Suggested edits (preview)</h4><ul class="generator-ai-review-changes-list">${edits
                    .map(function (edit) {
                        return `<li class="generator-ai-review-change-item">
                            <span class="generator-ai-review-change-field">${escapeHtml(edit.field || "")}</span>
                            <span class="generator-ai-review-change-priority">${escapeHtml(edit.risk || "medium")}</span>
                            <p class="generator-ai-review-change-issue">${escapeHtml(edit.reason || "")}</p>
                        </li>`;
                    })
                    .join("")}</ul>`
            );
        }

        return blocks.length > 0 ? blocks.join("") : `<p class="generator-collapsed-muted">No additional detail.</p>`;
    }

    function renderGeneratorStyleReviewSection(q, plainOnly) {
        const hasKey = Object.prototype.hasOwnProperty.call(q, "style_warnings");
        const list = hasKey && Array.isArray(q.style_warnings) ? q.style_warnings : null;

        if (list === null) {
            return `<p class="generator-collapsed-muted">Formatting checks appear after generation (not stored after reload).</p>`;
        }
        if (list.length === 0) {
            return `<p class="generator-collapsed-muted">No formatting notices detected.</p>`;
        }

        const sorted = list.slice().sort(function (a, b) {
            return styleWarningSeverityRank(a.severity) - styleWarningSeverityRank(b.severity);
        });
        const rawCodes = [];
        const items = sorted
            .map(function (w) {
                if (!w || typeof w !== "object") {
                    return "";
                }
                const sev = escapeHtml(w.severity || "warning");
                const sevClass = styleWarningSeverityClass(w.severity);
                const plain = escapeHtml(humanizeStyleWarning(w));
                const codeRaw = w.code;
                const fieldRaw = w.field;
                if (!plainOnly && codeRaw) {
                    const codeStr = String(codeRaw).trim();
                    const fieldStr = fieldRaw ? String(fieldRaw).trim() : "";
                    rawCodes.push(fieldStr ? `${fieldStr} · ${codeStr}` : codeStr);
                }

                return `<li class="generator-style-warning-item">
                    <span class="style-severity-badge ${sevClass}">${sev}</span>
                    <div class="generator-style-warning-body">
                        <p class="style-warning-message">${plain}</p>
                    </div>
                </li>`;
            })
            .join("");

        const rawBlock =
            !plainOnly && rawCodes.length > 0
                ? `<details class="generator-nested-details"><summary>Raw warning codes (${rawCodes.length})</summary><ul class="generator-technical-code-list">${rawCodes
                      .map((c) => `<li><code>${escapeHtml(c)}</code></li>`)
                      .join("")}</ul></details>`
                : "";

        return `<ul class="generator-style-warnings-list">${items}</ul>${rawBlock}`;
    }

    function renderTechnicalDetailsSection(q) {
        const draftWarnings = renderGeneratorStyleReviewSection(q, true);
        const previewWarnings =
            Array.isArray(q.validator_after_repair) && q.validator_after_repair.length > 0
                ? `<h4 class="generator-ai-review-h4">On suggested preview</h4>${renderStyleWarningListFromArray(
                      q.validator_after_repair,
                      true
                  )}`
                : "";

        const review = q.ai_review;
        const technicalCodes = [];
        if (review && typeof review === "object") {
            ["agreed_rule_warning_codes", "possible_false_positive_rule_warning_codes", "independent_concerns"].forEach(
                function (key) {
                    const arr = review[key];
                    if (Array.isArray(arr)) {
                        arr.forEach(function (item) {
                            if (String(item || "").trim() !== "") {
                                technicalCodes.push(String(item));
                            }
                        });
                    }
                }
            );
        }
        const reviewCodesBlock =
            technicalCodes.length > 0
                ? `<details class="generator-nested-details"><summary>AI review codes (${technicalCodes.length})</summary><ul class="generator-technical-code-list">${technicalCodes
                      .map((c) => `<li><code>${escapeHtml(c)}</code></li>`)
                      .join("")}</ul></details>`
                : "";

        const tutorTechnical = renderTutorReviewTechnicalDetails(q);

        return `<h4 class="generator-ai-review-h4">Formatting checks (draft)</h4>${draftWarnings}${previewWarnings}${reviewCodesBlock}
            ${tutorTechnical}
            <details class="generator-nested-details"><summary>Raw validator codes (full)</summary>
            ${renderGeneratorStyleReviewSection(q, false)}
            </details>`;
    }

    function renderStyleWarningListFromArray(list, plainOnly) {
        if (!Array.isArray(list) || list.length === 0) {
            return `<p class="generator-collapsed-muted">None</p>`;
        }
        const items = list
            .map(function (w) {
                if (!w || typeof w !== "object") {
                    return "";
                }
                const plain = escapeHtml(humanizeStyleWarning(w));
                return `<li class="generator-style-warning-item"><p class="style-warning-message">${plain}</p></li>`;
            })
            .join("");
        return `<ul class="generator-style-warnings-list">${items}</ul>`;
    }

    function aiReviewRatingLabel(value) {
        return String(value || "")
            .replace(/_/g, " ")
            .replace(/\b\w/g, function (c) {
                return c.toUpperCase();
            });
    }

    function renderGeneratorAiReviewDetails(q) {
        if (q.ai_review_loading) {
            return `<p class="generator-collapsed-muted">Quality check running…</p>`;
        }
        if (q.ai_review_error) {
            return `<p class="generator-ai-review-error-text">${escapeHtml(q.ai_review_error)}</p>`;
        }
        const review = q.ai_review;
        if (!review || typeof review !== "object") {
            return `<p class="generator-collapsed-muted">Use <strong>Run quality check</strong> in the Question quality tab for advisory feedback.</p>`;
        }

        const readiness = Number(review.overall_readiness_percent);
        const mla = Number(review.mla_compliance_percent);
        const facultyFlag = review.do_not_use_without_faculty_review
            ? `<p class="generator-ai-review-flag"><strong>Faculty review recommended</strong> before use.</p>`
            : "";

        const safetyList =
            Array.isArray(review.safety_concerns) && review.safety_concerns.length > 0
                ? `<ul class="generator-ai-review-bullets">${review.safety_concerns
                      .map((s) => `<li>${escapeHtml(s)}</li>`)
                      .join("")}</ul>`
                : `<p class="generator-ai-review-muted">None noted.</p>`;

        const styleList =
            Array.isArray(review.style_concerns) && review.style_concerns.length > 0
                ? `<ul class="generator-ai-review-bullets">${review.style_concerns
                      .map((s) => `<li>${escapeHtml(s)}</li>`)
                      .join("")}</ul>`
                : `<p class="generator-ai-review-muted">None noted.</p>`;

        const changes = Array.isArray(review.recommended_changes) ? review.recommended_changes : [];
        const changeItems =
            changes.length > 0
                ? changes
                      .map(function (c) {
                          if (!c || typeof c !== "object") {
                              return "";
                          }
                          return `<li class="generator-ai-review-change-item">
                                <span class="generator-ai-review-change-priority">${escapeHtml(c.priority || "medium")}</span>
                                <span class="generator-ai-review-change-field">${escapeHtml(c.field || "general")}</span>
                                <p class="generator-ai-review-change-issue">${escapeHtml(c.issue || "")}</p>
                                <p class="generator-ai-review-change-suggestion"><strong>Suggested:</strong> ${escapeHtml(
                                    c.suggested_change || ""
                                )}</p>
                            </li>`;
                      })
                      .join("")
                : `<li class="generator-ai-review-muted">No field-level changes suggested.</li>`;

        const technicalCodes = [];
        ["agreed_rule_warning_codes", "possible_false_positive_rule_warning_codes", "independent_concerns"].forEach(
            function (key) {
                const arr = review[key];
                if (Array.isArray(arr)) {
                    arr.forEach(function (item) {
                        if (String(item || "").trim() !== "") {
                            technicalCodes.push(String(item));
                        }
                    });
                }
            }
        );
        return `${facultyFlag}
                <p class="generator-ai-review-ratings">
                    <strong>Readiness:</strong> ${escapeHtml(String(readiness))}%
                    · <strong>MLA compliance:</strong> ${escapeHtml(String(mla))}%
                    · <strong>Clinical accuracy:</strong> ${escapeHtml(aiReviewRatingLabel(review.clinical_accuracy_rating))}
                    · <strong>One best answer:</strong> ${escapeHtml(aiReviewRatingLabel(review.one_best_answer_rating))}
                    · <strong>Skill alignment:</strong> ${escapeHtml(aiReviewRatingLabel(review.skill_alignment_rating))}
                </p>
                <h4 class="generator-ai-review-h4">Safety concerns</h4>${safetyList}
                <h4 class="generator-ai-review-h4">Style concerns</h4>${styleList}
                <h4 class="generator-ai-review-h4">Recommended changes</h4>
                <ul class="generator-ai-review-changes-list">${changeItems}</ul>
                <p class="generator-collapsed-muted">Raw review codes are under <strong>Show technical details</strong>.</p>`;
    }

    function renderRepairChangeList(entries, compact) {
        if (!entries.length) {
            return '<p class="generator-ai-repair-muted">None</p>';
        }
        if (compact) {
            return `<ul class="generator-repair-change-compact">${entries
                .map(function (entry) {
                    const field = escapeHtml(entry.field || "field");
                    const reason = entry.reason ? `<span class="generator-repair-reason">${escapeHtml(entry.reason)}</span>` : "";
                    return `<li><strong>${field}</strong> — ${escapeHtml(String(entry.before || "").slice(0, 80))}${
                        String(entry.before || "").length > 80 ? "…" : ""
                    } → ${escapeHtml(String(entry.after || "").slice(0, 80))}${
                        String(entry.after || "").length > 80 ? "…" : ""
                    }${reason ? `<br>${reason}` : ""}</li>`;
                })
                .join("")}</ul>`;
        }
        return `<table class="generator-ai-repair-table"><thead><tr><th>Field</th><th>Before</th><th>After</th><th>Reason</th></tr></thead><tbody>${entries
            .map(function (entry) {
                return `<tr><td>${escapeHtml(entry.field || "")}</td><td>${escapeHtml(
                    String(entry.before || "")
                )}</td><td>${escapeHtml(String(entry.after || ""))}</td><td>${escapeHtml(entry.reason || "")}</td></tr>`;
            })
            .join("")}</tbody></table>`;
    }

    function renderGeneratorAiRepairDetails(q) {
        if (q.ai_repair_loading) {
            return `<p class="generator-collapsed-muted">Suggesting improvements…</p>`;
        }
        if (q.ai_repair_error) {
            return `<p class="generator-ai-repair-error-text">${escapeHtml(q.ai_repair_error)}</p><p class="generator-ai-repair-muted">Original draft unchanged.</p>`;
        }
        const repair = q.ai_repair;
        if (!repair || typeof repair !== "object") {
            return `<p class="generator-collapsed-muted">After a quality check, use <strong>Generate suggestions</strong> to preview edits. The draft is not changed until you apply them.</p>`;
        }

        if (repair.repair_blocked) {
            const blockedReasons = Array.isArray(repair.repair_blocked_reasons) ? repair.repair_blocked_reasons : [];
            const reasonList =
                blockedReasons.length > 0
                    ? `<ul class="generator-ai-repair-blocked-reasons">${blockedReasons
                          .map((r) => `<li>${escapeHtml(String(r))}</li>`)
                          .join("")}</ul>`
                    : "";
            return `<p class="generator-ai-repair-flag"><strong>Suggested improvements were blocked</strong> because a possible clinical safety issue was detected.</p>${reasonList}`;
        }

        const changeLog = Array.isArray(repair.change_log) ? repair.change_log : [];
        const lowRisk = changeLog.filter(function (e) {
            return (
                (e.auto_apply_eligible === true || String(e.repair_risk || e.risk) === "low") &&
                (e.change_type === "style" || e.change_type === "structure") &&
                !e.tutor_review_required
            );
        });
        const mediumRisk = changeLog.filter(function (e) {
            return !lowRisk.includes(e);
        });

        return `<details class="generator-nested-details"><summary>Show full change log</summary>
                <div class="generator-ai-repair-change-section generator-ai-repair-change-section--low">
                    <h4 class="generator-ai-repair-change-heading">Wording changes (${lowRisk.length})</h4>
                    ${renderRepairChangeList(lowRisk, false)}
                </div>
                <div class="generator-ai-repair-change-section generator-ai-repair-change-section--medium">
                    <h4 class="generator-ai-repair-change-heading">Content changes to review (${mediumRisk.length})</h4>
                    ${renderRepairChangeList(mediumRisk, false)}
                </div>
            </details>`;
    }

    function renderClinicalSafetyConcernList(items) {
        if (!Array.isArray(items) || items.length === 0) {
            return "";
        }
        return `<ul class="generator-clinical-safety-bullets">${items
            .map((s) => `<li>${escapeHtml(s)}</li>`)
            .join("")}</ul>`;
    }

    function renderGeneratorClinicalSafetyDetails(q) {
        if (q.clinical_safety_review_loading) {
            return `<p class="generator-collapsed-muted">Clinical safety check running…</p>`;
        }
        if (q.clinical_safety_review_error) {
            return `<p class="generator-clinical-safety-error-text">${escapeHtml(q.clinical_safety_review_error)}</p>`;
        }
        const review = q.clinical_safety_review;
        if (!review || typeof review !== "object") {
            return `<p class="generator-collapsed-muted">Use <strong>Run clinical safety check</strong> in the Clinical safety tab. Advisory only.</p>`;
        }

        const reviewSourceNote =
            q.clinical_safety_review_source === "repaired_preview"
                ? `<p class="generator-clinical-safety-sub">Checked the <strong>suggested preview</strong> (not yet applied to the saved draft).</p>`
                : `<p class="generator-clinical-safety-sub">Checked the <strong>current draft</strong>.</p>`;

        const rating = String(review.clinical_safety_rating || "");
        const highPriority = renderClinicalSafetyConcernList(review.high_priority_concerns);
        const prescribing = renderClinicalSafetyConcernList(review.prescribing_or_monitoring_concerns);
        const stemKey = renderClinicalSafetyConcernList(review.stem_key_consistency_concerns);

        const detailBlocks = [];
        if (highPriority) {
            detailBlocks.push(`<h4 class="generator-ai-review-h4">High priority</h4>${highPriority}`);
        }
        if (prescribing) {
            detailBlocks.push(`<h4 class="generator-ai-review-h4">Prescribing / monitoring</h4>${prescribing}`);
        }
        if (stemKey) {
            detailBlocks.push(`<h4 class="generator-ai-review-h4">Stem–key consistency</h4>${stemKey}`);
        }

        return `${reviewSourceNote}
            <p class="generator-clinical-safety-ratings">
                <strong>Safety:</strong> ${escapeHtml(tutorFriendlyClinicalSafetyRating(rating))}
                · <strong>Clinical accuracy:</strong> ${escapeHtml(aiReviewRatingLabel(review.clinical_accuracy_rating))}
                · <strong>One best answer:</strong> ${escapeHtml(aiReviewRatingLabel(review.one_best_answer_fairness))}
            </p>
            ${detailBlocks.join("")}`;
    }

    function setQuestionReviewSessionState(questionId, patch) {
        const index = questions.findIndex((q) => String(q.id) === String(questionId));
        if (index === -1) {
            return;
        }
        questions[index] = { ...questions[index], ...patch };
        const filteredIndex = filteredQuestions.findIndex((q) => String(q.id) === String(questionId));
        if (filteredIndex !== -1) {
            filteredQuestions[filteredIndex] = questions[index];
        }
        if (
            filteredQuestions[currentIndex] &&
            String(filteredQuestions[currentIndex].id) === String(questionId)
        ) {
            renderCurrentQuestion();
        }
    }

    function setQuestionAiReviewState(questionId, patch) {
        setQuestionReviewSessionState(questionId, patch);
    }

    function setQuestionClinicalSafetyReviewState(questionId, patch) {
        setQuestionReviewSessionState(questionId, patch);
    }

    function requestTutorQuestionReview(questionId) {
        const idKey = String(questionId);
        const q = questions.find((item) => String(item.id) === idKey);
        if (!q) {
            showInfoModal("AI review", "Question not found.");
            return;
        }

        if (!canRequestTutorQuestionReview(q)) {
            return;
        }

        if (tutorReviewRequestsInFlight.has(idKey)) {
            return;
        }

        const reviewedContentHash = getQuestionContentFingerprint(q);
        tutorReviewRequestsInFlight.add(idKey);

        setQuestionReviewSessionState(questionId, {
            tutor_review_loading: true,
            tutor_review_error: null
        });

        const body = new URLSearchParams();
        body.append("question_id", idKey);
        if (Array.isArray(q.style_warnings)) {
            body.append("style_warnings", JSON.stringify(q.style_warnings));
        }

        fetch("../app/api/review_question_and_suggest_edits.php", {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: body.toString()
        })
            .then((res) => res.text())
            .then((text) => {
                let data;
                try {
                    data = JSON.parse(text);
                } catch (parseError) {
                    console.error("[tutor_review] JSON parse error", parseError, text);
                    applyTutorReviewFailureState(questionId, {
                        error_code: "incomplete_response",
                        tutor_message: TUTOR_REVIEW_FAILURE_MESSAGES.incomplete_response,
                        detail: "Server response was not valid JSON."
                    });
                    return;
                }

                if (data.ok === true && data.review) {
                    setQuestionReviewSessionState(questionId, {
                        tutor_review_loading: false,
                        tutor_review_error: null,
                        tutor_review_error_code: null,
                        tutor_review: data.review,
                        tutor_review_content_hash: reviewedContentHash,
                        suggestion_ignored_fields: {}
                    });
                    const updated = questions.find((item) => String(item.id) === idKey);
                    if (updated) {
                        persistTutorReviewForQuestion(updated);
                    }
                    return;
                }

                applyTutorReviewFailureState(questionId, data);
            })
            .catch((error) => {
                console.error("[tutor_review] network error", error);
                applyTutorReviewFailureState(questionId, {
                    error_code: "generic",
                    tutor_message: TUTOR_REVIEW_FAILURE_MESSAGES.generic,
                    detail: error && error.message ? String(error.message) : "Network request failed."
                });
            })
            .finally(function () {
                tutorReviewRequestsInFlight.delete(idKey);
            });
    }

    function requestGeneratorAiReview(questionId) {
        const q = questions.find((item) => String(item.id) === String(questionId));
        if (!q) {
            showInfoModal("AI review", "Question not found.");
            return;
        }

        setQuestionAiReviewState(questionId, {
            ai_review_loading: true,
            ai_review_error: null,
            ai_support_active_tab: "quality"
        });

        const body = new URLSearchParams();
        body.append("question_id", String(questionId));
        if (Array.isArray(q.style_warnings)) {
            body.append("style_warnings", JSON.stringify(q.style_warnings));
        }

        fetch("../app/api/ai_review_question.php", {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: body.toString()
        })
            .then((res) => res.text())
            .then((text) => {
                let data;
                try {
                    data = JSON.parse(text);
                } catch (parseError) {
                    console.error(parseError, text);
                    setQuestionAiReviewState(questionId, {
                        ai_review_loading: false,
                        ai_review_error: "Server returned invalid JSON."
                    });
                    return;
                }

                if (data.status === "success" && data.ai_review) {
                    setQuestionAiReviewState(questionId, {
                        ai_review_loading: false,
                        ai_review_error: null,
                        ai_review: data.ai_review,
                        ai_support_active_tab: "quality"
                    });
                    return;
                }

                const msg = data.message || "AI review failed.";
                const raw = data.raw_response ? String(data.raw_response).slice(0, 300) : "";
                setQuestionAiReviewState(questionId, {
                    ai_review_loading: false,
                    ai_review_error: raw ? msg + " " + raw : msg
                });
            })
            .catch((error) => {
                console.error(error);
                setQuestionAiReviewState(questionId, {
                    ai_review_loading: false,
                    ai_review_error: "Could not reach the AI review service."
                });
            });
    }

    function setQuestionAiRepairState(questionId, patch) {
        setQuestionReviewSessionState(questionId, patch);
    }

    function requestGeneratorClinicalSafetyReview(questionId, options) {
        const opts = options || {};
        const q = questions.find((item) => String(item.id) === String(questionId));
        if (!q) {
            showInfoModal("Clinical safety check", "Question not found.");
            return Promise.reject(new Error("Question not found"));
        }

        const activeTabPatch =
            opts.skipActiveTabSwitch === true
                ? {}
                : { ai_support_active_tab: "safety" };

        setQuestionClinicalSafetyReviewState(questionId, {
            clinical_safety_review_loading: true,
            clinical_safety_review_error: null,
            ...activeTabPatch
        });

        const body = new URLSearchParams();
        body.append("question_id", String(questionId));

        if (Array.isArray(opts.repairBlockedReasons) && opts.repairBlockedReasons.length > 0) {
            body.append("repair_blocked_reasons", JSON.stringify(opts.repairBlockedReasons));
        } else if (opts.questionOverride && typeof opts.questionOverride === "object") {
            body.append("question_override", JSON.stringify(opts.questionOverride));
            if (Array.isArray(opts.styleWarnings)) {
                body.append("style_warnings", JSON.stringify(opts.styleWarnings));
            }
        } else if (q.ai_repair && q.ai_repair.repair_blocked) {
            const blockedReasons = Array.isArray(q.ai_repair.repair_blocked_reasons)
                ? q.ai_repair.repair_blocked_reasons
                : [];
            if (blockedReasons.length > 0) {
                body.append("repair_blocked_reasons", JSON.stringify(blockedReasons));
            }
        } else if (q.ai_repair && q.ai_repair.repaired_question && !q.ai_repair.repair_blocked) {
            body.append("question_override", JSON.stringify(q.ai_repair.repaired_question));
            if (Array.isArray(q.validator_after_repair)) {
                body.append("style_warnings", JSON.stringify(q.validator_after_repair));
            }
        } else if (Array.isArray(q.style_warnings)) {
            body.append("style_warnings", JSON.stringify(q.style_warnings));
        }

        return fetch("../app/api/clinical_safety_review_question.php", {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: body.toString()
        })
            .then((res) => res.text())
            .then((text) => {
                let data;
                try {
                    data = JSON.parse(text);
                } catch (parseError) {
                    console.error(parseError, text);
                    setQuestionClinicalSafetyReviewState(questionId, {
                        clinical_safety_review_loading: false,
                        clinical_safety_review_error: "Clinical safety review failed. Original draft unchanged."
                    });
                    throw parseError;
                }

                if ((data.ok === true || data.status === "success") && data.clinical_safety_review) {
                    const completePatch = {
                        clinical_safety_review_loading: false,
                        clinical_safety_review_error: null,
                        clinical_safety_review: data.clinical_safety_review,
                        clinical_safety_review_source: data.review_source || opts.reviewSource || "original_draft"
                    };
                    if (opts.skipActiveTabSwitch !== true) {
                        completePatch.ai_support_active_tab = "safety";
                    }
                    setQuestionClinicalSafetyReviewState(questionId, completePatch);
                    return data;
                }

                const msg = data.detail || data.message || data.error || "Clinical safety review failed.";
                setQuestionClinicalSafetyReviewState(questionId, {
                    clinical_safety_review_loading: false,
                    clinical_safety_review_error: msg + " Original draft unchanged."
                });
                throw new Error(msg);
            })
            .catch((error) => {
                if (String(error.message || "").indexOf("JSON") === -1) {
                    console.error(error);
                    setQuestionClinicalSafetyReviewState(questionId, {
                        clinical_safety_review_loading: false,
                        clinical_safety_review_error: "Could not reach the clinical safety review service."
                    });
                }
                throw error;
            });
    }

    function requestGeneratorAiRepairPipeline(questionId) {
        const q = questions.find((item) => String(item.id) === String(questionId));
        if (!q) {
            showInfoModal("AI repair", "Question not found.");
            return;
        }
        if (!q.ai_review || typeof q.ai_review !== "object") {
            showInfoModal(
                "Generate suggestions",
                "Run a quality check first (Question quality tab) — suggestions use that feedback."
            );
            return;
        }

        setQuestionAiRepairState(questionId, {
            ai_repair_loading: true,
            ai_repair_error: null,
            clinical_safety_review_loading: true,
            clinical_safety_review_error: null,
            ai_support_active_tab: "suggestions"
        });

        const body = new URLSearchParams();
        body.append("question_id", String(questionId));
        body.append("ai_review", JSON.stringify(q.ai_review));
        if (Array.isArray(q.style_warnings)) {
            body.append("style_warnings", JSON.stringify(q.style_warnings));
        }

        fetchWithTimeout(
            "../app/api/ai_repair_question.php",
            {
                method: "POST",
                headers: { "Content-Type": "application/x-www-form-urlencoded" },
                body: body.toString()
            },
            130000
        )
            .then((res) => res.text())
            .then((text) => {
                let data;
                try {
                    data = JSON.parse(text);
                } catch (parseError) {
                    console.error(parseError, text);
                    setQuestionAiRepairState(questionId, {
                        ai_repair_loading: false,
                        ai_repair_error: "AI repair timed out. Original draft unchanged. You can try again or continue with manual review.",
                        clinical_safety_review_loading: false
                    });
                    return;
                }

                if ((data.ok !== true && data.status !== "success") || !data.ai_repair) {
                    const msg = data.message || data.error || "AI repair failed.";
                    setQuestionAiRepairState(questionId, {
                        ai_repair_loading: false,
                        ai_repair_error: msg,
                        clinical_safety_review_loading: false
                    });
                    return;
                }

                const repairPayload = data.ai_repair;
                if (
                    repairPayload &&
                    !repairPayload.repair_blocked &&
                    repairPayload.repaired_question
                ) {
                    const previewCheck = validateRepairedQuestionPreview(repairPayload.repaired_question);
                    if (!previewCheck.ok) {
                        repairPayload.repair_blocked = true;
                        repairPayload.repaired_question = null;
                        repairPayload.repair_blocked_reasons = [previewCheck.message];
                    }
                }

                setQuestionAiRepairState(questionId, {
                    ai_repair_loading: false,
                    ai_repair_error: null,
                    ai_repair: repairPayload,
                    validator_after_repair: Array.isArray(data.validator_after_repair)
                        ? data.validator_after_repair
                        : [],
                    ai_support_active_tab: "suggestions"
                });

                if (repairPayload && repairPayload.repair_blocked) {
                    const blockedReasons = Array.isArray(repairPayload.repair_blocked_reasons)
                        ? repairPayload.repair_blocked_reasons
                        : [];
                    return requestGeneratorClinicalSafetyReview(questionId, {
                        repairBlockedReasons: blockedReasons,
                        reviewSource: "original_draft",
                        skipActiveTabSwitch: true
                    });
                }

                return requestGeneratorClinicalSafetyReview(questionId, {
                    questionOverride: repairPayload.repaired_question,
                    styleWarnings: data.validator_after_repair,
                    reviewSource: "repaired_preview",
                    skipActiveTabSwitch: true
                });
            })
            .catch((error) => {
                console.error(error);
                setQuestionAiRepairState(questionId, {
                    ai_repair_loading: false,
                    ai_repair_error: "AI repair timed out. Original draft unchanged. You can try again or continue with manual review.",
                    clinical_safety_review_loading: false
                });
            });
    }

    function applyRepairedQuestionToDraft(questionId) {
        const q = questions.find((item) => String(item.id) === String(questionId));
        if (!q || !q.ai_repair || q.ai_repair.repair_blocked || !q.ai_repair.repaired_question) {
            showInfoModal(
                "Apply repair",
                q && q.ai_repair && q.ai_repair.repair_blocked
                    ? "Repair was blocked for clinical safety. Edit the original draft manually or run Reviewer 2 first."
                    : "No repaired preview to apply."
            );
            return;
        }

        const repaired = q.ai_repair.repaired_question;
        const stemNorm = normalizeMainStemObservationGrammar(repaired.stem || "");
        repaired.stem = stemNorm.stem;
        if (hasMainUnsafeObservationGrammar(repaired.stem)) {
            showInfoModal("Apply suggestions", MAIN_STEM_OBSERVATION_SAVE_BLOCKED);
            return;
        }

        const repairCheck = validateRepairedQuestionPreview(repaired);
        if (!repairCheck.ok) {
            showInfoModal("Apply suggestions", repairCheck.message);
            return;
        }

        const body = new URLSearchParams();
        body.append("question_id", String(questionId));
        body.append("title", repaired.title || "");
        body.append("stem", repaired.stem || "");
        body.append("lead_in", repaired.lead_in || "");
        body.append("option_a", repaired.option_a || "");
        body.append("option_b", repaired.option_b || "");
        body.append("option_c", repaired.option_c || "");
        body.append("option_d", repaired.option_d || "");
        body.append("option_e", repaired.option_e || "");
        body.append("correct_answer_letter", repairCheck.letter || repaired.correct_answer_letter || "");
        body.append("justification", repaired.justification || "");

        fetch("../app/api/update_question.php", {
            method: "POST",
            headers: { "Content-Type": "application/x-www-form-urlencoded" },
            body: body.toString()
        })
            .then((res) => res.json())
            .then((data) => {
                if (data.status !== "success") {
                    showInfoModal("Apply repair", data.message || "Could not apply repaired version.");
                    return;
                }
                const index = questions.findIndex((item) => String(item.id) === String(questionId));
                if (index === -1) {
                    return;
                }
                questions[index] = {
                    ...questions[index],
                    ...repaired,
                    correct_answer: repairCheck.correctAnswer || repaired.correct_answer || "",
                    correct_answer_letter: repairCheck.letter || repaired.correct_answer_letter || "",
                    repair_applied_session: true,
                    ai_repair: null,
                    ai_repair_error: null,
                    validator_after_repair: null,
                    clinical_safety_review: null,
                    clinical_safety_review_source: null
                };
                applyFilter(questionId);
                refreshEditPanelIfOpen(questionId);
                showInfoModal(
                    "Apply suggestions",
                    "Suggested improvements applied to the draft. Review the text, then approve for export when ready."
                );
            })
            .catch((error) => {
                console.error(error);
                showInfoModal("Apply repair", "Could not apply repaired version.");
            });
    }

    function refreshEditPanelIfOpen(questionId) {
        if (String(currentEditQuestionId) !== String(questionId)) {
            return;
        }
        if (!editPanelWrapper.classList.contains("open")) {
            return;
        }
        const q = questions.find((item) => String(item.id) === String(questionId));
        if (q) {
            loadQuestionIntoEditPanel(q);
        }
    }

    function discardAiRepairPreview(questionId) {
        setQuestionAiRepairState(questionId, {
            ai_repair: null,
            ai_repair_error: null,
            ai_repair_loading: false,
            validator_after_repair: null,
            clinical_safety_review: null,
            clinical_safety_review_source: null,
            clinical_safety_review_error: null
        });
        refreshEditPanelIfOpen(questionId);
    }

    function updateFilterButtons() {
        const buttons = document.querySelectorAll(".filter-btn");
        const filtersDisabled = questions.length === 0;

        buttons.forEach(button => {
            const filterValue = button.getAttribute("data-filter");
            button.disabled = filtersDisabled;

            if (filterValue === currentFilter) {
                button.classList.add("active-filter");
            } else {
                button.classList.remove("active-filter");
            }
        });
    }

    function formatJumpOptionLabel(q, displayIndex) {
        const fullTitle = String((q && q.title) || "Untitled question").trim() || "Untitled question";
        return `Q${displayIndex} · ${fullTitle}`;
    }

    function updateJumpDropdown() {
        if (!jumpSelect) return;

        jumpSelect.setAttribute("aria-label", "Select question");
        jumpSelect.innerHTML = "";
        jumpSelect.removeAttribute("title");

        if (filteredQuestions.length === 0) {
            jumpSelect.disabled = true;
            return;
        }

        jumpSelect.disabled = filteredQuestions.length <= 1;

        filteredQuestions.forEach((q, index) => {
            const option = document.createElement("option");
            option.value = String(index);
            option.textContent = formatJumpOptionLabel(q, index + 1);

            if (index === currentIndex) {
                option.selected = true;
            }

            jumpSelect.appendChild(option);
        });
    }

    function updateNavButtons() {
        if (prevBtn) {
            prevBtn.disabled = filteredQuestions.length === 0 || currentIndex <= 0;
        }

        if (nextBtn) {
            nextBtn.disabled = filteredQuestions.length === 0 || currentIndex >= filteredQuestions.length - 1;
        }

        if (nextDraftBtn) {
            const hasDraft = questions.some((q) => (q.status || "Draft") === "Draft");
            nextDraftBtn.disabled = !hasDraft;
        }

        updateWorkspaceChrome();
    }

    function updateExportReadinessPanel() {
        const total = questions.length;
        const draft = questions.filter((q) => (q.status || "Draft") === "Draft").length;
        const done = questions.filter((q) => q.status === "Done").length;
        const rejected = questions.filter((q) => q.status === "Rejected").length;

        const summaryCompact = document.getElementById("export-summary-compact");
        const readyCompact = document.getElementById("export-ready-compact");
        const exportBtn = document.getElementById("open-export-modal-btn");
        const totalEl = document.getElementById("export-total");
        const draftEl = document.getElementById("export-draft");
        const doneEl = document.getElementById("export-done");
        const rejectedEl = document.getElementById("export-rejected");
        const listEl = document.getElementById("export-ready-items");

        if (summaryCompact) {
            summaryCompact.textContent = `Draft: ${draft} · Approved: ${done} · Rejected: ${rejected}`;
        }
        if (readyCompact) {
            readyCompact.textContent =
                done > 0
                    ? `Ready for export: ${done}`
                    : "No questions ready for export yet.";
            readyCompact.classList.toggle("sidebar-compact-line--muted", done === 0);
        }
        if (exportBtn) {
            exportBtn.classList.toggle("hidden", done === 0);
        }

        if (totalEl) totalEl.innerText = "Total: " + total;
        if (draftEl) draftEl.innerText = "Draft: " + draft;
        if (doneEl) doneEl.innerText = "Approved: " + done;
        if (rejectedEl) rejectedEl.innerText = "Rejected: " + rejected;

        if (listEl) {
            listEl.innerHTML = "";
            const readyQuestions = questions.filter((q) => q.status === "Done");
            if (readyQuestions.length === 0) {
                listEl.innerHTML = "<li class=\"export-ready-empty\">None</li>";
            } else {
                readyQuestions.forEach((q) => {
                    const li = document.createElement("li");
                    li.className = "export-ready-item";
                    const titleSpan = document.createElement("span");
                    titleSpan.className = "export-ready-title";
                    titleSpan.innerText = q.title || "(Untitled question)";
                    const removeBtn = document.createElement("button");
                    removeBtn.type = "button";
                    removeBtn.className = "export-remove-btn btn-quiet btn--small";
                    removeBtn.innerText = "Remove";
                    removeBtn.title = "Move to Rejected";
                    removeBtn.setAttribute("data-id", q.id);
                    removeBtn.onclick = function () {
                        moveDoneQuestionToRejected(q.id);
                    };
                    li.appendChild(titleSpan);
                    li.appendChild(removeBtn);
                    listEl.appendChild(li);
                });
            }
        }
    }

    function updateWorkspaceChrome() {
        const totalQuestions = questions.length;
        const filteredCount = filteredQuestions.length;
        const multiFiltered = filteredCount > 1;
        const noQuestions = totalQuestions === 0;
        const showWorkspaceEmpty = filteredCount === 0;

        const questionListEmpty = document.getElementById("question-list-empty");
        const questionListControls = document.getElementById("question-list-controls");
        if (questionListEmpty) {
            questionListEmpty.classList.toggle("hidden", !noQuestions);
        }
        if (questionListControls) {
            questionListControls.classList.toggle("hidden", noQuestions);
            questionListControls.setAttribute("aria-hidden", noQuestions ? "true" : "false");
        }

        const questionDisplayCard = document.getElementById("question-display-card");
        const aiReviewCard = document.getElementById("ai-review-card");
        if (questionDisplayCard) {
            questionDisplayCard.classList.toggle("card--empty-state", showWorkspaceEmpty);
        }
        if (aiReviewCard) {
            aiReviewCard.classList.toggle("card--empty-state", showWorkspaceEmpty);
        }

        const aiReviewNote = document.getElementById("ai-review-column-note");
        if (aiReviewNote) {
            aiReviewNote.classList.toggle("hidden", showWorkspaceEmpty);
        }

        const jumpContainer = document.getElementById("jump-container");
        if (jumpContainer) {
            jumpContainer.classList.toggle("hidden", !multiFiltered);
        }

        const questionNav = document.getElementById("question-navigation");
        const reviewTools = document.getElementById("review-tools-row");
        if (questionNav) {
            questionNav.classList.toggle("hidden", !multiFiltered);
        }
        if (reviewTools) {
            const hasDraft = questions.some((q) => (q.status || "Draft") === "Draft");
            reviewTools.classList.toggle("hidden", !hasDraft || noQuestions);
        }

        if (counter) {
            if (filteredCount <= 1) {
                counter.classList.add("hidden");
            } else {
                counter.classList.remove("hidden");
                counter.textContent = `Question ${currentIndex + 1} of ${filteredCount}`;
            }
        }

        updateFilterButtons();
    }

    function renderQuestion(q) {
        const resolved = resolveQuestionCorrectAnswer(q);
        const letter = resolved.letter;
        const expectedByLetter = resolved.options;
        const expectedAnswer = Object.prototype.hasOwnProperty.call(expectedByLetter, letter)
            ? expectedByLetter[letter]
            : "";
        const storedAnswer = resolved.text;
        const showMismatchWarning =
            expectedAnswer !== "" &&
            storedAnswer !== "" &&
            !answerTextsMatch(storedAnswer, expectedAnswer);
        const mismatchWarningHtml = showMismatchWarning
            ? `<p class="question-inline-warn">The saved correct-answer text does not match the letter selected. Use <strong>Edit</strong> to fix.</p>`
            : "";

        return `
            <div class="question-card question-card-compact">
                <div class="question-card-content">
                <header class="question-card-header">
                    <hr class="question-card-divider question-card-divider--top" aria-hidden="true">
                    <h3 class="question-card-title">${escapeHtml(q.title || "Untitled question")}</h3>
                    ${renderQuestionMetadata(q)}
                    ${renderQuestionStatusBar(q)}
                    <hr class="question-card-divider question-card-divider--after-status" aria-hidden="true">
                </header>

                <article class="question-read-body">
                    <p class="question-read-stem">${escapeHtml(q.stem || "")}</p>
                    <p class="question-read-leadin"><strong>${escapeHtml(q.lead_in || "")}</strong></p>
                    <ul class="question-read-options">
                        <li><span class="question-opt-label">A.</span> ${escapeHtml(q.option_a || "")}</li>
                        <li><span class="question-opt-label">B.</span> ${escapeHtml(q.option_b || "")}</li>
                        <li><span class="question-opt-label">C.</span> ${escapeHtml(q.option_c || "")}</li>
                        <li><span class="question-opt-label">D.</span> ${escapeHtml(q.option_d || "")}</li>
                        <li><span class="question-opt-label">E.</span> ${escapeHtml(q.option_e || "")}</li>
                    </ul>
                    <p class="question-read-key"><strong>Correct answer:</strong> ${escapeHtml(
                        letter || q.correct_answer_letter || ""
                    )}${storedAnswer !== "" ? ` — ${escapeHtml(storedAnswer)}` : ""}</p>
                    ${mismatchWarningHtml}
                    <p class="question-read-justification"><strong>Justification:</strong> ${escapeHtml(
                        q.justification || ""
                    )}</p>
                </article>

                <hr class="question-card-divider question-card-divider--before-actions" aria-hidden="true">
                <div class="question-actions question-actions-primary">
                    <button type="button" class="mark-done-btn btn btn-primary btn--small" data-id="${q.id}">Approve for export</button>
                    <button type="button" class="edit-btn btn btn-secondary btn--small" data-id="${q.id}">Edit</button>
                    <button type="button" class="reject-btn btn btn-danger btn--small" data-id="${q.id}">Reject</button>
                </div>
                </div>
            </div>
        `;
    }

    function renderAiReviewPanelEmpty(message) {
        if (!aiReviewPanel) {
            return;
        }
        aiReviewPanel.innerHTML = `<p class="ai-review-empty">${escapeHtml(message)}</p>`;
    }

    function renderAiReviewPanelForQuestion(q) {
        if (!aiReviewPanel) {
            return;
        }
        aiReviewPanel.innerHTML = renderAiReviewAndSuggestionsContent(q);
    }

    function renderCurrentQuestion() {
        if (filteredQuestions.length === 0) {
            if (currentFilter === "All") {
                results.innerHTML = "<p class=\"workspace-empty workspace-empty--compact\">No questions generated yet.</p>";
                renderAiReviewPanelEmpty("Generate questions to run AI review.");
            } else {
                results.innerHTML = "<p class=\"workspace-empty workspace-empty--compact\">No questions match this filter.</p>";
                renderAiReviewPanelEmpty("No questions match this filter.");
            }
            updateJumpDropdown();
            updateExportReadinessPanel();
            updateNavButtons();
            return;
        }

        if (currentIndex < 0) {
            currentIndex = 0;
        }

        if (currentIndex > filteredQuestions.length - 1) {
            currentIndex = filteredQuestions.length - 1;
        }

        const q = filteredQuestions[currentIndex];
        results.innerHTML = renderQuestion(q);
        renderAiReviewPanelForQuestion(q);
        updateJumpDropdown();
        attachHandlers();
        updateExportReadinessPanel();
        updateNavButtons();
    }

    function applyFilter(preferredQuestionId = null, fallbackIndex = null) {
        let targetQuestionId = preferredQuestionId;

        if (targetQuestionId === null && filteredQuestions.length > 0 && filteredQuestions[currentIndex]) {
            targetQuestionId = filteredQuestions[currentIndex].id;
        }

        if (currentFilter === "All") {
            filteredQuestions = [...questions];
        } else {
            filteredQuestions = questions.filter(q => (q.status || "Draft") === currentFilter);
        }

        if (targetQuestionId !== null) {
            const newIndex = filteredQuestions.findIndex(q => String(q.id) === String(targetQuestionId));

            if (newIndex !== -1) {
                currentIndex = newIndex;
            } else if (fallbackIndex !== null && filteredQuestions.length > 0) {
                currentIndex = Math.min(fallbackIndex, filteredQuestions.length - 1);
            } else {
                currentIndex = 0;
            }
        } else if (fallbackIndex !== null && filteredQuestions.length > 0) {
            currentIndex = Math.min(fallbackIndex, filteredQuestions.length - 1);
        } else {
            currentIndex = 0;
        }

        updateFilterButtons();
        renderCurrentQuestion();
    }

    function findAdjacentVisibleIndexAfterChange(changedQuestionId) {
        const oldIndex = filteredQuestions.findIndex(q => String(q.id) === String(changedQuestionId));

        if (oldIndex === -1) {
            return currentIndex;
        }

        if (oldIndex < filteredQuestions.length - 1) {
            return oldIndex + 1;
        }

        if (oldIndex > 0) {
            return oldIndex - 1;
        }

        return 0;
    }

    function jumpToNextDraft() {
        if (questions.length === 0) {
            showInfoModal("No Draft questions", "There are no Draft questions left to review.");
            return;
        }

        const currentQuestionId = filteredQuestions[currentIndex]?.id || null;
        let absoluteStart = -1;

        if (currentQuestionId !== null) {
            absoluteStart = questions.findIndex(q => String(q.id) === String(currentQuestionId));
        }

        for (let i = absoluteStart + 1; i < questions.length; i++) {
            if ((questions[i].status || "Draft") === "Draft") {
                currentFilter = "All";
                applyFilter(questions[i].id);
                return;
            }
        }

        for (let i = 0; i <= absoluteStart; i++) {
            if ((questions[i].status || "Draft") === "Draft") {
                currentFilter = "All";
                applyFilter(questions[i].id);
                return;
            }
        }

        showInfoModal("No Draft questions", "There are no Draft questions left to review.");
    }

    function updateCurrentQuestionInArray(updatedQuestion) {
        if (!isMainGeneratorWorkspaceQuestion(updatedQuestion)) {
            return;
        }
        const stemNorm = prepareMainStemForSave(String(updatedQuestion.stem || ""));
        updatedQuestion = {
            ...updatedQuestion,
            stem: stemNorm.stem
        };
        const index = questions.findIndex(q => String(q.id) === String(updatedQuestion.id));
        if (index !== -1) {
            const prev = questions[index];
            const prevWarnings = prev.style_warnings;
            const prevAiReview = prev.ai_review;
            const prevAiReviewError = prev.ai_review_error;
            const prevTutorReview = prev.tutor_review;
            const prevTutorReviewHash = prev.tutor_review_content_hash;
            const prevTutorReviewError = prev.tutor_review_error;
            const prevTutorReviewLoading = prev.tutor_review_loading;
            const prevIgnored = prev.suggestion_ignored_fields;
            questions[index] = updatedQuestion;
            if (Array.isArray(prevWarnings)) {
                questions[index].style_warnings = prevWarnings;
            }
            if (prevAiReview && typeof prevAiReview === "object") {
                questions[index].ai_review = prevAiReview;
            }
            if (prevAiReviewError) {
                questions[index].ai_review_error = prevAiReviewError;
            }
            if (prevTutorReview && typeof prevTutorReview === "object") {
                questions[index].tutor_review = prevTutorReview;
                questions[index].tutor_review_content_hash = prevTutorReviewHash || "";
                if (prevIgnored && typeof prevIgnored === "object") {
                    questions[index].suggestion_ignored_fields = prevIgnored;
                }
            }
            if (prevTutorReviewError) {
                questions[index].tutor_review_error = prevTutorReviewError;
            }
            if (prevTutorReviewLoading) {
                questions[index].tutor_review_loading = true;
            }
            persistTutorReviewForQuestion(questions[index]);
        }
    }

    function getEditPanelFieldValue(field) {
        const inputId = EDIT_PANEL_FIELD_IDS[field];
        if (!inputId) {
            return "";
        }
        return String(document.getElementById(inputId)?.value ?? "");
    }

    function getEditFieldValues() {
        return {
            question_id: currentEditQuestionId,
            title: getEditPanelFieldValue("title"),
            stem: getEditPanelFieldValue("stem"),
            lead_in: getEditPanelFieldValue("lead_in"),
            option_a: getEditPanelFieldValue("option_a"),
            option_b: getEditPanelFieldValue("option_b"),
            option_c: getEditPanelFieldValue("option_c"),
            option_d: getEditPanelFieldValue("option_d"),
            option_e: getEditPanelFieldValue("option_e"),
            correct_answer_letter: getEditPanelFieldValue("correct_answer_letter"),
            justification: getEditPanelFieldValue("justification")
        };
    }

    function mergeSavedEditPayloadIntoQuestion(question, payload, answerCheck) {
        const merged = {
            ...question,
            title: payload.title,
            stem: payload.stem,
            lead_in: payload.lead_in,
            option_a: payload.option_a,
            option_b: payload.option_b,
            option_c: payload.option_c,
            option_d: payload.option_d,
            option_e: payload.option_e,
            justification: payload.justification,
            correct_answer_letter: answerCheck.valid ? answerCheck.letter : payload.correct_answer_letter
        };
        if (answerCheck.valid) {
            merged.correct_answer = answerCheck.text;
        }
        return merged;
    }

    function syncQuestionDraftFieldFromEditPanel(questionId, field, value) {
        const qIndex = questions.findIndex(function (item) {
            return String(item.id) === String(questionId);
        });
        if (qIndex === -1) {
            return;
        }
        const next = { ...questions[qIndex] };
        if (field === "correct_answer_letter") {
            next.correct_answer_letter = value;
            const options = getEditPanelOptionMap();
            const hasEditOptions = Object.values(options).some(function (text) {
                return String(text).trim() !== "";
            });
            const optionMap = hasEditOptions ? options : getQuestionOptionMap(next);
            if (optionMap[value]) {
                next.correct_answer = String(optionMap[value]).trim();
            }
        } else if (Object.prototype.hasOwnProperty.call(next, field)) {
            next[field] = value;
        }
        questions[qIndex] = next;
        const filteredIndex = filteredQuestions.findIndex(function (item) {
            return String(item.id) === String(questionId);
        });
        if (filteredIndex !== -1) {
            filteredQuestions[filteredIndex] = next;
        }
    }

    function markSuggestionFieldAccepted(questionId, field) {
        const qIndex = questions.findIndex(function (item) {
            return String(item.id) === String(questionId);
        });
        if (qIndex === -1) {
            return;
        }
        const ignored = { ...(questions[qIndex].suggestion_ignored_fields || {}) };
        ignored[field] = true;
        questions[qIndex].suggestion_ignored_fields = ignored;
        const filteredIndex = filteredQuestions.findIndex(function (item) {
            return String(item.id) === String(questionId);
        });
        if (filteredIndex !== -1) {
            filteredQuestions[filteredIndex] = questions[qIndex];
        }
        persistTutorReviewForQuestion(questions[qIndex]);
    }

    function applyAcceptedSuggestionToEditField(questionId, field, value) {
        const inputId = EDIT_PANEL_FIELD_IDS[field];
        const el = inputId ? document.getElementById(inputId) : null;
        if (el) {
            el.value = value;
            el.dispatchEvent(new Event("input", { bubbles: true }));
        }
        syncQuestionDraftFieldFromEditPanel(questionId, field, value);
        markSuggestionFieldAccepted(questionId, field);
        setAutosaveStatus("Unsaved changes", "pending");
        scheduleAutosave();
    }

    function setAutosaveStatus(message, stateClass) {
        const statusEl = document.getElementById("autosave-status");
        if (!statusEl) return;
        statusEl.textContent = message;
        statusEl.className = "autosave-status " + (stateClass || "");
    }

    function saveCurrentEdit({ closeAfterSave = false } = {}) {
        if (!currentEditQuestionId) return;

        const payload = getEditFieldValues();
        const answerCheck = validateMainCorrectAnswerPayload(payload);
        if (!answerCheck.valid) {
            setAutosaveStatus("Save blocked", "error");
            showInfoModal("Save blocked", answerCheck.message);
            return;
        }

        const stemNorm = prepareMainStemForSave(payload.stem);
        payload.stem = stemNorm.stem;

        const stemInput = document.getElementById("edit-stem");
        if (stemInput && stemNorm.changed) {
            stemInput.value = stemNorm.stem;
        }

        if (hasMainUnsafeObservationGrammar(payload.stem)) {
            setAutosaveStatus("Save blocked", "error");
            showInfoModal("Save blocked", MAIN_STEM_OBSERVATION_SAVE_BLOCKED);
            return;
        }

        setAutosaveStatus("Saving...", "saving");

        fetch("../app/api/update_question.php", {
            method: "POST",
            body: new URLSearchParams(payload)
        })
        .then(response => response.json())
        .then(saveData => {
            if (saveData.status === "success") {
                return fetch(`../app/api/get_question.php?question_id=${currentEditQuestionId}`);
            }
            throw new Error(saveData.message || "Error saving question.");
        })
        .then(response => response.json())
        .then(reloadData => {
            if (reloadData.status === "success") {
                const mergedQuestion = mergeSavedEditPayloadIntoQuestion(
                    reloadData.question,
                    payload,
                    answerCheck
                );
                updateCurrentQuestionInArray(mergedQuestion);
                applyFilter(mergedQuestion.id);
                setAutosaveStatus("All changes saved", "saved");

                if (closeAfterSave) {
                    closeEditPanel();
                }
            } else {
                throw new Error(reloadData.message || "Question saved, but could not reload.");
            }
        })
        .catch(error => {
            console.error(error);
            setAutosaveStatus("Save failed", "error");
            showInfoModal("Save failed", error.message || "Error saving question.");
        });
    }

    function scheduleAutosave() {
        if (!currentEditQuestionId) return;

        setAutosaveStatus("Unsaved changes", "pending");

        if (autosaveTimer) {
            clearTimeout(autosaveTimer);
        }

        autosaveTimer = setTimeout(() => {
            saveCurrentEdit({ closeAfterSave: false });
        }, 900);
    }

    function attachAutosaveListeners() {
        const ids = [
            "edit-title",
            "edit-stem",
            "edit-lead-in",
            "edit-option-a",
            "edit-option-b",
            "edit-option-c",
            "edit-option-d",
            "edit-option-e",
            "edit-correct-answer",
            "edit-justification"
        ];

        ids.forEach(id => {
            const el = document.getElementById(id);
            if (!el) return;
            el.addEventListener("input", scheduleAutosave);
            el.addEventListener("change", scheduleAutosave);
        });
    }

    const EDIT_PANEL_FIELD_IDS = {
        title: "edit-title",
        stem: "edit-stem",
        lead_in: "edit-lead-in",
        option_a: "edit-option-a",
        option_b: "edit-option-b",
        option_c: "edit-option-c",
        option_d: "edit-option-d",
        option_e: "edit-option-e",
        correct_answer_letter: "edit-correct-answer",
        justification: "edit-justification"
    };

    const EDIT_PANEL_SUGGESTION_FIELDS = [
        "title",
        "stem",
        "lead_in",
        "option_a",
        "option_b",
        "option_c",
        "option_d",
        "option_e",
        "correct_answer_letter",
        "justification"
    ];

    function normalizeEditFieldText(value) {
        return String(value ?? "").trim();
    }

    const SUGGESTED_EDITS_BLOCKED_MESSAGE =
        "AI suggestions could not be applied because the suggested correct answer was incomplete. Original draft unchanged.";

    function isIncompleteCorrectAnswerLabel(value) {
        const t = String(value ?? "").trim();
        if (t === "") {
            return true;
        }
        return /^correct\s+answer\s*:?\s*$/i.test(t) || /^keyed\s+answer\s*:?\s*$/i.test(t);
    }

    function isPlaceholderAnswerText(value) {
        const t = String(value ?? "").trim();
        if (t === "") {
            return true;
        }
        if (isIncompleteCorrectAnswerLabel(t)) {
            return true;
        }
        if (/^[\-–—‐‑‒―]+$/.test(t)) {
            return true;
        }
        const lower = t.toLowerCase();
        return [
            "none",
            "n/a",
            "na",
            "null",
            "unknown",
            "tbd",
            "todo",
            "not applicable",
            "not set",
            "unset",
            "placeholder",
            "missing",
            "blank",
            "empty"
        ].includes(lower);
    }

    function normalizeAnswerText(value) {
        return String(value ?? "")
            .trim()
            .toLowerCase()
            .replace(/\s+/g, " ");
    }

    function answerTextsMatch(a, b) {
        const na = normalizeAnswerText(a);
        const nb = normalizeAnswerText(b);
        if (na === "" || nb === "") {
            return false;
        }
        if (na === nb) {
            return true;
        }
        return na.includes(nb) || nb.includes(na);
    }

    function getQuestionOptionMap(question) {
        return {
            A: String(question.option_a ?? ""),
            B: String(question.option_b ?? ""),
            C: String(question.option_c ?? ""),
            D: String(question.option_d ?? ""),
            E: String(question.option_e ?? "")
        };
    }

    function resolveQuestionCorrectAnswer(question) {
        const letter = String(question.correct_answer_letter ?? "")
            .trim()
            .toUpperCase();
        const options = getQuestionOptionMap(question);
        let text = String(question.correct_answer ?? "").trim();
        if (isPlaceholderAnswerText(text) && /^[A-E]$/.test(letter) && options[letter]) {
            text = String(options[letter]).trim();
        }
        return { letter, text, options };
    }

    function getEditPanelOptionMap() {
        return {
            A: String(document.getElementById("edit-option-a")?.value ?? "").trim(),
            B: String(document.getElementById("edit-option-b")?.value ?? "").trim(),
            C: String(document.getElementById("edit-option-c")?.value ?? "").trim(),
            D: String(document.getElementById("edit-option-d")?.value ?? "").trim(),
            E: String(document.getElementById("edit-option-e")?.value ?? "").trim()
        };
    }

    function resolveMainCorrectAnswerSuggestionLetter(suggested, options) {
        const sug = String(suggested ?? "").trim();
        if (sug === "" || isPlaceholderAnswerText(sug) || isIncompleteCorrectAnswerLabel(sug)) {
            return "";
        }
        if (/^[A-E]$/i.test(sug)) {
            return sug.toUpperCase();
        }
        for (const letter of ["A", "B", "C", "D", "E"]) {
            const optionText = String(options[letter] ?? "").trim();
            if (optionText !== "" && answerTextsMatch(sug, optionText)) {
                return letter;
            }
        }
        return "";
    }

    function validateMainCorrectAnswerPayload(payload) {
        const letter = String(payload.correct_answer_letter ?? "")
            .trim()
            .toUpperCase();
        if (!/^[A-E]$/.test(letter)) {
            return { valid: false, message: MAIN_CORRECT_ANSWER_SAVE_BLOCKED };
        }
        const options = {
            A: String(payload.option_a ?? "").trim(),
            B: String(payload.option_b ?? "").trim(),
            C: String(payload.option_c ?? "").trim(),
            D: String(payload.option_d ?? "").trim(),
            E: String(payload.option_e ?? "").trim()
        };
        const optionText = String(options[letter] ?? "").trim();
        if (optionText === "" || isPlaceholderAnswerText(optionText)) {
            return { valid: false, message: MAIN_CORRECT_ANSWER_SAVE_BLOCKED };
        }
        return { valid: true, letter: letter, text: optionText, options: options };
    }

    function isMainCorrectAnswerSuggestionAcceptable(question, suggestedText) {
        const options = getEditPanelOptionMap();
        const hasEditOptions = Object.values(options).some(function (text) {
            return String(text).trim() !== "";
        });
        const optionMap = hasEditOptions ? options : getQuestionOptionMap(question);
        const letter = resolveMainCorrectAnswerSuggestionLetter(suggestedText, optionMap);
        if (!letter) {
            return false;
        }
        const optionText = String(optionMap[letter] ?? "").trim();
        return optionText !== "" && !isPlaceholderAnswerText(optionText);
    }

    function validateRepairedQuestionPreview(repaired) {
        if (!repaired || typeof repaired !== "object") {
            return { ok: false, message: SUGGESTED_EDITS_BLOCKED_MESSAGE };
        }
        const options = getQuestionOptionMap(repaired);
        for (const letter of ["A", "B", "C", "D", "E"]) {
            const text = String(options[letter] ?? "").trim();
            if (text === "" || isPlaceholderAnswerText(text)) {
                return { ok: false, message: SUGGESTED_EDITS_BLOCKED_MESSAGE };
            }
        }
        const letter = String(repaired.correct_answer_letter ?? "")
            .trim()
            .toUpperCase();
        if (!/^[A-E]$/.test(letter)) {
            return { ok: false, message: SUGGESTED_EDITS_BLOCKED_MESSAGE };
        }
        let correctAnswer = String(repaired.correct_answer ?? "").trim();
        if (isPlaceholderAnswerText(correctAnswer)) {
            correctAnswer = String(options[letter] ?? "").trim();
        }
        if (correctAnswer === "" || isPlaceholderAnswerText(correctAnswer)) {
            return { ok: false, message: SUGGESTED_EDITS_BLOCKED_MESSAGE };
        }
        if (!answerTextsMatch(correctAnswer, options[letter])) {
            return { ok: false, message: SUGGESTED_EDITS_BLOCKED_MESSAGE };
        }
        return { ok: true, message: "", letter, correctAnswer };
    }

    const TUTOR_REVIEW_DUPLICATE_OPTIONS_BANNER =
        "AI suggestions were not applied because two options may overlap. Original draft unchanged.";

    const TUTOR_REVIEW_FAILURE_MESSAGES = {
        timeout: "AI review timed out. Original draft unchanged.",
        incomplete_response: "AI review returned an incomplete response. Original draft unchanged.",
        suggestions_inconsistent:
            "AI suggestions were blocked because they were inconsistent. Original draft unchanged.",
        generic: "AI review failed. Original draft unchanged."
    };

    function resolveTutorReviewFailureMessage(data) {
        const payload = data && typeof data === "object" ? data : {};
        const fromApi = String(payload.tutor_message || "").trim();
        if (fromApi !== "") {
            return fromApi;
        }
        const code = String(payload.error_code || "").trim();
        if (code && Object.prototype.hasOwnProperty.call(TUTOR_REVIEW_FAILURE_MESSAGES, code)) {
            return TUTOR_REVIEW_FAILURE_MESSAGES[code];
        }
        return TUTOR_REVIEW_FAILURE_MESSAGES.generic;
    }

    function applyTutorReviewFailureState(questionId, data) {
        const payload = data && typeof data === "object" ? data : {};
        const tutorMessage = resolveTutorReviewFailureMessage(payload);
        const errorCode = String(payload.error_code || "generic").trim() || "generic";
        const detail = String(payload.detail || payload.message || payload.error || "").trim();
        if (detail !== "") {
            console.error("[tutor_review] failed", { questionId: questionId, error_code: errorCode, detail: detail });
        } else {
            console.error("[tutor_review] failed", { questionId: questionId, error_code: errorCode });
        }
        setQuestionReviewSessionState(questionId, {
            tutor_review_loading: false,
            tutor_review_error: tutorMessage,
            tutor_review_error_code: errorCode
        });
    }

    function getTutorReviewErrorDisplayMessage(q) {
        const msg = String(q.tutor_review_error || "").trim();
        if (msg !== "") {
            return msg;
        }
        const code = String(q.tutor_review_error_code || "").trim();
        if (code && Object.prototype.hasOwnProperty.call(TUTOR_REVIEW_FAILURE_MESSAGES, code)) {
            return TUTOR_REVIEW_FAILURE_MESSAGES[code];
        }
        return TUTOR_REVIEW_FAILURE_MESSAGES.generic;
    }

    function getTutorReviewSuggestedEditsBlockedReason(review) {
        if (!review || typeof review !== "object") {
            return "";
        }
        const status = review.status && typeof review.status === "object" ? review.status : {};
        return String(status.suggested_edits_blocked_reason || "").trim();
    }

    function isTutorReviewDuplicateOptionsBlocked(review) {
        if (!review || typeof review !== "object") {
            return false;
        }
        const status = review.status && typeof review.status === "object" ? review.status : {};
        return status.duplicate_options_detected === true;
    }

    function isTutorReviewSuggestedEditsBlocked(review) {
        if (!review || typeof review !== "object") {
            return false;
        }
        const status = review.status && typeof review.status === "object" ? review.status : {};
        return status.suggested_edits_valid === false;
    }

    function renderTutorReviewSuggestedEditsBlockedBanner(review) {
        if (!isTutorReviewSuggestedEditsBlocked(review)) {
            return "";
        }
        const message = isTutorReviewDuplicateOptionsBlocked(review)
            ? TUTOR_REVIEW_DUPLICATE_OPTIONS_BANNER
            : getTutorReviewSuggestedEditsBlockedReason(review);
        if (message === "") {
            return "";
        }
        return `<p class="ai-support-alert ai-support-alert--warn ai-support-alert--blocked-edits">${escapeHtml(message)}</p>`;
    }

    function isTutorReviewConcernLineRedundantForBlockedEdits(line, review) {
        const t = String(line || "").trim();
        if (t === "") {
            return true;
        }
        if (!isTutorReviewSuggestedEditsBlocked(review)) {
            return false;
        }
        const blocked = getTutorReviewSuggestedEditsBlockedReason(review);
        if (blocked !== "" && t === blocked) {
            return true;
        }
        if (isTutorReviewDuplicateOptionsBlocked(review)) {
            const lower = t.toLowerCase();
            if (lower === TUTOR_REVIEW_DUPLICATE_OPTIONS_BANNER.toLowerCase()) {
                return true;
            }
            if (
                lower.includes("original draft unchanged") &&
                (lower.includes("two options may overlap") ||
                    lower.includes("duplicate") ||
                    lower.includes("near-duplicate") ||
                    lower.includes("ai suggestions need tutor review"))
            ) {
                return true;
            }
        }
        return false;
    }

    function filterTutorReviewConcernLines(lines, review) {
        if (!Array.isArray(lines)) {
            return [];
        }
        return lines.filter(function (line) {
            return !isTutorReviewConcernLineRedundantForBlockedEdits(line, review);
        });
    }

    function renderTutorReviewTechnicalDetails(q) {
        const review = q.tutor_review;
        if (!review || typeof review !== "object") {
            return "";
        }
        const status = review.status && typeof review.status === "object" ? review.status : {};
        const parts = [];

        if (status.duplicate_options_detail) {
            parts.push(
                `<p class="generator-collapsed-muted"><strong>Overlapping options:</strong> ${escapeHtml(
                    String(status.duplicate_options_detail)
                )}</p>`
            );
        }
        if (status.suggested_edits_blocked_reason && isTutorReviewSuggestedEditsBlocked(review)) {
            parts.push(
                `<p class="generator-collapsed-muted"><strong>Suggested edits blocked:</strong> ${escapeHtml(
                    String(status.suggested_edits_blocked_reason)
                )}</p>`
            );
        }

        const edits = Array.isArray(review.suggested_edits) ? review.suggested_edits : [];
        if (edits.length > 0 && !isTutorReviewSuggestedEditsUsable(q)) {
            parts.push(
                `<details class="generator-nested-details"><summary>Blocked AI suggestion payload (${edits.length})</summary>
                <ul class="generator-ai-review-changes-list">${edits
                    .map(function (edit) {
                        return `<li class="generator-ai-review-change-item">
                            <span class="generator-ai-review-change-field">${escapeHtml(edit.field || "")}</span>
                            <p class="generator-ai-review-change-issue">${escapeHtml(edit.reason || "")}</p>
                            <p class="generator-collapsed-muted">Suggested: ${escapeHtml(String(edit.suggested ?? ""))}</p>
                        </li>`;
                    })
                    .join("")}</ul></details>`
            );
        }

        if (q.tutor_review_error) {
            const code = String(q.tutor_review_error_code || "").trim();
            if (code !== "") {
                parts.push(
                    `<p class="generator-collapsed-muted"><strong>Last review error code:</strong> ${escapeHtml(code)}</p>`
                );
            }
        }

        if (parts.length === 0) {
            return "";
        }

        return `<h4 class="generator-ai-review-h4">AI review (technical)</h4>${parts.join("")}`;
    }

    function isTutorReviewSuggestedEditsUsable(q) {
        const review = q.tutor_review;
        if (!review || typeof review !== "object") {
            return false;
        }
        const status = review.status && typeof review.status === "object" ? review.status : {};
        if (status.suggested_edits_valid === false) {
            return false;
        }
        return true;
    }

    function getQuestionDraftFieldValue(question, field) {
        return String(question[field] ?? "");
    }

    function getRepairedPreviewFieldValue(repair, field) {
        if (!repair || typeof repair !== "object" || !repair.repaired_question) {
            return null;
        }
        const repaired = repair.repaired_question;
        if (repaired && typeof repaired === "object" && Object.prototype.hasOwnProperty.call(repaired, field)) {
            return String(repaired[field] ?? "");
        }
        if (!Array.isArray(repair.change_log)) {
            return null;
        }
        for (let i = 0; i < repair.change_log.length; i++) {
            if (String(repair.change_log[i].field || "") === field) {
                return String(repair.change_log[i].after ?? "");
            }
        }
        return null;
    }

    function getRepairSuggestionForField(repair, field) {
        const value = getRepairedPreviewFieldValue(repair, field);
        return value === null ? "" : value;
    }

    function repairFieldSuggestionDiffers(question, repair, field) {
        if (!repair || repair.repair_blocked || !repair.repaired_question) {
            return false;
        }
        if (!validateRepairedQuestionPreview(repair.repaired_question).ok) {
            return false;
        }
        const suggested = getRepairedPreviewFieldValue(repair, field);
        if (suggested === null) {
            return false;
        }
        const current = getQuestionDraftFieldValue(question, field);
        return normalizeEditFieldText(current) !== normalizeEditFieldText(suggested);
    }

    function countRepairFieldSuggestions(question) {
        const tutorCount = countTutorReviewSuggestions(question);
        if (tutorCount > 0) {
            return tutorCount;
        }
        const repair = question.ai_repair;
        if (!repair || repair.repair_blocked || !repair.repaired_question) {
            return 0;
        }
        let count = 0;
        EDIT_PANEL_SUGGESTION_FIELDS.forEach(function (field) {
            if (repairFieldSuggestionDiffers(question, repair, field)) {
                count += 1;
            }
        });
        return count;
    }

    function renderEditPanelSuggestionSummary(question) {
        const count = countRepairFieldSuggestions(question);
        if (count === 0) {
            return "";
        }
        const noun = count === 1 ? "field" : "fields";
        return `<p class="edit-panel-note edit-panel-note--info">Suggested edits are available for ${count} ${noun}. Review before saving.</p>`;
    }

    function renderEditPanelAlerts(question) {
        const blocks = [];
        if (isTutorReviewStale(question)) {
            blocks.push(
                `<p class="edit-panel-note edit-panel-note--warn">These suggestions were generated before the latest saved edit and may no longer match the current question.</p>`
            );
        }
        const tutorReview = question.tutor_review;
        const tutorBlockedReason = getTutorReviewSuggestedEditsBlockedReason(tutorReview);
        if (tutorBlockedReason !== "" && !isTutorReviewDuplicateOptionsBlocked(tutorReview)) {
            blocks.push(`<p class="edit-panel-note edit-panel-note--warn">${escapeHtml(tutorBlockedReason)}</p>`);
        }
        if (tutorReview && typeof tutorReview === "object") {
            const judgement = String(tutorReview.overall_judgement || "");
            const clinicalSafety =
                tutorReview.status && tutorReview.status.clinical_safety
                    ? String(tutorReview.status.clinical_safety)
                    : "";
            if (clinicalSafety === "unsafe" || judgement === "do_not_approve_yet") {
                blocks.push(
                    `<p class="edit-panel-note edit-panel-note--danger">Clinical concern identified. Do not approve until edited and reviewed.</p>`
                );
            }
        }

        const repair = question.ai_repair;
        if (repair && repair.repair_blocked) {
            blocks.push(
                `<p class="edit-panel-note edit-panel-note--warn">Suggested improvements were blocked because a possible clinical safety issue was detected. Please edit manually.</p>`
            );
        }

        const legacySafety = question.clinical_safety_review;
        if (legacySafety && typeof legacySafety === "object" && !tutorReview) {
            const rating = String(legacySafety.clinical_safety_rating || "");
            if (rating === "unsafe" || rating === "needs_faculty_review") {
                blocks.push(
                    `<p class="edit-panel-note edit-panel-note--danger">Clinical concern identified. Do not approve until edited and reviewed.</p>`
                );
            }
        }

        return blocks.join("");
    }

    function fieldHasActiveSuggestion(question, field) {
        const ignored = question.suggestion_ignored_fields || {};
        if (ignored[field]) {
            return false;
        }
        if (!isTutorReviewSuggestedEditsUsable(question)) {
            const repair = question.ai_repair;
            return repairFieldSuggestionDiffers(question, repair, field);
        }
        const tutorEdit = getTutorReviewEditForPanelField(question, field);
        if (tutorEdit) {
            if (isTutorReviewStale(question)) {
                return tutorReviewSuggestionAppliesToSavedQuestion(question, tutorEdit);
            }
            if (tutorReviewFieldSuggestionDiffers(question, tutorEdit)) {
                return true;
            }
        }
        const repair = question.ai_repair;
        return repairFieldSuggestionDiffers(question, repair, field);
    }

    function getSuggestionValueForEditField(question, field) {
        const tutorEdit = isTutorReviewSuggestedEditsUsable(question)
            ? getTutorReviewEditForPanelField(question, field)
            : null;
        if (tutorEdit && tutorReviewFieldSuggestionDiffers(question, tutorEdit)) {
            const suggested = String(tutorEdit.suggested ?? "").trim();
            if (field === "correct_answer_letter") {
                const options = getEditPanelOptionMap();
                const hasEditOptions = Object.values(options).some(function (text) {
                    return String(text).trim() !== "";
                });
                const optionMap = hasEditOptions ? options : getQuestionOptionMap(question);
                const letter = resolveMainCorrectAnswerSuggestionLetter(suggested, optionMap);
                return letter || "";
            }
            return suggested;
        }
        return getRepairSuggestionForField(question.ai_repair, field);
    }

    function renderEditFieldSuggestionBlock(question, field) {
        if (!fieldHasActiveSuggestion(question, field)) {
            return "";
        }

        const tutorEdit = isTutorReviewSuggestedEditsUsable(question)
            ? getTutorReviewEditForPanelField(question, field)
            : null;
        let displaySuggested = "";
        let reasonHtml = "";
        if (tutorEdit && tutorReviewFieldSuggestionDiffers(question, tutorEdit)) {
            displaySuggested = String(tutorEdit.suggested ?? "").trim();
            if (tutorEdit.reason) {
                reasonHtml = `<p class="edit-suggestion-reason">${escapeHtml(String(tutorEdit.reason))}</p>`;
            }
        } else {
            const repair = question.ai_repair;
            const suggested = getRepairedPreviewFieldValue(repair, field);
            displaySuggested = suggested === null ? "" : String(suggested);
        }

        if (displaySuggested === "") {
            displaySuggested = "(empty)";
        }

        let suggestionBlocked = false;
        let suggestionBlockedNote = "";
        if (field === "stem" && displaySuggested !== "(empty)") {
            const currentStem = getEditPanelFieldValue("stem") || question.stem || "";
            const acceptCheck = validateMainStemSuggestionAcceptance(displaySuggested, currentStem);
            if (acceptCheck.valid) {
                displaySuggested = acceptCheck.stem;
            } else {
                suggestionBlocked = true;
                suggestionBlockedNote = MAIN_STEM_OBSERVATION_SUGGESTION_NOTE;
                if (acceptCheck.stem && acceptCheck.stem !== displaySuggested) {
                    displaySuggested = acceptCheck.stem;
                }
            }
        }
        if (field === "correct_answer_letter" && displaySuggested !== "(empty)") {
            if (!isMainCorrectAnswerSuggestionAcceptable(question, displaySuggested)) {
                suggestionBlocked = true;
                suggestionBlockedNote = MAIN_CORRECT_ANSWER_SUGGESTION_NOTE;
            } else {
                const options = getQuestionOptionMap(question);
                const letter = resolveMainCorrectAnswerSuggestionLetter(displaySuggested, options);
                if (letter) {
                    displaySuggested = letter;
                }
            }
        }
        const advisoryNote = suggestionBlocked
            ? `<p class="edit-suggestion-reason edit-suggestion-reason--warn" role="note">${escapeHtml(
                  suggestionBlockedNote
              )}</p>`
            : "";
        const acceptButton = suggestionBlocked
            ? ""
            : `<button type="button" class="use-suggestion-btn btn btn-secondary btn--subtle" data-field="${escapeHtml(
                  field
              )}">Accept suggestion</button>`;

        return `<div class="edit-field-suggestion" data-field="${escapeHtml(field)}">
            <p class="edit-suggestion-heading"><strong>Suggested edit</strong></p>
            <p class="edit-suggestion-proposed">${escapeHtml(displaySuggested)}</p>
            ${reasonHtml}
            ${advisoryNote}
            <div class="edit-suggestion-actions">
                ${acceptButton}
                <button type="button" class="keep-current-btn btn btn-secondary btn--subtle" data-field="${escapeHtml(
                    field
                )}">Keep current</button>
            </div>
        </div>`;
    }

    function loadQuestionIntoEditPanel(question) {
        if (!editPanelBody) {
            return;
        }

        currentEditQuestionId = String(question.id);
        setAutosaveStatus("No changes yet", "");

        const stemForEdit = prepareMainStemForSave(question.stem || "").stem;

        editPanelBody.innerHTML = `
            ${renderEditPanelAlerts(question)}
            ${renderEditPanelSuggestionSummary(question)}

            <div class="edit-field-group">
                <label for="edit-title">Title</label>
                <input type="text" id="edit-title" value="${escapeHtml(question.title || "")}">
                ${renderEditFieldSuggestionBlock(question, "title")}
            </div>

            <div class="edit-field-group">
                <label for="edit-stem">Stem</label>
                <textarea id="edit-stem" rows="5">${escapeHtml(stemForEdit)}</textarea>
                ${renderEditFieldSuggestionBlock(question, "stem")}
            </div>

            <div class="edit-field-group">
                <label for="edit-lead-in">Lead-in</label>
                <textarea id="edit-lead-in" rows="3">${escapeHtml(question.lead_in || "")}</textarea>
                ${renderEditFieldSuggestionBlock(question, "lead_in")}
            </div>

            <div class="edit-field-group">
                <label for="edit-option-a">Option A</label>
                <input type="text" id="edit-option-a" value="${escapeHtml(question.option_a || "")}">
                ${renderEditFieldSuggestionBlock(question, "option_a")}
            </div>

            <div class="edit-field-group">
                <label for="edit-option-b">Option B</label>
                <input type="text" id="edit-option-b" value="${escapeHtml(question.option_b || "")}">
                ${renderEditFieldSuggestionBlock(question, "option_b")}
            </div>

            <div class="edit-field-group">
                <label for="edit-option-c">Option C</label>
                <input type="text" id="edit-option-c" value="${escapeHtml(question.option_c || "")}">
                ${renderEditFieldSuggestionBlock(question, "option_c")}
            </div>

            <div class="edit-field-group">
                <label for="edit-option-d">Option D</label>
                <input type="text" id="edit-option-d" value="${escapeHtml(question.option_d || "")}">
                ${renderEditFieldSuggestionBlock(question, "option_d")}
            </div>

            <div class="edit-field-group">
                <label for="edit-option-e">Option E</label>
                <input type="text" id="edit-option-e" value="${escapeHtml(question.option_e || "")}">
                ${renderEditFieldSuggestionBlock(question, "option_e")}
            </div>

            <div class="edit-field-group">
                <label for="edit-correct-answer">Correct answer</label>
                <select id="edit-correct-answer">
                    <option value="">Select answer</option>
                    <option value="A" ${question.correct_answer_letter === "A" ? "selected" : ""}>A</option>
                    <option value="B" ${question.correct_answer_letter === "B" ? "selected" : ""}>B</option>
                    <option value="C" ${question.correct_answer_letter === "C" ? "selected" : ""}>C</option>
                    <option value="D" ${question.correct_answer_letter === "D" ? "selected" : ""}>D</option>
                    <option value="E" ${question.correct_answer_letter === "E" ? "selected" : ""}>E</option>
                </select>
                ${renderEditFieldSuggestionBlock(question, "correct_answer_letter")}
            </div>

            <div class="edit-field-group">
                <label for="edit-justification">Justification</label>
                <textarea id="edit-justification" rows="4">${escapeHtml(question.justification || "")}</textarea>
                ${renderEditFieldSuggestionBlock(question, "justification")}
            </div>
        `;

        attachAutosaveListeners();

        editPanelBody.querySelectorAll(".use-suggestion-btn").forEach(function (btn) {
            btn.addEventListener("click", function () {
                const field = btn.getAttribute("data-field") || "";
                let value = getSuggestionValueForEditField(question, field);
                if (field === "stem") {
                    const currentStem = getEditPanelFieldValue("stem") || question.stem || "";
                    const acceptCheck = validateMainStemSuggestionAcceptance(value, currentStem);
                    if (!acceptCheck.valid) {
                        showInfoModal("Suggestion not applied", MAIN_STEM_OBSERVATION_ACCEPT_BLOCKED_MSG);
                        return;
                    }
                    value = acceptCheck.stem;
                }
                if (field === "correct_answer_letter") {
                    const options = getEditPanelOptionMap();
                    const hasEditOptions = Object.values(options).some(function (text) {
                        return String(text).trim() !== "";
                    });
                    const optionMap = hasEditOptions ? options : getQuestionOptionMap(question);
                    const letter = resolveMainCorrectAnswerSuggestionLetter(value, optionMap);
                    if (!letter || !isMainCorrectAnswerSuggestionAcceptable(question, value)) {
                        showInfoModal("Suggestion not applied", MAIN_CORRECT_ANSWER_ACCEPT_BLOCKED_MSG);
                        return;
                    }
                    value = letter;
                }
                const inputId = EDIT_PANEL_FIELD_IDS[field];
                const el = inputId ? document.getElementById(inputId) : null;
                if (!el) {
                    return;
                }
                applyAcceptedSuggestionToEditField(question.id, field, value);
                const block = btn.closest(".edit-field-suggestion");
                if (block) {
                    block.classList.add("edit-field-suggestion--accepted");
                    block.setAttribute("hidden", "hidden");
                }
            });
        });

        editPanelBody.querySelectorAll(".keep-current-btn").forEach(function (btn) {
            btn.addEventListener("click", function () {
                const field = btn.getAttribute("data-field") || "";
                const block = btn.closest(".edit-field-suggestion");
                if (block) {
                    block.classList.add("edit-field-suggestion--kept");
                    block.setAttribute("hidden", "hidden");
                }
                const qIndex = questions.findIndex((item) => String(item.id) === String(question.id));
                if (qIndex !== -1) {
                    const ignored = { ...(questions[qIndex].suggestion_ignored_fields || {}) };
                    ignored[field] = true;
                    questions[qIndex].suggestion_ignored_fields = ignored;
                    const filteredIndex = filteredQuestions.findIndex(
                        (item) => String(item.id) === String(question.id)
                    );
                    if (filteredIndex !== -1) {
                        filteredQuestions[filteredIndex] = questions[qIndex];
                    }
                    persistTutorReviewForQuestion(questions[qIndex]);
                }
            });
        });

        openEditPanel();
    }

    function openQuestionForEdit(questionId) {
        const localQ = questions.find((item) => String(item.id) === String(questionId));
        if (localQ) {
            loadQuestionIntoEditPanel(localQ);
            return;
        }
        fetch(`../app/api/get_question.php?question_id=${questionId}`)
            .then((response) => response.json())
            .then((data) => {
                if (data.status === "success" && isMainGeneratorWorkspaceQuestion(data.question)) {
                    loadQuestionIntoEditPanel(data.question);
                } else if (data.status === "success") {
                    showInfoModal("Load failed", "That question belongs to the Variant Generator and cannot be opened here.");
                } else {
                    showInfoModal("Load failed", data.message || "Error loading question.");
                }
            })
            .catch((error) => {
                console.error(error);
                showInfoModal("Load failed", "Could not load question for editing.");
            });
    }

    function performStatusUpdate(questionId, newStatus, autoAdvance) {
        const fallbackIndex = autoAdvance ? findAdjacentVisibleIndexAfterChange(questionId) : currentIndex;

        fetch("../app/api/update_question_status.php", {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            body: new URLSearchParams({
                question_id: questionId,
                status: newStatus
            }).toString()
        })
        .then(response => response.json())
        .then(data => {
            if (data.status === "success") {
                const foundIndex = questions.findIndex(q => String(q.id) === String(questionId));

                if (foundIndex !== -1) {
                    questions[foundIndex].status = newStatus;
                }

                if (autoAdvance) {
                    applyFilter(null, fallbackIndex);
                } else {
                    applyFilter(questionId);
                }
            } else {
                showInfoModal("Update failed", data.message || "Error updating question status.");
            }
        })
        .catch(error => {
            console.error(error);
            showInfoModal("Update failed", "Error updating question status.");
        });
    }

    function moveDoneQuestionToRejected(questionId) {
        showConfirmModal(
            "Move question to Rejected",
            "This will remove the question from the Ready for export list and change its status to Rejected. Continue?",
            function () {
                performStatusUpdate(questionId, "Rejected", false);
            }
        );
    }

    function attachHandlers() {
        const done = document.querySelector(".mark-done-btn");
        const reject = document.querySelector(".reject-btn");
        const edit = document.querySelector(".edit-btn");

        document.querySelectorAll(".discard-ai-repair-btn").forEach(function (btn) {
            btn.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();
                const questionId = this.getAttribute("data-id");
                if (questionId) {
                    discardAiRepairPreview(questionId);
                }
            };
        });

        document.querySelectorAll(".apply-ai-repair-btn").forEach(function (btn) {
            btn.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();
                const questionId = this.getAttribute("data-id");
                if (questionId) {
                    applyRepairedQuestionToDraft(questionId);
                }
            };
        });

        document.querySelectorAll(".ai-repair-tab-run-btn").forEach(function (btn) {
            btn.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();
                const questionId = this.getAttribute("data-id");
                if (!questionId || this.disabled) {
                    return;
                }
                requestGeneratorAiRepairPipeline(questionId);
            };
        });

        document.querySelectorAll(".clinical-safety-tab-run-btn").forEach(function (btn) {
            btn.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();
                const questionId = this.getAttribute("data-id");
                if (!questionId || this.disabled) {
                    return;
                }
                requestGeneratorClinicalSafetyReview(questionId).catch(function () {});
            };
        });

        document.querySelectorAll(".tutor-review-run-btn").forEach(function (btn) {
            btn.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();
                const questionId = this.getAttribute("data-id");
                if (!questionId || this.disabled) {
                    return;
                }
                requestTutorQuestionReview(questionId);
            };
        });

        document.querySelectorAll(".open-edit-suggestions-btn").forEach(function (btn) {
            btn.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();
                const questionId = this.getAttribute("data-id");
                if (questionId) {
                    openQuestionForEdit(questionId);
                }
            };
        });

        if (done) {
            done.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();

                const questionId = this.getAttribute("data-id");
                this.disabled = true;
                performStatusUpdate(questionId, "Done", true);
            };
        }

        if (reject) {
            reject.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();

                const questionId = this.getAttribute("data-id");

                showConfirmModal(
                    "Reject question",
                    "Are you sure you want to move this question to Rejected?",
                    function () {
                        performStatusUpdate(questionId, "Rejected", true);
                    }
                );
            };
        }

        if (edit) {
            edit.onclick = function (event) {
                event.preventDefault();
                event.stopPropagation();
                const questionId = this.getAttribute("data-id");
                if (questionId) {
                    openQuestionForEdit(questionId);
                }
            };
        }
    }

    function generateNextQuestion(jobId) {
        const data = new FormData();
        data.append("job_id", jobId);

        fetchWithTimeout(
            "../app/api/generate_next_question.php",
            {
                method: "POST",
                body: data
            },
            120000
        )
        .then(res => res.text())
        .then(text => {
            let data;
            try {
                data = JSON.parse(text);
            } catch (parseError) {
                console.error(parseError, text);
                const skill = currentPendingSkillLabel();
                const msg = skill
                    ? `Generation stopped. Existing drafts were saved. The next question failed to generate while generating ${skill}.`
                    : "Generation stopped. Existing drafts were saved. The next question failed to generate.";
                const errCompleted =
                    activeGenerationRun && Number.isFinite(Number(activeGenerationRun.completed))
                        ? Number(activeGenerationRun.completed)
                        : null;
                const errTotal =
                    activeGenerationRun && Number.isFinite(Number(activeGenerationRun.total))
                        ? Number(activeGenerationRun.total)
                        : null;
                activeGenerationRun = null;
                setGenerationProgress(msg, false, {
                    phase: "error",
                    completed: errCompleted,
                    total: errTotal
                });
                showInfoModal("Generation failed", msg);
                return;
            }

            if ((data.ok === true || data.status === "success") && data.question) {
                const hadNoQuestionsBefore = questions.length === 0;

                upsertQuestionInArray({
                    ...data.question,
                    style_warnings: Array.isArray(data.style_warnings) ? data.style_warnings : []
                });

                refreshQuestionListAfterGeneration(data.question, hadNoQuestionsBefore);

                const completed = Number(data.questions_completed || 0);
                const total = Number(data.num_questions || 0);

                if (activeGenerationRun) {
                    activeGenerationRun.completed = completed;
                    activeGenerationRun.total = total;
                }

                if (data.job_status === "running") {
                    setGenerationProgress(`${completed} of ${total} questions generated`, true, {
                        phase: "running",
                        completed: completed,
                        total: total,
                        showLatestLink: questions.length > 1,
                        latestQuestionId: data.question.id,
                        newDraftGenerated: !hadNoQuestionsBefore
                    });
                    generateNextQuestion(jobId);
                } else {
                    activeGenerationRun = null;
                    setGenerationProgress(`Generation complete: ${total} of ${total} questions generated`, false, {
                        phase: "complete",
                        completed: total,
                        total: total,
                        showLatestLink: questions.length > 1,
                        latestQuestionId: data.question.id
                    });
                    updateNavButtons();
                }
            } else {
                const completed = Number.isFinite(Number(data.questions_completed))
                    ? Number(data.questions_completed)
                    : Number.isFinite(Number(data.completed))
                      ? Number(data.completed)
                      : null;
                const total = Number.isFinite(Number(data.num_questions))
                    ? Number(data.num_questions)
                    : Number.isFinite(Number(data.total))
                      ? Number(data.total)
                      : null;
                const failedSkill = String(data.failed_skill || "").trim() || currentPendingSkillLabel();
                const msg = completed !== null && total !== null
                    ? `Generation stopped after ${completed} of ${total} questions. Existing drafts were saved. The${failedSkill ? " [" + failedSkill + "]" : " next"} question failed to generate.`
                    : (data.message || data.error || "Generation stopped. Existing drafts were saved. The next question failed to generate.");
                activeGenerationRun = null;
                setGenerationProgress(msg, false, {
                    phase: "error",
                    completed: completed,
                    total: total
                });
                showInfoModal("Generation failed", msg);
            }
        })
        .catch(error => {
            console.error(error);
            const isTimeout = error && error.name === "AbortError";
            const skill = currentPendingSkillLabel();
            const completed = activeGenerationRun && Number.isFinite(Number(activeGenerationRun.completed))
                ? Number(activeGenerationRun.completed)
                : null;
            const total = activeGenerationRun && Number.isFinite(Number(activeGenerationRun.total))
                ? Number(activeGenerationRun.total)
                : null;
            const msg = isTimeout
                ? (completed !== null && total !== null
                    ? `Generation stopped after ${completed} of ${total} questions. Existing drafts were saved. The${skill ? " [" + skill + "]" : " next"} question timed out.`
                    : (skill
                        ? `Generation stopped. Existing drafts were saved. The [${skill}] question timed out.`
                        : "Generation stopped. Existing drafts were saved. The next question request timed out."))
                : (skill
                    ? `Generation stopped. Existing drafts were saved. The next question failed to generate while generating ${skill}.`
                    : "Generation stopped. Existing drafts were saved. The next question failed to generate.");
            activeGenerationRun = null;
            setGenerationProgress(msg, false, {
                phase: "error",
                completed: completed,
                total: total
            });
            showInfoModal("Generation failed", msg);
        });
    }

    function fetchWithTimeout(url, options, timeoutMs) {
        const controller = new AbortController();
        const timeout = setTimeout(function () {
            controller.abort();
        }, timeoutMs);
        const merged = { ...(options || {}), signal: controller.signal };
        return fetch(url, merged).finally(function () {
            clearTimeout(timeout);
        });
    }

    function parseSkillList(skillsValue) {
        if (!skillsValue) return [];
        if (Array.isArray(skillsValue)) {
            return skillsValue.map((s) => String(s || "").trim()).filter(Boolean);
        }
        return String(skillsValue)
            .split(",")
            .map((s) => s.trim())
            .filter(Boolean);
    }

    function currentPendingSkillLabel() {
        if (!activeGenerationRun || !Array.isArray(activeGenerationRun.skills)) {
            return "";
        }
        const idx = Number(activeGenerationRun.completed || 0);
        return activeGenerationRun.skills[idx] || "";
    }

    function loadWorkspaceQuestions() {
        const workspaceId = localStorage.getItem("workspace_id");

        if (!workspaceId) {
            updateFilterButtons();
            updateJumpDropdown();
            updateExportReadinessPanel();
            updateNavButtons();
            return;
        }

        migrateLegacyTutorReviewCacheIfNeeded();

        fetch(`../app/api/get_workspace_questions.php?workspace_id=${encodeURIComponent(workspaceId)}`)
            .then(response => response.json())
            .then(data => {
                if (data.status === "success") {
                    questions = filterQuestionsForMainGenerator(data.questions);
                    pruneTutorReviewCacheToMainGeneratorQuestions();
                    applyTutorReviewCacheToQuestions();
                    applyFilter();

                    if (questions.length > 0) {
                        setGenerationProgress("No generation in progress.", false, { phase: "idle" });
                    } else {
                        setGenerationProgress("No generation in progress.", false, { phase: "idle" });
                    }
                    updateGenerationContextFromForm();
                } else {
                    setGenerationProgress("No generation in progress.", false, { phase: "idle" });
                    updateGenerationContextFromForm();
                    updateFilterButtons();
                    updateJumpDropdown();
                    updateExportReadinessPanel();
                    updateNavButtons();
                }
            })
            .catch(error => {
                console.error(error);
                setGenerationProgress("No generation in progress.", false, { phase: "idle" });
                updateGenerationContextFromForm();
                updateFilterButtons();
                updateJumpDropdown();
                updateExportReadinessPanel();
                updateNavButtons();
            });
    }

    function exportDoneQuestions() {
        const workspaceId = localStorage.getItem("workspace_id");

        if (!workspaceId) {
            showInfoModal("Export failed", "Preparing workspace....");
            return;
        }

        const doneQuestions = questions.filter(q => q.status === "Done");

        if (doneQuestions.length === 0) {
            showInfoModal("Nothing to export", "There are no questions marked Done yet.");
            return;
        }

        const url = `../app/api/export_done_questions.php?workspace_id=${encodeURIComponent(workspaceId)}`;

        const link = document.createElement("a");
        link.href = url;
        link.download = "sba_questions_risr_export.csv";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);

        showInfoModal("Export complete", "RISR export downloaded successfully.");
    }

    function exportToWord() {
        const doneQuestions = questions.filter(q => q.status === "Done");

        if (doneQuestions.length === 0) {
            showInfoModal("Nothing to export", "There are no questions marked Done yet.");
            return;
        }

        const exportDate = formatDateTimeForHeader();

        let htmlContent = `
            <html>
            <head>
                <meta charset="utf-8">
                <title>SBA Questions Export</title>
                <style>
                    body { font-family: Arial, sans-serif; line-height: 1.5; }
                    h1 { color: #010057; }
                    h2 { color: #010057; margin-top: 28px; }
                    p, li { font-size: 12pt; }
                    ul { margin-top: 6px; }
                    .question-block { margin-bottom: 28px; page-break-inside: avoid; }
                    .export-meta { margin-bottom: 22px; }
                </style>
            </head>
            <body>
                <h1>SBA Questions Export</h1>
                <div class="export-meta">
                    <p><strong>Exported:</strong> ${escapeHtml(exportDate)}</p>
                    <p><strong>Number of questions exported:</strong> ${doneQuestions.length}</p>
                </div>
        `;

        doneQuestions.forEach((q, index) => {
            htmlContent += `
                <div class="question-block">
                    <h2>Question ${index + 1}</h2>
                    <p><strong>Title:</strong> ${escapeHtml(q.title || "")}</p>
                    <p><strong>Tags:</strong> ${escapeHtml(buildTagsString(q))}</p>
                    <p><strong>Stem:</strong> ${escapeHtml(q.stem || "")}</p>
                    <p><strong>Lead-in:</strong> ${escapeHtml(q.lead_in || "")}</p>
                    <ul>
                        <li>A. ${escapeHtml(q.option_a || "")}</li>
                        <li>B. ${escapeHtml(q.option_b || "")}</li>
                        <li>C. ${escapeHtml(q.option_c || "")}</li>
                        <li>D. ${escapeHtml(q.option_d || "")}</li>
                        <li>E. ${escapeHtml(q.option_e || "")}</li>
                    </ul>
                    <p><strong>Correct Answer:</strong> ${escapeHtml(q.correct_answer_letter || "")} - ${escapeHtml(q.correct_answer || "")}</p>
                    <p><strong>Justification:</strong> ${escapeHtml(q.justification || "")}</p>
                </div>
            `;
        });

        htmlContent += `
            </body>
            </html>
        `;

        const blob = new Blob([htmlContent], { type: "application/msword" });
        const url = URL.createObjectURL(blob);

        const link = document.createElement("a");
        link.href = url;
        link.download = "sba_questions_export.doc";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);

        showInfoModal("Export complete", "Word export completed successfully.");
    }

    function exportToMicrosoftForms() {
        const workspaceId = localStorage.getItem("workspace_id");

        if (!workspaceId) {
            showInfoModal("Export failed", "Preparing workspace....");
            return;
        }

        const doneQuestions = questions.filter(q => q.status === "Done" && !isVariantWorkspaceQuestion(q));

        if (doneQuestions.length === 0) {
            showInfoModal("Nothing to export", "There are no questions marked Done yet.");
            return;
        }

        const url = `../app/api/export_forms_docx.php?workspace_id=${encodeURIComponent(workspaceId)}&source=main`;

        fetch(url)
            .then(function (response) {
                if (!response.ok) {
                    return response.text().then(function (text) {
                        throw new Error(text || "Microsoft Forms export failed.");
                    });
                }
                return response.blob();
            })
            .then(function (blob) {
                const objectUrl = URL.createObjectURL(blob);
                const link = document.createElement("a");
                link.href = objectUrl;
                link.download = "microsoft_forms_import_template.docx";
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
                URL.revokeObjectURL(objectUrl);
                showInfoModal(
                    "Export complete",
                    "Microsoft Forms quiz .docx exported (Answer and Points lines included). Import as a Quiz, then confirm all questions are Required before sharing."
                );
            })
            .catch(function (error) {
                console.error("[export_forms_docx]", error);
                showInfoModal("Export failed", "Microsoft Forms export failed. Please try again.");
            });
    }

    function exportToLearnUltra() {
        const doneQuestions = questions.filter(q => q.status === "Done");

        if (doneQuestions.length === 0) {
            showInfoModal("Nothing to export", "There are no questions marked Done yet.");
            return;
        }

        const exportDate = formatDateTimeForHeader();

        let textContent = "";
        textContent += "Learn Ultra Question Export\n";
        textContent += "===========================\n";
        textContent += "Exported: " + exportDate + "\n";
        textContent += "Number of questions exported: " + doneQuestions.length + "\n\n";

        doneQuestions.forEach((q, index) => {
            textContent += "QUESTION " + (index + 1) + "\n";
            textContent += "Title: " + (q.title || "") + "\n";
            textContent += "Tags: " + buildTagsString(q) + "\n";
            textContent += "Type: Multiple Choice\n";
            textContent += "Stem: " + (q.stem || "") + "\n";
            textContent += "Lead-in: " + (q.lead_in || "") + "\n";
            textContent += "A. " + (q.option_a || "") + "\n";
            textContent += "B. " + (q.option_b || "") + "\n";
            textContent += "C. " + (q.option_c || "") + "\n";
            textContent += "D. " + (q.option_d || "") + "\n";
            textContent += "E. " + (q.option_e || "") + "\n";
            textContent += "Correct Answer Letter: " + (q.correct_answer_letter || "") + "\n";
            textContent += "Correct Answer Text: " + (q.correct_answer || "") + "\n";
            textContent += "Explanation: " + (q.justification || "") + "\n";
            textContent += "\n----------------------------------------\n\n";
        });

        const blob = new Blob([textContent], { type: "text/plain;charset=utf-8;" });
        const url = URL.createObjectURL(blob);

        const link = document.createElement("a");
        link.href = url;
        link.download = "learn_ultra_question_export.txt";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);

        showInfoModal("Export complete", "Learn Ultra structured export completed successfully.");
    }

    function clearWorkspaceView() {
        clearTutorReviewCache();
        questions = [];
        filteredQuestions = [];
        currentIndex = 0;
        currentFilter = "All";

        results.innerHTML = "<p class=\"workspace-empty workspace-empty--compact\">No questions generated yet.</p>";
        renderAiReviewPanelEmpty("Generate questions to run AI review.");
        updateJumpDropdown();
        updateExportReadinessPanel();
        updateNavButtons();
        closeEditPanel();
        setGenerationProgress("No generation in progress.", false, { phase: "idle" });
        updateGenerationContextFromForm();
    }

function clearWorkspaceOnServer() {
    const workspaceId = localStorage.getItem("workspace_id");

    if (!workspaceId) {
        return Promise.resolve();
    }

    return fetch("../app/api/clear_workspace_questions.php", {
        method: "POST",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        },
        body: new URLSearchParams({
            workspace_id: workspaceId
        })
    }).then(res => res.text());
}

function resetWorkspace() {
    return fetch("../app/api/create_workspace.php")
        .then(res => res.text())
        .then(text => {
            const match = text.match(/Workspace created:\s*(\w+)/);

            if (!match) {
                throw new Error("Could not reset workspace");
            }

            const newId = match[1];
            localStorage.setItem("workspace_id", newId);
        });
}

    function getInfoContent(type) {
        if (type === "context") {
            return {
                title: "Generation Context",
                message: "This panel shows the key details of the current generation setup and the progress of the latest generation run."
            };
        }

        if (type === "export") {
            return {
                title: "Export Readiness",
                message: "This panel summarises how many questions are still Draft, how many are marked Done, and how many are Rejected. Only questions marked Done are included in export."
            };
        }

        if (type === "setup") {
            return {
                title: "Question setup",
                message: "Use this panel to define the question-generation inputs, including specialty, clinical focus, and the skills to assess. The generator creates one question per selected skill. Tutor comments are optional."
            };
        }

        if (type === "filter") {
            return {
                title: "Filter questions",
                message: "Use these buttons to view all questions or only questions with a selected status such as Draft, Approved, or Rejected."
            };
        }

        if (type === "questions") {
            return {
                title: "Generated question",
                message: "This section shows the selected question. Use Prev and Next or the filter panel to move through the set. Edit in the right-hand panel, run advisory checks, then approve for export or reject."
            };
        }

        if (type === "edit") {
            return {
                title: "Edit Question",
                message: "The edit panel allows you to update the title, stem, options, correct answer, and justification. Changes autosave as you type, and you can also save manually."
            };
        }

        return {
            title: "Information",
            message: "No additional information is available for this section."
        };
    }

    if (prevBtn) {
        prevBtn.onclick = function () {
            if (currentIndex > 0) {
                currentIndex--;
                renderCurrentQuestion();
            }
        };
    }

    if (nextBtn) {
        nextBtn.onclick = function () {
            if (currentIndex < filteredQuestions.length - 1) {
                currentIndex++;
                renderCurrentQuestion();
            }
        };
    }

    if (jumpSelect) {
        jumpSelect.onchange = function () {
            const selectedIndex = parseInt(this.value, 10);

            if (!isNaN(selectedIndex)) {
                currentIndex = selectedIndex;
                renderCurrentQuestion();
            }
        };
    }

    if (nextDraftBtn) {
        nextDraftBtn.onclick = function () {
            jumpToNextDraft();
        };
    }

    if (closeEditPanelBtn) {
        closeEditPanelBtn.onclick = function () {
            closeEditPanel();
        };
    }

    if (cancelEditPanelBtn) {
        cancelEditPanelBtn.onclick = function () {
            closeEditPanel();
        };
    }

    if (saveQuestionBtn) {
        saveQuestionBtn.onclick = function () {
            saveCurrentEdit({ closeAfterSave: true });
        };
    }

    if (modalClose) {
        modalClose.onclick = closeModal;
    }

    if (modalCancel) {
        modalCancel.onclick = closeModal;
    }

    if (modalOk) {
        modalOk.onclick = closeModal;
    }

    if (modalConfirm) {
        modalConfirm.onclick = function () {
            if (typeof modalConfirmAction === "function") {
                modalConfirmAction();
            }
            closeModal();
        };
    }

    if (modalOverlay) {
        modalOverlay.addEventListener("click", function (event) {
            if (event.target === modalOverlay) {
                closeModal();
            }
        });
    }

    if (openExportBtn) {
        openExportBtn.onclick = openExportModal;
    }

    if (closeExportModalBtn) {
        closeExportModalBtn.onclick = closeExportModal;
    }

    if (exportModal) {
        exportModal.addEventListener("click", function (event) {
            if (event.target === exportModal) {
                closeExportModal();
            }
        });
    }

    exportOptions.forEach(button => {
        button.onclick = function () {
            const format = this.getAttribute("data-format");

            if (format === "risr") {
                exportDoneQuestions();
            } else if (format === "word") {
                exportToWord();
            } else if (format === "forms") {
                exportToMicrosoftForms();
            } else if (format === "ultra") {
                exportToLearnUltra();
            }

            closeExportModal();
        };
    });

    document.addEventListener("keydown", function (event) {
        if (event.key === "Escape") {
            if (exportModal && !exportModal.classList.contains("hidden")) {
                closeExportModal();
                return;
            }

            if (modalOverlay && !modalOverlay.classList.contains("hidden")) {
                closeModal();
                return;
            }

            if (editPanelWrapper.classList.contains("open")) {
                closeEditPanel();
            }
        }
    });

    document.addEventListener("click", function (event) {
        if (!editPanelWrapper.classList.contains("open")) return;

        const clickedInsidePanel = editPanelWrapper.contains(event.target);
        const clickedEditButton = event.target.closest(".edit-btn");

        if (!clickedInsidePanel && !clickedEditButton) {
            closeEditPanel();
        }
    });

    if (form) {
        form.addEventListener("change", updateGenerationContextFromForm);
        form.addEventListener("input", updateGenerationContextFromForm);

        form.addEventListener("submit", function (e) {
            e.preventDefault();

            const validationError = validateGeneratorFormBeforeSubmit();
            if (validationError) {
                if (getSelectedSkillCount() === 0) {
                    showSkillsValidationError(SKILLS_REQUIRED_MESSAGE);
                } else {
                    clearSkillsValidationError();
                }
                if (validationError !== SKILLS_REQUIRED_MESSAGE) {
                    showInfoModal("Missing required fields", validationError);
                }
                return;
            }

            clearSkillsValidationError();

            const workspaceId = localStorage.getItem("workspace_id");

            if (!workspaceId) {
                showInfoModal("Preparing workspace...", "No workspace was found for this session.");
                return;
            }

            const formData = new FormData(form);
            formData.append("workspace_id", workspaceId);

            updateGenerationContextFromForm();
            const startingTotal = getSelectedSkillCount();
            setGenerationProgress("Starting generation...", true, {
                phase: "running",
                completed: 0,
                total: startingTotal > 0 ? startingTotal : null
            });

            fetchWithTimeout(
                "../app/api/start_generation_job.php",
                {
                    method: "POST",
                    body: formData
                },
                45000
            )
            .then(res => res.text())
            .then(text => {
                let data;
                try {
                    data = JSON.parse(text);
                } catch (parseError) {
                    console.error(parseError, text);
                    const msg = "Generation could not be started. No draft was saved.";
                    setGenerationProgress(msg, false, { phase: "error" });
                    showInfoModal("Generation failed", msg);
                    return;
                }

                if ((data.ok === true || data.status === "success") && data.job_id) {
                    const jobTotal = Number(data.num_questions || 0);
                    activeGenerationRun = {
                        jobId: String(data.job_id),
                        completed: 0,
                        total: jobTotal,
                        skills: parseSkillList(data.skills),
                        lastGeneratedQuestionId: null,
                        preserveSelectionId: getPreservedSelectionQuestionId()
                    };
                    setGenerationProgress("Starting generation...", true, {
                        phase: "running",
                        completed: 0,
                        total: jobTotal
                    });
                    generateNextQuestion(data.job_id);
                    return;
                }

                const msg = data.message || data.error || "Generation could not be started. No draft was saved.";
                activeGenerationRun = null;
                setGenerationProgress(msg, false, { phase: "error" });
                showInfoModal("Generation failed", msg);
            })
            .catch(error => {
                console.error(error);
                const isTimeout = error && error.name === "AbortError";
                const msg = isTimeout
                    ? "Generation could not be started. No draft was saved."
                    : "Generation could not be started. No draft was saved.";
                activeGenerationRun = null;
                setGenerationProgress(msg, false, { phase: "error" });
                showInfoModal("Generation failed", msg);
            });
        });
    }

    const filterButtons = document.querySelectorAll(".filter-btn");

    filterButtons.forEach(button => {
        button.onclick = function () {
            if (this.disabled || questions.length === 0) {
                return;
            }
            currentFilter = this.getAttribute("data-filter");
            applyFilter();
        };
    });

    const clearBtn = document.getElementById("clear-workspace-btn");

    if (clearBtn) {
    clearBtn.onclick = function () {
        showConfirmModal(
            "Clear Workspace",
            "Are you sure you want to clear the current workspace from this page? This removes the questions from the current view.",
            function () {
                clearWorkspaceOnServer()
                    .then(() => {
                        clearWorkspaceView();
                        return resetWorkspace();
                    })
                    .then(() => {
                        setGenerationProgress("No generation in progress.", false, { phase: "idle" });
                        updateGenerationContextFromForm();
                    })
                    .catch(error => {
                        console.error(error);
                        showInfoModal("Clear failed", "The workspace could not be reset.");
                    });
            }
        );
    };
    }

    const infoIcons = document.querySelectorAll(".info-icon");

    infoIcons.forEach(icon => {
        icon.onclick = function (event) {
            event.stopPropagation();
            const type = this.getAttribute("data-info");
            const info = getInfoContent(type);
            showInfoModal(info.title, info.message);
        };
    });

    const assessmentCard = document.getElementById("assessment-card");
    const assessmentToggle = document.getElementById("assessment-toggle");

    if (assessmentToggle) {
        assessmentToggle.onclick = function (event) {
            if (event.target.classList.contains("info-icon")) {
                return;
            }
            assessmentCard.classList.toggle("open");
        };
    }

    if (form) {
        form.addEventListener("submit", function () {
            if (assessmentCard) {
                assessmentCard.classList.remove("open");
            }
        });

        form.addEventListener("input", updateGenerationContextFromForm);
        form.addEventListener("change", updateGenerationContextFromForm);
    }

    if (progress) {
        progress.addEventListener("click", function (event) {
            const btn = event.target.closest(".go-to-latest-draft-btn");
            if (!btn) {
                return;
            }
            event.preventDefault();
            const questionId = btn.getAttribute("data-question-id");
            if (questionId) {
                applyFilter(questionId);
            }
        });
    }

    updateGenerationContextFromForm();
    updateSkillQuestionCountDisplay();
    closeEditPanel();
    updateFilterButtons();
    updateJumpDropdown();
    updateWorkspaceChrome();
    ensureWorkspace().then(() => {
    loadWorkspaceQuestions();
});
});