import { useEffect, useMemo, useRef, useState } from 'react';
import {
  fetchAssessmentListProjection,
  fetchFormWithSectionsAndQuestions,
  fetchRpnLikelihood,
  fetchRpnSeverity
} from '../../api.js';
import { AUTH_STORAGE_KEY } from '../../apiConfig.js';
import { clearPendingSnap, setPendingSnap } from './executorSnapObservationCache.js';
import { submitPendingSnapObservationsToDatabase } from './executorPendingSnapSubmit.js';
import { buildSectionQuestionGroups, sortSectionQuestionsRpnLast } from '../../utils/oracQuestionGroups.js';
import SnapMarkEditorModal from './SnapMarkEditorModal.jsx';
import './SnapDetailCreatePage.css';

function fmtNow() {
  const d = new Date();
  const pad = (n) => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}

function getUploaderName() {
  try {
    const u = JSON.parse(localStorage.getItem(AUTH_STORAGE_KEY) || '{}').user;
    return u?.employee_name || u?.email_id || '—';
  } catch {
    return '—';
  }
}

function fmt(val) {
  if (val == null) return '—';
  const s = String(val).trim();
  return s === '' ? '—' : s;
}

function fmtDate(val) {
  if (!val) return '—';
  try {
    return new Date(val).toLocaleDateString('en-IN', {
      day: '2-digit',
      month: 'short',
      year: 'numeric'
    });
  } catch {
    return fmt(val);
  }
}

/**
 * SAT-style RPN: question text roles (case-insensitive).
 * Admin can set `orac_combined_question` (main = RPN, children = L/S) so linking the RPN question
 * to a section auto-adds children; the snap UI still matches L/S/RPN by these text patterns.
 */
function getRpnRole(questionText) {
  const t = (questionText || '').toLowerCase();
  if (t.includes('likelihood of harm')) return 'likelihood';
  if (t.includes('severity of harm')) return 'severity';
  if (t.includes('rpn')) return 'rpn';
  return null;
}

function computeRpnValue(likelihoodStr, severityStr) {
  const l = Number(String(likelihoodStr ?? '').trim());
  const s = Number(String(severityStr ?? '').trim());
  if (!Number.isFinite(l) || !Number.isFinite(s)) return '';
  return String(l * s);
}

function formatRpnFactorDisplay(v) {
  const n = Number(v);
  if (!Number.isFinite(n)) return String(v ?? '');
  return Number.isInteger(n) ? String(n) : String(n);
}

/** SAT UI bands: x < 8 low, 8 <= x < 32 medium, x >= 32 high */
function getRpnBandClass(numericRpn) {
  const x = Number(numericRpn);
  if (!Number.isFinite(x)) return '';
  if (x < 8) return 'snap-create-rpn-band--low';
  if (x < 32) return 'snap-create-rpn-band--med';
  return 'snap-create-rpn-band--high';
}

/** Map DB question row (type + options) to a control kind. */
function questionControlType(q) {
  if (q.options?.length) return 'select';
  const t = (q.question_type || '').toLowerCase();
  if (/(dropdown|drop-down|drop down|select\s*list|multi-select|multiple\s*select)/.test(t)) {
    return 'select';
  }
  if (/(textarea|long|paragraph|multi)/.test(t)) return 'textarea';
  if (/(number|numeric|integer|decimal)/.test(t)) return 'number';
  return 'text';
}

/** Stable key for one question within one section instance (allows duplicate sections). */
function instanceAnswerKey(instanceId, questionId) {
  return `i:${instanceId}:q:${questionId}`;
}

function newExtraSectionId() {
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return crypto.randomUUID();
  }
  return `extra-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
}

function buildInstancesFromSchema(schema) {
  const sections = schema?.sections || [];
  return sections.map((sec) => ({
    instanceId: newExtraSectionId(),
    section_id: sec.section_id,
    title: sec.title?.trim() || `Section ${sec.section_id}`,
    questions: sortSectionQuestionsRpnLast(sec.questions || [])
  }));
}

function buildAnswersFromInstances(instances) {
  const initial = {};
  for (const inst of instances) {
    for (const q of inst.questions || []) {
      initial[instanceAnswerKey(inst.instanceId, q.question_id)] = '';
    }
  }
  return initial;
}

/** Flatten section instances into rows for `POST /api/orac/orac_submissions`. */
function buildSubmissionAnswers(sectionInstances, answersMap) {
  const rows = [];
  for (const inst of sectionInstances) {
    for (const q of inst.questions || []) {
      const rawId = q.question_id;
      const qid =
        typeof rawId === 'number' && Number.isInteger(rawId)
          ? rawId
          : parseInt(String(rawId ?? ''), 10);
      if (!Number.isInteger(qid) || qid <= 0) continue;
      const raw = answersMap[instanceAnswerKey(inst.instanceId, q.question_id)];
      rows.push({
        question_id: qid,
        answer_value: raw == null ? '' : String(raw),
        is_media: 0
      });
    }
  }
  return rows;
}

function hasAnyAnswer(sectionInstances, answersMap) {
  return buildSubmissionAnswers(sectionInstances, answersMap).some(
    (r) => String(r.answer_value || '').trim() !== ''
  );
}

function SnapQuestionField({
  q,
  value,
  onChange,
  inputId,
  rpnRole,
  likelihoodRows,
  severityRows
}) {
  const id = inputId ?? `snap-q-${q.question_id}`;

  if (rpnRole === 'likelihood' && likelihoodRows?.length) {
    return (
      <select
        id={id}
        className="snap-create-select snap-create-field-control"
        value={value}
        onChange={(e) => onChange(e.target.value)}
      >
        <option value="">—</option>
        {likelihoodRows.map((row) => {
          const v = formatRpnFactorDisplay(row.value);
          const optVal = Number.isFinite(Number(row.value)) ? String(Number(row.value)) : String(row.value ?? '');
          return (
            <option key={row.likelihood_of_harm} value={optVal}>
              {row.likelihood_of_harm} ({v})
            </option>
          );
        })}
      </select>
    );
  }

  if (rpnRole === 'severity' && severityRows?.length) {
    return (
      <select
        id={id}
        className="snap-create-select snap-create-field-control"
        value={value}
        onChange={(e) => onChange(e.target.value)}
      >
        <option value="">—</option>
        {severityRows.map((row) => {
          const v = formatRpnFactorDisplay(row.value);
          const optVal = Number.isFinite(Number(row.value)) ? String(Number(row.value)) : String(row.value ?? '');
          return (
            <option key={row.severity_of_harm} value={optVal}>
              {row.severity_of_harm} ({v})
            </option>
          );
        })}
      </select>
    );
  }

  if (rpnRole === 'rpn') {
    const n = Number(String(value ?? '').trim());
    const band = getRpnBandClass(n);
    const title =
      !Number.isFinite(n) || value === ''
        ? ''
        : n < 8
          ? 'Low risk'
          : n < 32
            ? 'Medium risk'
            : 'High risk';
    return (
      <input
        id={id}
        type="text"
        readOnly
        title={title || undefined}
        className={`snap-create-input snap-create-field-control snap-create-rpn-input ${band}`.trim()}
        value={value ?? ''}
        placeholder="—"
        aria-readonly="true"
      />
    );
  }

  const kind = questionControlType(q);

  if (kind === 'select') {
    return (
      <select
        id={id}
        className="snap-create-select snap-create-field-control"
        value={value}
        onChange={(e) => onChange(e.target.value)}
      >
        <option value="">—</option>
        {q.options.map((opt) => (
          <option
            key={opt.question_options_id ?? `${opt.option}-${opt.question_id}`}
            value={opt.option ?? ''}
          >
            {opt.option}
          </option>
        ))}
      </select>
    );
  }
  if (kind === 'textarea') {
    return (
      <textarea
        id={id}
        className="snap-create-textarea snap-create-field-control"
        rows={3}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        placeholder="Your answer"
      />
    );
  }
  if (kind === 'number') {
    return (
      <input
        id={id}
        type="number"
        className="snap-create-input snap-create-field-control"
        value={value}
        onChange={(e) => onChange(e.target.value)}
      />
    );
  }
  return (
    <input
      id={id}
      type="text"
      className="snap-create-input snap-create-field-control"
      value={value}
      onChange={(e) => onChange(e.target.value)}
      placeholder="Your answer"
    />
  );
}

/**
 * Full-page risk assessment after marking a snap.
 * Sections and questions come from ORAC (`orac_form_section_map` + section/question maps).
 * draft: { assessment, markedImageDataUrl, marksJson, violationTime, baseFrameDataUrl }
 */
export default function SnapDetailCreatePage({ draft, onBack, onObservationsSubmitted }) {
  const {
    assessment,
    markedImageDataUrl: draftMarkedUrl,
    marksJson: draftMarksJson,
    violationTime,
    baseFrameDataUrl: draftBaseFrame,
    resumeVideoSeconds: draftResumeVideoSeconds
  } = draft || {};
  const [localMarkedImage, setLocalMarkedImage] = useState(draftMarkedUrl ?? '');
  const [localMarksJson, setLocalMarksJson] = useState(draftMarksJson ?? '');
  /** Unmarked video frame — keeps marks on overlay so eraser can remove them in the editor */
  const [localBaseFrame, setLocalBaseFrame] = useState(draftBaseFrame ?? '');
  const [editImageOpen, setEditImageOpen] = useState(false);
  const [uploadDate] = useState(() => fmtNow());
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState('');

  const [formSchema, setFormSchema] = useState(null);
  const [formLoading, setFormLoading] = useState(true);
  const [formLoadError, setFormLoadError] = useState('');
  /** Answers keyed by instanceAnswerKey(instanceId, questionId) */
  const [answers, setAnswers] = useState({});
  /** Each entry = one section block with DB-mapped questions (orac_section_question_map). */
  const [sectionInstances, setSectionInstances] = useState([]);
  /** Merged with list API row (includes computed `orac_form_id`). */
  const [resolvedAssessment, setResolvedAssessment] = useState(assessment);
  /** Set when `orac_assessments.form_id` is not set (executor must assign on the video screen first). */
  const [formMissingHint, setFormMissingHint] = useState(null);

  /** Queued observations for this snap (browser cache). Each entry: { id, sectionInstances, answers } */
  const [observationsQueue, setObservationsQueue] = useState([]);

  /** RPN master data (`orac_likelihood_of_harm` / `orac_severity_of_harm`) for SAT-style L × S fields */
  const [rpnLikelihoodRows, setRpnLikelihoodRows] = useState([]);
  const [rpnSeverityRows, setRpnSeverityRows] = useState([]);

  const [cacheJustAdded, setCacheJustAdded] = useState(false);
  const cacheFlashTimerRef = useRef(null);
  const sectionInstancesRef = useRef([]);
  /** List to persist when user confirms submit (avoids stale state vs window.confirm). */
  const pendingSubmitListRef = useRef(null);
  const [submitConfirmOpen, setSubmitConfirmOpen] = useState(false);
  const [submitConfirmCount, setSubmitConfirmCount] = useState(0);
  const [leaveConfirmOpen, setLeaveConfirmOpen] = useState(false);

  const uploaderName = useMemo(() => getUploaderName(), []);

  useEffect(() => {
    sectionInstancesRef.current = sectionInstances;
  }, [sectionInstances]);

  useEffect(() => {
    setLocalMarkedImage(draftMarkedUrl ?? '');
    setLocalMarksJson(draftMarksJson ?? '');
    setLocalBaseFrame(draftBaseFrame ?? '');
  }, [draftMarkedUrl, draftMarksJson, draftBaseFrame]);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const [L, S] = await Promise.all([fetchRpnLikelihood(), fetchRpnSeverity()]);
        if (!cancelled) {
          setRpnLikelihoodRows(Array.isArray(L) ? L : []);
          setRpnSeverityRows(Array.isArray(S) ? S : []);
        }
      } catch {
        if (!cancelled) {
          setRpnLikelihoodRows([]);
          setRpnSeverityRows([]);
        }
      }
    })();
    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    setResolvedAssessment(assessment);
  }, [assessment?.assessment_id]);

  const videoDetailsItems = useMemo(
    () => [
      { label: 'Video title', value: fmt(resolvedAssessment?.video_title) },
      { label: 'Location', value: fmt(resolvedAssessment?.location_name) },
      { label: 'Section / work area', value: fmt(resolvedAssessment?.department_name) },
      { label: 'Contract details', value: fmt(assessment?.contract_details ?? assessment?.contact_details) },
      { label: 'Field capture date', value: fmtDate(resolvedAssessment?.video_capture_date) },
      { label: 'Field capture time', value: fmt(resolvedAssessment?.video_capture_time) },
      { label: 'Video remark', value: fmt(resolvedAssessment?.remarks) },
      { label: 'Original upload by', value: fmt(resolvedAssessment?.uploaded_by_name || resolvedAssessment?.uploader_user_id) },
      { label: 'Executor', value: fmt(resolvedAssessment?.executor_name || resolvedAssessment?.executor_id) },
      { label: 'Verifier', value: fmt(resolvedAssessment?.verifier_name || resolvedAssessment?.verifier_id) }
    ],
    [resolvedAssessment]
  );

  const snapDetailsItems = useMemo(
    () => [
      { label: 'Marked by', value: uploaderName },
      { label: 'Mark saved at', value: uploadDate },
      { label: 'Risk violation time (in video)', value: fmt(violationTime) }
    ],
    [uploaderName, uploadDate, violationTime]
  );

  useEffect(() => {
    let cancelled = false;

    async function loadForm() {
      setFormLoading(true);
      setFormLoadError('');
      setFormSchema(null);
      setAnswers({});
      setSectionInstances([]);
      setFormMissingHint(null);

      if (!assessment?.assessment_id) {
        setFormLoading(false);
        return;
      }

      let row = { ...assessment };
      try {
        const projected = await fetchAssessmentListProjection(assessment.assessment_id);
        if (!cancelled && projected) {
          row = { ...row, ...projected };
        }
        if (!cancelled) setResolvedAssessment(row);

        const explicit =
          row.form_id != null && row.form_id !== '' ? Number(row.form_id) : null;
        const formId =
          Number.isInteger(explicit) && explicit > 0 && !Number.isNaN(explicit) ? explicit : null;

        if (formId == null) {
          if (!cancelled) {
            setFormMissingHint('executor_must_assign_form');
          }
          setFormLoading(false);
          return;
        }

        const schema = await fetchFormWithSectionsAndQuestions(formId);
        if (cancelled) return;
        setFormSchema(schema);
        setFormMissingHint(null);
        const stacked = buildInstancesFromSchema(schema);
        setSectionInstances(stacked);
        setAnswers(buildAnswersFromInstances(stacked));
      } catch (e) {
        if (!cancelled) {
          setFormLoadError(e.message || 'Failed to load assessment form.');
        }
      } finally {
        if (!cancelled) setFormLoading(false);
      }
    }

    loadForm();
    return () => {
      cancelled = true;
    };
  }, [assessment?.assessment_id, assessment?.form_id, assessment?.orac_form_id]);

  function setInstanceAnswer(instanceId, questionId, value) {
    setAnswers((prev) => {
      const key = instanceAnswerKey(instanceId, questionId);
      const next = { ...prev, [key]: value };
      const inst = sectionInstancesRef.current.find((s) => s.instanceId === instanceId);
      if (!inst?.questions?.length) return next;
      const qRow = inst.questions.find((qq) => qq.question_id === questionId);
      const role = getRpnRole(qRow?.question);
      if (role !== 'likelihood' && role !== 'severity') return next;
      const lQ = inst.questions.find((qq) => getRpnRole(qq.question) === 'likelihood');
      const sQ = inst.questions.find((qq) => getRpnRole(qq.question) === 'severity');
      const rpnQ = inst.questions.find((qq) => getRpnRole(qq.question) === 'rpn');
      if (!rpnQ) return next;
      const lVal = role === 'likelihood' ? value : lQ ? next[instanceAnswerKey(instanceId, lQ.question_id)] : '';
      const sVal = role === 'severity' ? value : sQ ? next[instanceAnswerKey(instanceId, sQ.question_id)] : '';
      const rpn = computeRpnValue(lVal, sVal);
      next[instanceAnswerKey(instanceId, rpnQ.question_id)] = rpn;
      return next;
    });
  }

  function removeInstance(instanceId) {
    setSectionInstances((prev) => prev.filter((s) => s.instanceId !== instanceId));
    setAnswers((prev) => {
      const next = { ...prev };
      const prefix = `i:${instanceId}:q:`;
      for (const k of Object.keys(next)) {
        if (k.startsWith(prefix)) delete next[k];
      }
      return next;
    });
  }

  function newObservationId() {
    if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
      return crypto.randomUUID();
    }
    return `obs-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
  }

  function snapshotCurrentObservation() {
    return {
      id: newObservationId(),
      sectionInstances: sectionInstances.map((inst) => ({
        instanceId: inst.instanceId,
        section_id: inst.section_id,
        title: inst.title,
        questions: (inst.questions || []).map((q) => ({ question_id: q.question_id }))
      })),
      answers: { ...answers }
    };
  }

  function buildPendingPayload(observationsList, overrides = {}) {
    return {
      assessment_id: assessment.assessment_id,
      markedImageDataUrl: overrides.markedImageDataUrl ?? localMarkedImage,
      marksJson: overrides.marksJson ?? localMarksJson,
      baseFrameDataUrl: overrides.baseFrameDataUrl ?? localBaseFrame,
      violationTime,
      observations: observationsList,
      form_id: formSchema?.form_id ?? null,
      resolvedAssessmentSnapshot: {
        assessment_id: resolvedAssessment?.assessment_id,
        location_id: resolvedAssessment?.location_id,
        orac_form_id: resolvedAssessment?.orac_form_id
      }
    };
  }

  function writePendingCache(observationsList, overrides = {}) {
    return setPendingSnap(buildPendingPayload(observationsList, overrides));
  }

  function handleImageEditApply(markedDataUrl, newMarksJson, baseFrameDataUrlOut) {
    const nextBase =
      baseFrameDataUrlOut != null && String(baseFrameDataUrlOut).trim() !== ''
        ? baseFrameDataUrlOut
        : localBaseFrame;
    setLocalMarkedImage(markedDataUrl);
    setLocalMarksJson(newMarksJson);
    if (baseFrameDataUrlOut != null && String(baseFrameDataUrlOut).trim() !== '') {
      setLocalBaseFrame(baseFrameDataUrlOut);
    }
    setEditImageOpen(false);
    const list = [...observationsQueue];
    if (sectionInstances.length > 0 && hasAnyAnswer(sectionInstances, answers)) {
      list.push(snapshotCurrentObservation());
    }
    if (list.length > 0) {
      writePendingCache(list, {
        markedImageDataUrl: markedDataUrl,
        marksJson: newMarksJson,
        baseFrameDataUrl: nextBase
      });
    }
  }

  function handleAddObservationToQueue() {
    if (!assessment?.assessment_id) return;
    if (sectionInstances.length === 0) {
      setError('Add at least one section with questions before recording an observation.');
      return;
    }
    if (!hasAnyAnswer(sectionInstances, answers)) {
      setError('Answer at least one question before adding an observation.');
      return;
    }
    const snap = snapshotCurrentObservation();
    const next = [...observationsQueue, snap];
    const w = writePendingCache(next);
    if (!w.ok) {
      setError(w.message || 'Could not save to browser cache.');
      return;
    }
    setObservationsQueue(next);
    clearForm();
    setError('');
    if (cacheFlashTimerRef.current) clearTimeout(cacheFlashTimerRef.current);
    setCacheJustAdded(true);
    cacheFlashTimerRef.current = window.setTimeout(() => {
      setCacheJustAdded(false);
      cacheFlashTimerRef.current = null;
    }, 2800);
  }

  function handleSubmitObservations() {
    if (!assessment?.assessment_id) return;
    let list = [...observationsQueue];
    if (sectionInstances.length > 0 && hasAnyAnswer(sectionInstances, answers)) {
      list = [...list, snapshotCurrentObservation()];
    }
    if (list.length === 0) {
      setError('Fill in at least one observation before submitting.');
      return;
    }
    pendingSubmitListRef.current = list;
    setSubmitConfirmCount(list.length);
    setSubmitConfirmOpen(true);
  }

  function cancelSubmitConfirm() {
    pendingSubmitListRef.current = null;
    setSubmitConfirmCount(0);
    setSubmitConfirmOpen(false);
  }

  async function executeSubmitObservations() {
    const list = pendingSubmitListRef.current;
    if (!list?.length || !assessment?.assessment_id) {
      cancelSubmitConfirm();
      return;
    }
    const n = list.length;
    setSubmitConfirmOpen(false);
    pendingSubmitListRef.current = null;

    const backup = writePendingCache(list);
    if (!backup.ok) {
      setError(backup.message || 'Could not save to browser cache before submit.');
      return;
    }
    setSubmitting(true);
    setError('');
    try {
      const payload = buildPendingPayload(list);
      const result = await submitPendingSnapObservationsToDatabase(payload);
      let assessmentRow = null;
      try {
        assessmentRow = await fetchAssessmentListProjection(assessment.assessment_id);
      } catch {
        /* list projection optional */
      }
      const mergedAssessment =
        assessmentRow && typeof assessmentRow === 'object'
          ? { ...assessment, ...assessmentRow }
          : assessment;
      onObservationsSubmitted?.({
        observationCount: result?.observationCount ?? n,
        assessmentRow
      });
      setObservationsQueue([]);
      onBack?.({
        resumeVideoSeconds: draftResumeVideoSeconds,
        afterSubmit: true,
        assessment: mergedAssessment
      });
    } catch (e) {
      setError(e.message || 'Could not save observations to the database. Please try again.');
    } finally {
      setSubmitting(false);
    }
  }

  function handleBackToAssessment() {
    const dirty = observationsQueue.length > 0 || hasAnyAnswer(sectionInstances, answers);
    if (dirty) {
      setLeaveConfirmOpen(true);
      return;
    }
    onBack?.({ resumeVideoSeconds: draftResumeVideoSeconds });
  }

  function confirmLeaveWithoutSubmit() {
    setLeaveConfirmOpen(false);
    clearPendingSnap();
    onBack?.({ resumeVideoSeconds: draftResumeVideoSeconds });
  }

  function cancelLeaveConfirm() {
    setLeaveConfirmOpen(false);
  }

  function clearForm() {
    if (formSchema) {
      const stacked = buildInstancesFromSchema(formSchema);
      setSectionInstances(stacked);
      setAnswers(buildAnswersFromInstances(stacked));
    } else {
      setSectionInstances([]);
      setAnswers({});
    }
  }

  function renderFormBody() {
    if (formLoading) {
      return <p className="snap-create-form-loading">Loading assessment form…</p>;
    }
    if (formLoadError) {
      return <p className="snap-create-form-msg snap-create-form-msg--error">{formLoadError}</p>;
    }

    return (
      <>
        {!formSchema && formMissingHint === 'executor_must_assign_form' && (
          <>
            <p className="snap-create-form-msg">
              No ORAC form is saved on this assessment yet. Go back to the assessment page, use the{' '}
              <strong>ORAC form for this assessment</strong> section at the top (below the title bar), choose a form, save
              — then return here to continue.
            </p>
            <p className="snap-create-form-msg snap-create-form-msg--muted">
              The form must be one linked to this location (when the location has mapped forms).
            </p>
          </>
        )}

        {formSchema && sectionInstances.length === 0 && (
          <p className="snap-create-form-msg snap-create-form-msg--muted">
            This linked form has no sections yet. Ask an admin to attach sections and questions to the form in ORAC.
          </p>
        )}

        {sectionInstances.length > 0 ? (
          <div className="snap-create-form-sections-grid">
            {sectionInstances.map((inst) => (
              <section
                key={inst.instanceId}
                className="snap-create-form-section snap-create-form-section--extra"
                aria-labelledby={`sec-inst-${inst.instanceId}`}
              >
                <div className="snap-create-extra-head">
                  <h3 id={`sec-inst-${inst.instanceId}`} className="snap-create-section-title snap-create-section-title--inline">
                    {inst.title?.trim() || `Section ${inst.section_id}`}
                  </h3>
                  <button
                    type="button"
                    className="snap-create-extra-remove"
                    onClick={() => removeInstance(inst.instanceId)}
                  >
                    Remove
                  </button>
                </div>
                {(inst.questions || []).length === 0 ? (
                  <p className="snap-create-form-msg snap-create-form-msg--muted">No questions mapped to this section.</p>
                ) : (
                  <div className="snap-create-q-list">
                    {buildSectionQuestionGroups(inst.questions || []).map((g) => (
                      <div key={`${inst.instanceId}-g-${g.main.question_id}`} className="snap-create-q-group">
                        <div className="snap-create-q-field">
                          <label
                            className="snap-create-q-label"
                            htmlFor={`snap-q-${inst.instanceId}-${g.main.question_id}`}
                          >
                            {g.main.question}
                          </label>
                          <SnapQuestionField
                            q={g.main}
                            inputId={`snap-q-${inst.instanceId}-${g.main.question_id}`}
                            rpnRole={getRpnRole(g.main.question)}
                            likelihoodRows={rpnLikelihoodRows}
                            severityRows={rpnSeverityRows}
                            value={answers[instanceAnswerKey(inst.instanceId, g.main.question_id)] ?? ''}
                            onChange={(v) => setInstanceAnswer(inst.instanceId, g.main.question_id, v)}
                          />
                        </div>
                        {g.children.map((cq) => {
                          const fid = `snap-q-${inst.instanceId}-${cq.question_id}`;
                          return (
                            <div
                              key={`${inst.instanceId}-${cq.question_id}`}
                              className="snap-create-q-field snap-create-q-field--combined-child"
                            >
                              <label className="snap-create-q-label" htmlFor={fid}>
                                {cq.question}
                              </label>
                              <SnapQuestionField
                                q={cq}
                                inputId={fid}
                                rpnRole={getRpnRole(cq.question)}
                                likelihoodRows={rpnLikelihoodRows}
                                severityRows={rpnSeverityRows}
                                value={answers[instanceAnswerKey(inst.instanceId, cq.question_id)] ?? ''}
                                onChange={(v) => setInstanceAnswer(inst.instanceId, cq.question_id, v)}
                              />
                            </div>
                          );
                        })}
                      </div>
                    ))}
                  </div>
                )}
              </section>
            ))}
          </div>
        ) : null}
      </>
    );
  }

  if (!assessment) {
    return (
      <main className="dashboard-main snap-create">
        <p>Missing assessment.</p>
        <button type="button" className="snap-create-back" onClick={onBack}>
          Back
        </button>
      </main>
    );
  }

  const videoTitle = assessment?.video_title || 'Assessment';
  const formTitle = formSchema?.title?.trim() || 'Risk assessment';
  /** Form area usable: finished loading and no fatal API error */
  const canUseForm = !formLoading && !formLoadError;
  const canClear = canUseForm && sectionInstances.length > 0;
  const canAddObservationNow =
    canUseForm && sectionInstances.length > 0 && hasAnyAnswer(sectionInstances, answers);
  const canSubmitObservations =
    canUseForm &&
    (observationsQueue.length > 0 ||
      (sectionInstances.length > 0 && hasAnyAnswer(sectionInstances, answers)));

  return (
    <main className="dashboard-main snap-create">
      <header className="snap-create-header">
        <div className="snap-create-header-inner">
          <button type="button" className="snap-create-back" onClick={handleBackToAssessment}>
            <span className="snap-create-back-icon" aria-hidden>
              ‹
            </span>
            Back
          </button>
          <div className="snap-create-header-text">
            <p className="snap-create-eyebrow">Off-site risk assessment</p>
            <h1 className="snap-create-title">Snap details &amp; risk assessment</h1>
            <p className="snap-create-subtitle">{videoTitle}</p>
          </div>
        </div>
      </header>

      {error && <div className="snap-create-error">{error}</div>}

      <div className="snap-create-card">
        <div className="snap-create-split">
          <aside className="snap-create-left" aria-label="Marked image and source video details">
            <section className="snap-create-media" aria-label="Marked frame">
              <p className="snap-create-media-label">Marked frame</p>
              <div className="snap-create-media-frame">
                {localMarkedImage ? (
                  <img src={localMarkedImage} alt="Marked snap from field video" className="snap-create-img" />
                ) : (
                  <div className="snap-create-img-placeholder">No image preview</div>
                )}
              </div>
              <div className="snap-create-media-actions">
                <button
                  type="button"
                  className="snap-create-edit-mark"
                  onClick={() => setEditImageOpen(true)}
                  disabled={submitting || !localMarkedImage}
                  title="Draw or erase on this snap — opens the editor here"
                >
                  Edit image
                </button>
              </div>
              {!localBaseFrame && localMarkedImage ? (
                <p className="snap-create-edit-legacy-note">
                  Older snaps only store one merged picture. The eraser can remove strokes you add in the editor, not
                  marks already baked into the image. Use <strong>Back</strong> and <strong>Snap Photo</strong> again to
                  capture a full undoable snap.
                </p>
              ) : null}
              {observationsQueue.length > 0 && (
                <p
                  className={`snap-create-left-cache${cacheJustAdded ? ' snap-create-left-cache--flash' : ''}`}
                  role="status"
                  aria-live="polite"
                >
                  <span className="snap-create-left-cache-icon" aria-hidden>
                    ✓
                  </span>
                  {observationsQueue.length} observation{observationsQueue.length !== 1 ? 's' : ''} queued
                </p>
              )}
              <p className="snap-create-cache-note">
                Use <strong>Add observation</strong> to queue multiple observations for this snap, then click{' '}
                <strong>Submit observations</strong> to save them all to the database at once.
              </p>
            </section>

            <section className="snap-create-detail-block" aria-labelledby="snap-video-details-heading">
              <h2 id="snap-video-details-heading" className="snap-create-block-title">
                Uploaded video details
              </h2>
              <p className="snap-create-block-hint">Context from the field video for this assessment.</p>
              <dl className="snap-create-dl">
                {videoDetailsItems.map(({ label, value }, i) => (
                  <div key={`${label}-${i}`} className="snap-create-dl-item">
                    <dt>{label}</dt>
                    <dd>{value}</dd>
                  </div>
                ))}
              </dl>
            </section>

            <section className="snap-create-detail-block" aria-labelledby="snap-mark-meta-heading">
              <h2 id="snap-mark-meta-heading" className="snap-create-block-title">
                This mark
              </h2>
              <dl className="snap-create-dl">
                {snapDetailsItems.map(({ label, value }, i) => (
                  <div key={`${label}-${i}`} className="snap-create-dl-item">
                    <dt>{label}</dt>
                    <dd>{value}</dd>
                  </div>
                ))}
              </dl>
            </section>
          </aside>

          <div className="snap-create-right">
            <section
              className={`snap-create-risk-section${observationsQueue.length > 0 ? ' snap-create-risk-section--has-cache' : ''}`}
              aria-labelledby="snap-risk-heading"
            >
              <div className="snap-create-risk-head">
                <div className="snap-create-risk-head-row">
                  <div className="snap-create-risk-head-text">
                    <h2 id="snap-risk-heading">{formTitle}</h2>
                    <p className="snap-create-risk-lead">
                      {formSchema?.description?.trim() ||
                        'Sections are built from ORAC: each section loads its mapped questions.'}
                    </p>
                    {observationsQueue.length > 0 && (
                      <p
                        className={`snap-create-queue-badge${cacheJustAdded ? ' snap-create-queue-badge--flash' : ''}`}
                        role="status"
                        aria-live="polite"
                      >
                        <span className="snap-create-queue-badge-icon" aria-hidden>
                          ✓
                        </span>
                        {observationsQueue.length} observation{observationsQueue.length !== 1 ? 's' : ''} queued —
                        add more or click <strong>Submit observations</strong> to save all to the database.
                      </p>
                    )}
                  </div>
                </div>
              </div>

              <div className="snap-create-form-scroll">{renderFormBody()}</div>
            </section>

            <footer className="snap-create-footer">
              <button type="button" className="snap-create-clear" onClick={clearForm} disabled={!canClear}>
                Clear form
              </button>
              <div className="snap-create-footer-actions snap-create-footer-actions--wrap">
                <button
                  type="button"
                  className="snap-create-submit snap-create-submit-secondary"
                  onClick={handleBackToAssessment}
                  disabled={submitting}
                >
                  Back to assessment
                </button>
                <button
                  type="button"
                  className="snap-create-submit snap-create-submit-secondary"
                  onClick={handleAddObservationToQueue}
                  disabled={submitting || !canAddObservationNow}
                >
                  Add observation
                </button>
                <button
                  type="button"
                  className="snap-create-submit"
                  onClick={handleSubmitObservations}
                  disabled={submitting || !canSubmitObservations}
                >
                  {submitting ? 'Saving to database…' : 'Submit observations'}
                </button>
              </div>
            </footer>
          </div>
        </div>
      </div>

      {editImageOpen && localMarkedImage ? (
        <SnapMarkEditorModal
          snapDataUrl={localBaseFrame || localMarkedImage}
          initialMarksJson={localBaseFrame ? localMarksJson : undefined}
          exportUnmarkedBase={Boolean(localBaseFrame)}
          bakedMarksLegacyHint={!localBaseFrame}
          showViolationTime={false}
          onClose={() => setEditImageOpen(false)}
          onApply={handleImageEditApply}
          primaryButtonLabel="Save image"
          headerTitle="Edit marks on image"
          ariaLabel="Edit marks on snap image"
        />
      ) : null}

      {submitConfirmOpen ? (
        <div
          className="snap-confirm-overlay"
          role="presentation"
          onClick={submitting ? undefined : cancelSubmitConfirm}
        >
          <div
            className="snap-confirm-dialog"
            role="dialog"
            aria-modal="true"
            aria-labelledby="snap-confirm-submit-title"
            onClick={(e) => e.stopPropagation()}
          >
            <h2 id="snap-confirm-submit-title" className="snap-confirm-title">
              Submit to database?
            </h2>
            <p className="snap-confirm-body">
              Submit {submitConfirmCount} observation{submitConfirmCount !== 1 ? 's' : ''} to the database? They will be
              saved permanently on this assessment.
            </p>
            <div className="snap-confirm-actions">
              <button
                type="button"
                className="snap-confirm-btn snap-confirm-btn--secondary"
                onClick={cancelSubmitConfirm}
                disabled={submitting}
              >
                Cancel
              </button>
              <button
                type="button"
                className="snap-confirm-btn snap-confirm-btn--primary"
                onClick={() => void executeSubmitObservations()}
                disabled={submitting}
              >
                {submitting ? 'Saving…' : 'Submit'}
              </button>
            </div>
          </div>
        </div>
      ) : null}

      {leaveConfirmOpen ? (
        <div
          className="snap-confirm-overlay"
          role="presentation"
          onClick={cancelLeaveConfirm}
        >
          <div
            className="snap-confirm-dialog"
            role="dialog"
            aria-modal="true"
            aria-labelledby="snap-confirm-leave-title"
            onClick={(e) => e.stopPropagation()}
          >
            <h2 id="snap-confirm-leave-title" className="snap-confirm-title">
              Leave without submitting?
            </h2>
            <p className="snap-confirm-body">
              Any unsaved answers will be lost. Use <strong>Submit observations</strong> to save them to the database
              first.
            </p>
            <div className="snap-confirm-actions">
              <button
                type="button"
                className="snap-confirm-btn snap-confirm-btn--secondary"
                onClick={cancelLeaveConfirm}
              >
                Stay on page
              </button>
              <button
                type="button"
                className="snap-confirm-btn snap-confirm-btn--danger"
                onClick={confirmLeaveWithoutSubmit}
              >
                Leave without saving
              </button>
            </div>
          </div>
        </div>
      ) : null}
    </main>
  );
}
