const state = {
  assessments: [],
  drafts: [],
  invalidRecords: [],
  packFiles: [],
  comparison: null,
  findings: null,
  findingsFilter: "all",
  workflowDashboard: null,
  workflowActivity: null,
  assessmentComparisonCatalogue: null,
  assessmentVersionComparison: null,
  dueDatePlanner: null,
  backupPreview: null,
  backupRestoreFile: null,
  backupRestoreConfirmation: "",
  backupRestoreConfirmationResetReason: "",
  backupRestoreCompleted: false,
  selectedBackupRestoreStrategy: "",
  selectedWorkflowRows: new Set(),
  workflowFilters: {
    search: "",
    versionScope: "latest",
    status: "all",
    reviewIndication: "all",
    owner: "all",
    priority: "all",
    overdue: false,
  },
  workflowActivityFilters: {
    search: "",
    scope: "latest",
    company: "all",
    operation: "all",
    dateFrom: "",
    dateTo: "",
  },
  assessmentComparisonFilters: {
    company: "",
    previousKey: "",
    currentKey: "",
    view: "changed",
    section: "all",
    transition: "all",
  },
  dueDateFilters: {
    search: "",
    versionScope: "latest",
    company: "all",
    owner: "all",
    itemType: "all",
    status: "all",
    bucket: "all",
    includeComplete: false,
  },
  selectedPath: null,
  selectedDraftId: null,
  search: "",
  catalogueMode: "active",
  formMode: "new",
  reviewPath: null,
  draftId: null,
  pendingKeys: [],
  selectedAssessmentSection: "overview",
};

const elements = {
  packCount: document.querySelector("#pack-count"),
  validCount: document.querySelector("#valid-count"),
  attentionCount: document.querySelector("#attention-count"),
  resultCount: document.querySelector("#result-count"),
  assessmentList: document.querySelector("#assessment-list"),
  attentionPanel: document.querySelector("#attention-panel"),
  attentionList: document.querySelector("#attention-list"),
  emptyState: document.querySelector("#empty-state"),
  emptyStateEyebrow: document.querySelector("#empty-state-eyebrow"),
  emptyStateIntro: document.querySelector("#empty-state-intro"),
  selectedEmptyStateGrid: document.querySelector("#selected-empty-state-grid"),
  firstRunEmptyState: document.querySelector("#first-run-empty-state"),
  emptyStateActions: Array.from(
    document.querySelectorAll("[data-empty-state-action]")
  ),
  detail: document.querySelector("#assessment-detail"),
  draftDetail: document.querySelector("#draft-detail"),
  versionBadge: document.querySelector("#version-badge"),
  assessmentDetailTabs: Array.from(
    document.querySelectorAll("[data-assessment-section]")
  ),
  assessmentDetailPanels: Array.from(
    document.querySelectorAll("[data-assessment-section-panel]")
  ),
  assessmentSectionJumpButtons: Array.from(
    document.querySelectorAll("[data-assessment-section-jump]")
  ),
  notice: document.querySelector("#notice"),
  catalogueEyebrow: document.querySelector("#catalogue-eyebrow"),
  catalogueTitle: document.querySelector("#catalogue-title"),
  catalogueIntro: document.querySelector("#catalogue-intro"),
  refreshButton: document.querySelector("#refresh-button"),
  backupButton: document.querySelector("#backup-button"),
  restorePreviewViewButton: document.querySelector("#restore-preview-view-button"),
  operatorGuidesViewButton: document.querySelector("#operator-guides-view-button"),
  productReadinessViewButton: document.querySelector("#product-readiness-view-button"),
  localDataViewButton: document.querySelector("#local-data-view-button"),
  operatorChecklistViewButton: document.querySelector("#operator-checklist-view-button"),
  backupRestorePreview: document.querySelector("#backup-restore-preview"),
  operatorGuidesPanel: document.querySelector("#operator-guides-panel"),
  productReadinessPanel: document.querySelector("#product-readiness-panel"),
  localDataPanel: document.querySelector("#local-data-panel"),
  operatorChecklistPanel: document.querySelector("#operator-checklist-panel"),
  backupRestoreFile: document.querySelector("#backup-restore-file"),
  backupRestoreReportButton: document.querySelector("#backup-restore-report-button"),
  backupRestoreClearButton: document.querySelector("#backup-restore-clear-button"),
  backupRestoreExecution: document.querySelector("#backup-restore-execution"),
  backupRestoreSelectedFile: document.querySelector("#backup-restore-selected-file"),
  backupRestoreSelectedStrategy: document.querySelector("#backup-restore-selected-strategy"),
  backupRestoreRequiredPhrase: document.querySelector("#backup-restore-required-phrase"),
  backupRestoreConfirmation: document.querySelector("#backup-restore-confirmation"),
  backupRestoreExecuteButton: document.querySelector("#backup-restore-execute-button"),
  backupRestoreExecutionStatus: document.querySelector("#backup-restore-execution-status"),
  backupRestoreSummary: document.querySelector("#backup-restore-summary"),
  backupRestoreContent: document.querySelector("#backup-restore-content"),
  assessmentLibraryButton: document.querySelector("#assessment-library-button"),
  assessmentWorkspace: document.querySelector("#assessment-workspace"),
  workflowDashboard: document.querySelector("#workflow-dashboard"),
  workflowDashboardSummary: document.querySelector("#workflow-dashboard-summary"),
  workflowDashboardContent: document.querySelector("#workflow-dashboard-content"),
  workflowResultCount: document.querySelector("#workflow-result-count"),
  workflowSearch: document.querySelector("#workflow-search"),
  workflowVersionFilter: document.querySelector("#workflow-version-filter"),
  workflowStatusFilter: document.querySelector("#workflow-status-filter"),
  workflowReviewIndicationFilter: document.querySelector(
    "#workflow-review-indication-filter"
  ),
  workflowOwnerFilter: document.querySelector("#workflow-owner-filter"),
  workflowPriorityFilter: document.querySelector("#workflow-priority-filter"),
  workflowOverdueFilter: document.querySelector("#workflow-overdue-filter"),
  workflowSelectVisible: document.querySelector("#workflow-select-visible"),
  workflowSelectedCount: document.querySelector("#workflow-selected-count"),
  workflowBulkOwner: document.querySelector("#workflow-bulk-owner"),
  workflowAssignOwnerButton: document.querySelector("#workflow-assign-owner-button"),
  workflowBulkStatus: document.querySelector("#workflow-bulk-status"),
  workflowAssignStatusButton: document.querySelector("#workflow-assign-status-button"),
  workflowBulkTargetDate: document.querySelector("#workflow-bulk-target-date"),
  workflowAssignTargetDateButton: document.querySelector("#workflow-assign-target-date-button"),
  workflowBulkNote: document.querySelector("#workflow-bulk-note"),
  workflowAppendNoteButton: document.querySelector("#workflow-append-note-button"),
  workflowExportButton: document.querySelector("#workflow-export-button"),
  workflowViewButton: document.querySelector("#workflow-view-button"),
  workflowActivityRegister: document.querySelector(
    "#workflow-activity-register"
  ),
  workflowActivitySummary: document.querySelector("#workflow-activity-summary"),
  workflowActivityContent: document.querySelector("#workflow-activity-content"),
  workflowActivityResultCount: document.querySelector(
    "#workflow-activity-result-count"
  ),
  workflowActivitySearch: document.querySelector("#workflow-activity-search"),
  workflowActivityScopeFilter: document.querySelector(
    "#workflow-activity-scope-filter"
  ),
  workflowActivityCompanyFilter: document.querySelector(
    "#workflow-activity-company-filter"
  ),
  workflowActivityOperationFilter: document.querySelector(
    "#workflow-activity-operation-filter"
  ),
  workflowActivityDateFrom: document.querySelector(
    "#workflow-activity-date-from"
  ),
  workflowActivityDateTo: document.querySelector("#workflow-activity-date-to"),
  workflowActivityExportButton: document.querySelector(
    "#workflow-activity-export-button"
  ),
  activityViewButton: document.querySelector("#activity-view-button"),
  assessmentVersionComparison: document.querySelector(
    "#assessment-version-comparison"
  ),
  assessmentComparisonSummary: document.querySelector(
    "#assessment-comparison-summary"
  ),
  assessmentComparisonCompany: document.querySelector(
    "#assessment-comparison-company"
  ),
  assessmentComparisonPrevious: document.querySelector(
    "#assessment-comparison-previous"
  ),
  assessmentComparisonCurrent: document.querySelector(
    "#assessment-comparison-current"
  ),
  assessmentComparisonViewFilter: document.querySelector(
    "#assessment-comparison-view-filter"
  ),
  assessmentComparisonSectionFilter: document.querySelector(
    "#assessment-comparison-section-filter"
  ),
  assessmentComparisonTransitionFilter: document.querySelector(
    "#assessment-comparison-transition-filter"
  ),
  assessmentComparisonContext: document.querySelector(
    "#assessment-comparison-context"
  ),
  assessmentComparisonResultCount: document.querySelector(
    "#assessment-comparison-result-count"
  ),
  assessmentComparisonExportButton: document.querySelector(
    "#assessment-comparison-export-button"
  ),
  assessmentComparisonContent: document.querySelector(
    "#assessment-comparison-content"
  ),
  comparisonViewButton: document.querySelector("#comparison-view-button"),
  dueDatePlanner: document.querySelector("#due-date-planner"),
  dueDateSummary: document.querySelector("#due-date-summary"),
  dueDateSearch: document.querySelector("#due-date-search"),
  dueDateVersionFilter: document.querySelector("#due-date-version-filter"),
  dueDateCompanyFilter: document.querySelector("#due-date-company-filter"),
  dueDateOwnerFilter: document.querySelector("#due-date-owner-filter"),
  dueDateTypeFilter: document.querySelector("#due-date-type-filter"),
  dueDateStatusFilter: document.querySelector("#due-date-status-filter"),
  dueDateBucketFilter: document.querySelector("#due-date-bucket-filter"),
  dueDateCompleteFilter: document.querySelector("#due-date-complete-filter"),
  dueDateContext: document.querySelector("#due-date-context"),
  dueDateResultCount: document.querySelector("#due-date-result-count"),
  dueDateExportButton: document.querySelector("#due-date-export-button"),
  dueDateContent: document.querySelector("#due-date-content"),
  dueViewButton: document.querySelector("#due-view-button"),
  summaryStrip: document.querySelector("#summary-strip"),
  searchInput: document.querySelector("#search-input"),
  generateButton: document.querySelector("#generate-button"),
  reviewButton: document.querySelector("#review-button"),
  pendingReviewButton: document.querySelector("#pending-review-button"),
  comparisonSection: document.querySelector("#comparison-section"),
  comparisonSummary: document.querySelector("#comparison-summary"),
  comparisonCount: document.querySelector("#comparison-count"),
  comparisonContent: document.querySelector("#comparison-content"),
  findingsSummary: document.querySelector("#findings-summary"),
  workflowSummary: document.querySelector("#workflow-summary"),
  findingsContent: document.querySelector("#findings-content"),
  findingsFilters: Array.from(document.querySelectorAll(".finding-filter")),
  copyButton: document.querySelector("#copy-button"),
  archiveButton: document.querySelector("#archive-button"),
  restoreButton: document.querySelector("#restore-button"),
  deleteButton: document.querySelector("#delete-button"),
  createPackActionState: document.querySelector("#create-pack-action-state"),
  localRecordActionState: document.querySelector("#local-record-action-state"),
  packFileCount: document.querySelector("#pack-file-count"),
  packFilesList: document.querySelector("#pack-files-list"),
  openPackFolderButton: document.querySelector("#open-pack-folder-button"),
  archiveViewButton: document.querySelector("#archive-view-button"),
  draftViewButton: document.querySelector("#draft-view-button"),
  newAssessmentButton: document.querySelector("#new-assessment-button"),
  assessmentDialog: document.querySelector("#assessment-dialog"),
  libraryPage: document.querySelector("#library-page"),
  assessmentForm: document.querySelector("#assessment-form"),
  identityFields: document.querySelector("#identity-fields"),
  formEyebrow: document.querySelector("#assessment-form-eyebrow"),
  formTitle: document.querySelector("#new-assessment-title"),
  formIntro: document.querySelector("#assessment-form-intro"),
  formContextScope: document.querySelector("#assessment-form-context-scope"),
  formContextSave: document.querySelector("#assessment-form-context-save"),
  formContextOutput: document.querySelector("#assessment-form-context-output"),
  workflowCarryForwardOption: document.querySelector(
    "#workflow-carry-forward-option"
  ),
  workflowCarryForward: document.querySelector("#workflow-carry-forward"),
  questionnaireSections: document.querySelector("#questionnaire-sections"),
  questionProgress: document.querySelector("#question-progress"),
  formError: document.querySelector("#form-error"),
  createAssessmentButton: document.querySelector("#create-assessment-button"),
  saveDraftButton: document.querySelector("#save-draft-button"),
  resumeDraftButton: document.querySelector("#resume-draft-button"),
  deleteDraftButton: document.querySelector("#delete-draft-button"),
};

let questionnaireDefinition = null;

function isArchivedMode() {
  return state.catalogueMode === "archived";
}

function isDraftMode() {
  return state.catalogueMode === "drafts";
}

function isWorkflowMode() {
  return state.catalogueMode === "workflow";
}

function isActivityMode() {
  return state.catalogueMode === "activity";
}

function isComparisonMode() {
  return state.catalogueMode === "comparison";
}

function isDueDateMode() {
  return state.catalogueMode === "due";
}

function isBackupPreviewMode() {
  return state.catalogueMode === "backup-preview";
}

function isProductReadinessMode() {
  return state.catalogueMode === "product-readiness";
}

function isLocalDataMode() {
  return state.catalogueMode === "local-data";
}

function isOperatorChecklistMode() {
  return state.catalogueMode === "operator-checklist";
}

function setText(selector, value) {
  document.querySelector(selector).textContent = value ?? "Not recorded";
}

function showNotice(message, isError = false) {
  elements.notice.replaceChildren();
  elements.notice.textContent = message;
  elements.notice.classList.toggle("error", isError);
  elements.notice.classList.remove("generation-success-notice");
  elements.notice.hidden = false;
}

function clearNotice() {
  elements.notice.hidden = true;
  elements.notice.replaceChildren();
  elements.notice.textContent = "";
  elements.notice.classList.remove("error", "generation-success-notice");
}

function showGenerationSuccessNotice(result, workflowMessage = "") {
  elements.notice.replaceChildren();
  elements.notice.classList.remove("error");
  elements.notice.classList.add("generation-success-notice");
  elements.notice.hidden = false;

  const shell = document.createElement("div");
  shell.className = "generation-success-layout";

  const heading = document.createElement("div");
  heading.className = "generation-success-heading";
  const eyebrow = document.createElement("span");
  eyebrow.textContent = "Pack generated";
  const title = document.createElement("strong");
  title.textContent = `${result.company_name || "Assessment"} pack is ready`;
  const summary = document.createElement("p");
  summary.textContent = `Saved in ${result.output_folder}. Open Files, start with START_HERE.txt, then review the Document Pack Summary, Implementation Action Plan, and Evidence Register.`;

  const meta = document.createElement("div");
  meta.className = "generation-success-meta";
  [
    ["Output folder", result.output_folder],
    ["Files created", `${result.file_count}`],
  ].forEach(([label, value]) => {
    const item = document.createElement("span");
    const name = document.createElement("small");
    name.textContent = label;
    const detail = document.createElement("strong");
    detail.textContent = value;
    item.append(name, detail);
    meta.append(item);
  });
  heading.append(eyebrow, title, summary, meta);

  const steps = document.createElement("div");
  steps.className = "generation-success-steps";
  [
    [
      "1",
      "Open Files",
      "Use the selected pack's Files tab to open the generated outputs.",
      true,
    ],
    [
      "2",
      "Start with START_HERE.txt",
      "Follow the pack front door before treating any output as approved.",
      false,
    ],
    [
      "3",
      "Review the working files",
      "Read the Document Pack Summary, Implementation Action Plan, and Evidence Register before workflow updates.",
      false,
    ],
    [
      "4",
      "Back up before changes",
      "Use Backup data before moving, restoring, deleting, or making major local-data changes.",
      false,
    ],
  ].forEach(([number, label, text, isPrimary]) => {
    const item = document.createElement("div");
    item.className = "generation-success-step";
    item.classList.toggle("generation-success-step-primary", isPrimary);
    const marker = document.createElement("span");
    marker.textContent = number;
    const content = document.createElement("p");
    const strong = document.createElement("strong");
    strong.textContent = label;
    const description = document.createElement("small");
    description.textContent = text;
    content.append(strong, description);
    item.append(marker, content);
    steps.append(item);
  });

  const actions = document.createElement("div");
  actions.className = "generation-success-actions";
  const actionHint = document.createElement("span");
  actionHint.className = "generation-success-action-hint";
  actionHint.textContent = "Next best actions";
  const filesButton = document.createElement("button");
  filesButton.type = "button";
  filesButton.className = "primary-button generation-success-primary-action";
  filesButton.textContent = "Go to Files";
  filesButton.addEventListener("click", () => openAssessmentSection("files"));
  const backupButton = document.createElement("button");
  backupButton.type = "button";
  backupButton.className = "secondary-button generation-success-secondary-action";
  backupButton.textContent = "Backup data";
  backupButton.addEventListener("click", downloadPortableBackup);
  actions.append(actionHint, filesButton, backupButton);

  shell.append(heading, steps, actions);
  if (workflowMessage) {
    const workflow = document.createElement("p");
    workflow.className = "generation-success-workflow";
    workflow.textContent = workflowMessage;
    shell.append(workflow);
  }
  elements.notice.append(shell);
}

function currentAssessment() {
  return state.assessments.find(item => item.answers_path === state.selectedPath) || null;
}

function currentDraft() {
  return state.drafts.find(item => item.draft_id === state.selectedDraftId) || null;
}

function assessmentDetailSectionsAvailable() {
  return {
    overview: true,
    findings: true,
    changes: !isArchivedMode(),
    files: true,
    actions: true,
  };
}

function renderAssessmentDetailSections() {
  const availableSections = assessmentDetailSectionsAvailable();
  if (!availableSections[state.selectedAssessmentSection]) {
    state.selectedAssessmentSection = "overview";
  }
  elements.assessmentDetailTabs.forEach(button => {
    const section = button.dataset.assessmentSection;
    const selected = section === state.selectedAssessmentSection;
    button.hidden = !availableSections[section];
    button.classList.toggle("selected", selected);
    button.setAttribute("aria-selected", String(selected));
    button.tabIndex = selected ? 0 : -1;
  });
  elements.assessmentDetailPanels.forEach(panel => {
    panel.hidden = panel.dataset.assessmentSectionPanel !== state.selectedAssessmentSection;
  });
}

function openAssessmentSection(section) {
  const availableSections = assessmentDetailSectionsAvailable();
  if (!availableSections[section]) return;

  state.selectedAssessmentSection = section;
  renderAssessmentDetailSections();
  elements.detail.scrollIntoView({ behavior: "smooth", block: "start" });
}

function filteredAssessments() {
  const query = state.search.trim().toLocaleLowerCase();
  if (!query) return state.assessments;
  return state.assessments.filter(item =>
    item.company_name.toLocaleLowerCase().includes(query)
    || item.folder_name.toLocaleLowerCase().includes(query)
    || item.document_owner.toLocaleLowerCase().includes(query)
  );
}

function filteredDrafts() {
  const query = state.search.trim().toLocaleLowerCase();
  if (!query) return state.drafts;
  return state.drafts.filter(draft =>
    draft.company_name.toLocaleLowerCase().includes(query)
    || draft.document_owner.toLocaleLowerCase().includes(query)
  );
}

function versionScopedWorkflowRows() {
  const rows = state.workflowDashboard?.rows || [];
  return state.workflowFilters.versionScope === "all"
    ? rows
    : rows.filter(row => row.is_latest);
}

function filteredWorkflowRows() {
  const filters = state.workflowFilters;
  const query = filters.search.trim().toLocaleLowerCase();
  return versionScopedWorkflowRows().filter(row => {
    const searchable = [
      row.company_name,
      row.folder_name,
      row.area,
      row.linked_action,
      row.priority,
      row.workflow.owner,
      row.workflow.status,
      row.workflow.target_date,
    ].join(" ").toLocaleLowerCase();
    return (
      (!query || searchable.includes(query))
      && (filters.status === "all" || row.workflow.status === filters.status)
      && (
        filters.reviewIndication === "all"
        || row.review_indication === filters.reviewIndication
      )
      && (filters.owner === "all" || (
        filters.owner === "__unassigned__"
          ? row.is_unassigned
          : row.workflow.owner === filters.owner
      ))
      && (filters.priority === "all" || row.priority === filters.priority)
      && (!filters.overdue || row.is_overdue)
    );
  });
}

function workflowRowKey(row) {
  return JSON.stringify([row.answers_path, row.key]);
}

function renderWorkflowSelection(rows) {
  const visibleKeys = rows.map(workflowRowKey);
  const selectedVisible = visibleKeys.filter(
    key => state.selectedWorkflowRows.has(key)
  ).length;
  elements.workflowSelectVisible.checked = (
    visibleKeys.length > 0
    && selectedVisible === visibleKeys.length
  );
  elements.workflowSelectVisible.indeterminate = (
    selectedVisible > 0
    && selectedVisible < visibleKeys.length
  );
  elements.workflowSelectedCount.textContent = (
    `${state.selectedWorkflowRows.size} selected`
  );
  elements.workflowAssignOwnerButton.disabled = (
    state.selectedWorkflowRows.size === 0
    || !elements.workflowBulkOwner.value.trim()
  );
  elements.workflowAssignStatusButton.disabled = (
    state.selectedWorkflowRows.size === 0
    || !elements.workflowBulkStatus.value
  );
  elements.workflowAssignTargetDateButton.disabled = (
    state.selectedWorkflowRows.size === 0
  );
  elements.workflowAppendNoteButton.disabled = (
    state.selectedWorkflowRows.size === 0
    || !elements.workflowBulkNote.value.trim()
  );
}

function populateWorkflowFilters() {
  const dashboard = state.workflowDashboard;
  const owners = Array.from(new Set(
    dashboard.rows.map(row => row.workflow.owner).filter(Boolean)
  )).sort((left, right) => left.localeCompare(right));
  const priorities = Array.from(new Set(
    dashboard.rows.map(row => row.priority).filter(Boolean)
  )).sort((left, right) => left.localeCompare(right));

  const currentBulkStatus = elements.workflowBulkStatus.value;
  elements.workflowStatusFilter.replaceChildren(new Option("All statuses", "all"));
  elements.workflowBulkStatus.replaceChildren(
    new Option("Status for selected findings", "")
  );
  dashboard.workflow_statuses.forEach(status => {
    elements.workflowStatusFilter.append(new Option(status, status));
    elements.workflowBulkStatus.append(new Option(status, status));
  });
  elements.workflowBulkStatus.value = dashboard.workflow_statuses.includes(
    currentBulkStatus
  ) ? currentBulkStatus : "";
  elements.workflowReviewIndicationFilter.replaceChildren(
    new Option("All review indications", "all")
  );
  dashboard.review_indications.forEach(indication => {
    elements.workflowReviewIndicationFilter.append(
      new Option(indication, indication)
    );
  });
  elements.workflowOwnerFilter.replaceChildren(
    new Option("All owners", "all"),
    new Option("Unassigned", "__unassigned__")
  );
  owners.forEach(owner => {
    elements.workflowOwnerFilter.append(new Option(owner, owner));
  });
  elements.workflowPriorityFilter.replaceChildren(new Option("All priorities", "all"));
  priorities.forEach(priority => {
    elements.workflowPriorityFilter.append(new Option(priority, priority));
  });
  elements.workflowStatusFilter.value = state.workflowFilters.status;
  elements.workflowReviewIndicationFilter.value = (
    state.workflowFilters.reviewIndication
  );
  elements.workflowOwnerFilter.value = state.workflowFilters.owner;
  elements.workflowPriorityFilter.value = state.workflowFilters.priority;
  elements.workflowVersionFilter.value = state.workflowFilters.versionScope;
}

function renderWorkflowDashboard(message = null) {
  elements.workflowDashboardContent.replaceChildren();
  elements.workflowDashboardSummary.replaceChildren();
  const dashboard = state.workflowDashboard;
  if (message || !dashboard) {
    elements.workflowResultCount.textContent = "";
    elements.workflowExportButton.disabled = true;
    const empty = document.createElement("div");
    empty.className = "workflow-dashboard-message";
    empty.textContent = message || "No workflow data is available.";
    elements.workflowDashboardContent.append(empty);
    return;
  }

  const scopedRows = versionScopedWorkflowRows();
  [
    ["Overdue", scopedRows.filter(row => row.is_overdue).length],
    ["Blocked", scopedRows.filter(
      row => row.workflow.status === "Blocked"
    ).length],
    ["In progress", scopedRows.filter(
      row => row.workflow.status === "In progress"
    ).length],
    ["Unassigned", scopedRows.filter(row => row.is_unassigned).length],
    ["Recently updated", scopedRows.filter(
      row => row.is_recently_updated
    ).length],
  ].forEach(([label, value]) => {
    const item = document.createElement("div");
    const name = document.createElement("span");
    name.textContent = label;
    const count = document.createElement("strong");
    count.textContent = value;
    item.append(name, count);
    elements.workflowDashboardSummary.append(item);
  });

  const rows = filteredWorkflowRows();
  renderWorkflowSelection(rows);
  elements.workflowResultCount.textContent = (
    `${rows.length} of ${scopedRows.length} findings shown`
  );
  elements.workflowExportButton.disabled = rows.length === 0;
  if (!rows.length) {
    const empty = document.createElement("div");
    empty.className = "workflow-dashboard-message";
    empty.textContent = "No active findings match these filters.";
    elements.workflowDashboardContent.append(empty);
    return;
  }

  const table = document.createElement("table");
  table.className = "workflow-dashboard-table";
  const header = document.createElement("thead");
  header.innerHTML = "<tr><th class=\"workflow-row-select\"><span class=\"sr-only\">Select</span></th><th>Company / pack</th><th>Finding</th><th>Action</th><th>Status</th><th>Owner</th><th>Target</th><th>Updated</th><th></th></tr>";
  const body = document.createElement("tbody");
  rows.forEach(row => {
    const tableRow = document.createElement("tr");
    tableRow.classList.toggle("workflow-row-overdue", row.is_overdue);
    tableRow.classList.toggle("workflow-row-unassigned", row.is_unassigned);
    const selectCell = document.createElement("td");
    selectCell.className = "workflow-row-select";
    const select = document.createElement("input");
    select.type = "checkbox";
    select.setAttribute(
      "aria-label",
      `Select ${row.area} for ${row.company_name}`
    );
    select.checked = state.selectedWorkflowRows.has(workflowRowKey(row));
    select.addEventListener("change", () => {
      const key = workflowRowKey(row);
      if (select.checked) {
        state.selectedWorkflowRows.add(key);
      } else {
        state.selectedWorkflowRows.delete(key);
      }
      renderWorkflowSelection(filteredWorkflowRows());
    });
    selectCell.append(select);
    const company = document.createElement("td");
    company.className = "workflow-company-cell";
    company.dataset.label = "Company / pack";
    const companyName = document.createElement("strong");
    companyName.textContent = row.company_name;
    const version = document.createElement("span");
    version.className = "workflow-row-meta";
    version.textContent = `${row.folder_name} · Version ${row.history_version} of ${row.history_count}`;
    company.append(companyName, version);

    const finding = document.createElement("td");
    finding.className = "workflow-finding-cell";
    finding.dataset.label = "Finding";
    const findingName = document.createElement("strong");
    findingName.textContent = row.area;
    const indication = document.createElement("span");
    indication.className = "workflow-row-meta";
    indication.textContent = row.review_indication;
    finding.append(findingName, indication);

    const action = document.createElement("td");
    action.className = "workflow-action-cell";
    action.dataset.label = "Action";
    const actionRef = document.createElement("strong");
    actionRef.textContent = row.linked_action;
    const priority = document.createElement("span");
    priority.className = "workflow-priority-chip";
    priority.textContent = `${row.priority} priority`;
    action.append(actionRef, priority);

    const status = document.createElement("td");
    status.className = "workflow-status-cell";
    status.dataset.label = "Status";
    const statusBadge = document.createElement("span");
    statusBadge.className = `workflow-status ${workflowStatusClass(row.workflow.status)}`;
    statusBadge.textContent = row.workflow.status;
    status.append(statusBadge);

    const owner = document.createElement("td");
    owner.className = "workflow-owner-cell";
    owner.dataset.label = "Owner";
    owner.textContent = row.workflow.owner || "Unassigned";
    owner.classList.toggle("workflow-muted-cell", row.is_unassigned);
    const target = document.createElement("td");
    target.className = "workflow-target-cell";
    target.dataset.label = "Target";
    target.textContent = row.workflow.target_date || "No date";
    target.classList.toggle("overdue-date", row.is_overdue);
    const updated = document.createElement("td");
    updated.className = "workflow-updated-cell";
    updated.dataset.label = "Updated";
    updated.textContent = row.workflow.updated_at
      ? new Date(row.workflow.updated_at).toLocaleDateString()
      : "Never";

    const open = document.createElement("td");
    open.className = "workflow-row-actions";
    const openButton = document.createElement("button");
    openButton.type = "button";
    openButton.className = "secondary-button workflow-open-button";
    openButton.textContent = "Open finding";
    openButton.addEventListener("click", () => openWorkflowFinding(row));
    open.append(openButton);
    tableRow.append(
      selectCell,
      company,
      finding,
      action,
      status,
      owner,
      target,
      updated,
      open
    );
    body.append(tableRow);
  });
  table.append(header, body);
  elements.workflowDashboardContent.append(table);
}

async function assignSelectedWorkflowOwner() {
  const owner = elements.workflowBulkOwner.value.trim();
  if (!owner || !state.selectedWorkflowRows.size) return;

  const rowsByKey = new Map(
    state.workflowDashboard.rows.map(row => [workflowRowKey(row), row])
  );
  const selections = Array.from(state.selectedWorkflowRows).map(key => {
    const row = rowsByKey.get(key);
    return {
      answers_path: row.answers_path,
      finding_key: row.key,
    };
  });
  elements.workflowAssignOwnerButton.disabled = true;
  elements.workflowAssignOwnerButton.textContent = "Assigning...";
  clearNotice();
  try {
    const response = await fetch("/api/workflow-dashboard/assign-owner", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ owner, selections }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "The selected findings could not be assigned."
    );
    state.workflowDashboard = payload.dashboard;
    state.invalidRecords = [
      ...payload.dashboard.invalid_records,
      ...payload.dashboard.invalid_workflows.map(record => ({
        folder_name: record.folder_name,
        error: record.error,
      })),
    ];
    state.selectedWorkflowRows.clear();
    elements.workflowBulkOwner.value = "";
    populateWorkflowFilters();
    renderWorkflowDashboard();
    renderAttentionRecords();
    showNotice(
      `Assigned ${payload.updated_count} findings to ${payload.owner}.`
    );
  } catch (error) {
    showNotice(error.message, true);
    renderWorkflowSelection(filteredWorkflowRows());
  } finally {
    elements.workflowAssignOwnerButton.textContent = "Assign owner";
  }
}

async function assignSelectedWorkflowStatus() {
  const status = elements.workflowBulkStatus.value;
  if (!status || !state.selectedWorkflowRows.size) return;

  const rowsByKey = new Map(
    state.workflowDashboard.rows.map(row => [workflowRowKey(row), row])
  );
  const selections = Array.from(state.selectedWorkflowRows).map(key => {
    const row = rowsByKey.get(key);
    return {
      answers_path: row.answers_path,
      finding_key: row.key,
    };
  });
  elements.workflowAssignStatusButton.disabled = true;
  elements.workflowAssignStatusButton.textContent = "Setting...";
  clearNotice();
  try {
    const response = await fetch("/api/workflow-dashboard/assign-status", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ status, selections }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "The selected finding statuses could not be updated."
    );
    state.workflowDashboard = payload.dashboard;
    state.invalidRecords = [
      ...payload.dashboard.invalid_records,
      ...payload.dashboard.invalid_workflows.map(record => ({
        folder_name: record.folder_name,
        error: record.error,
      })),
    ];
    state.selectedWorkflowRows.clear();
    elements.workflowBulkStatus.value = "";
    populateWorkflowFilters();
    renderWorkflowDashboard();
    renderAttentionRecords();
    showNotice(
      `Set ${payload.updated_count} findings to ${payload.status}.`
    );
  } catch (error) {
    showNotice(error.message, true);
    renderWorkflowSelection(filteredWorkflowRows());
  } finally {
    elements.workflowAssignStatusButton.textContent = "Set status";
  }
}


async function assignSelectedWorkflowTargetDate() {
  if (!state.selectedWorkflowRows.size) return;

  const targetDate = elements.workflowBulkTargetDate.value.trim();
  const rowsByKey = new Map(
    state.workflowDashboard.rows.map(row => [workflowRowKey(row), row])
  );
  const selections = Array.from(state.selectedWorkflowRows).map(key => {
    const row = rowsByKey.get(key);
    return {
      answers_path: row.answers_path,
      finding_key: row.key,
    };
  });
  elements.workflowAssignTargetDateButton.disabled = true;
  elements.workflowAssignTargetDateButton.textContent = "Setting...";
  clearNotice();
  try {
    const response = await fetch("/api/workflow-dashboard/assign-target-date", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ target_date: targetDate, selections }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "The selected finding target dates could not be updated."
    );
    state.workflowDashboard = payload.dashboard;
    state.invalidRecords = [
      ...payload.dashboard.invalid_records,
      ...payload.dashboard.invalid_workflows.map(record => ({
        folder_name: record.folder_name,
        error: record.error,
      })),
    ];
    state.selectedWorkflowRows.clear();
    elements.workflowBulkTargetDate.value = "";
    populateWorkflowFilters();
    renderWorkflowDashboard();
    renderAttentionRecords();
    showNotice(
      payload.target_date
        ? `Set ${payload.updated_count} finding target dates to ${payload.target_date}.`
        : `Cleared target dates for ${payload.updated_count} findings.`
    );
  } catch (error) {
    showNotice(error.message, true);
    renderWorkflowSelection(filteredWorkflowRows());
  } finally {
    elements.workflowAssignTargetDateButton.textContent = "Set target date";
  }
}


async function appendSelectedWorkflowNote() {
  const note = elements.workflowBulkNote.value.trim();
  if (!note || !state.selectedWorkflowRows.size) return;

  const rowsByKey = new Map(
    state.workflowDashboard.rows.map(row => [workflowRowKey(row), row])
  );
  const selections = Array.from(state.selectedWorkflowRows).map(key => {
    const row = rowsByKey.get(key);
    return {
      answers_path: row.answers_path,
      finding_key: row.key,
    };
  });
  elements.workflowAppendNoteButton.disabled = true;
  elements.workflowAppendNoteButton.textContent = "Adding...";
  clearNotice();
  try {
    const response = await fetch("/api/workflow-dashboard/append-note", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ note, selections }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "The selected finding notes could not be updated."
    );
    state.workflowDashboard = payload.dashboard;
    state.invalidRecords = [
      ...payload.dashboard.invalid_records,
      ...payload.dashboard.invalid_workflows.map(record => ({
        folder_name: record.folder_name,
        error: record.error,
      })),
    ];
    state.selectedWorkflowRows.clear();
    elements.workflowBulkNote.value = "";
    populateWorkflowFilters();
    renderWorkflowDashboard();
    renderAttentionRecords();
    showNotice(`Added note to ${payload.updated_count} workflow findings.`);
  } catch (error) {
    showNotice(error.message, true);
    renderWorkflowSelection(filteredWorkflowRows());
  } finally {
    elements.workflowAppendNoteButton.textContent = "Add note";
  }
}


function buildWorkflowDashboardExportUrl() {
  const query = new URLSearchParams({
    search: state.workflowFilters.search,
    status: state.workflowFilters.status,
    review_indication: state.workflowFilters.reviewIndication,
    version_scope: state.workflowFilters.versionScope,
    owner: state.workflowFilters.owner,
    priority: state.workflowFilters.priority,
    overdue: String(state.workflowFilters.overdue),
  });
  return `/api/workflow-dashboard/export?${query.toString()}`;
}

function exportWorkflowDashboard() {
  const rows = filteredWorkflowRows();
  if (!state.workflowDashboard || !rows.length) return;

  window.location.href = buildWorkflowDashboardExportUrl();
  showNotice(`Exporting ${rows.length} workflow findings to CSV.`);
}


async function openWorkflowFinding(row) {
  state.catalogueMode = "active";
  state.selectedPath = row.answers_path;
  state.selectedAssessmentSection = "findings";
  renderCatalogueMode();
  await loadCatalogue();
  await loadSelectedAssessmentFindings(row.answers_path, false);
  const finding = document.querySelector(`[data-finding-key="${row.key}"]`);
  if (finding) {
    finding.scrollIntoView({ behavior: "smooth", block: "center" });
    finding.querySelector(".workflow-editor")?.setAttribute("open", "");
  }
}

async function loadWorkflowDashboard() {
  clearNotice();
  renderCatalogueMode();
  elements.refreshButton.disabled = true;
  renderWorkflowDashboard("Loading active workflow...");
  try {
    const response = await fetch("/api/workflow-dashboard", { cache: "no-store" });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not load the workflow dashboard."
    );
    state.workflowDashboard = payload;
    state.selectedWorkflowRows.clear();
    populateWorkflowFilters();
    renderWorkflowDashboard();
    state.invalidRecords = [
      ...payload.invalid_records,
      ...payload.invalid_workflows.map(record => ({
        folder_name: record.folder_name,
        error: record.error,
      })),
    ];
    renderAttentionRecords();
  } catch (error) {
    state.workflowDashboard = null;
    state.invalidRecords = [];
    renderWorkflowDashboard(error.message);
    renderAttentionRecords();
    showNotice(error.message, true);
  } finally {
    elements.refreshButton.disabled = false;
  }
}

function workflowActivityRowsForScope() {
  const rows = state.workflowActivity?.rows || [];
  const scope = state.workflowActivityFilters.scope;
  if (scope === "latest") {
    return rows.filter(row => row.record_scope === "active" && row.is_latest);
  }
  if (scope === "active") {
    return rows.filter(row => row.record_scope === "active");
  }
  if (scope === "archived") {
    return rows.filter(row => row.record_scope === "archived");
  }
  return rows;
}

function filteredWorkflowActivityRows() {
  const filters = state.workflowActivityFilters;
  const query = filters.search.trim().toLocaleLowerCase();
  return workflowActivityRowsForScope().filter(row => {
    const changedValues = Object.values(row.changes).flatMap(change => [
      change.previous,
      change.current,
    ]);
    const searchable = [
      row.company_name,
      row.folder_name,
      row.finding_area,
      row.operation_label,
      ...changedValues,
    ].join(" ").toLocaleLowerCase();
    return (
      (!query || searchable.includes(query))
      && (
        filters.company === "all"
        || row.company_name === filters.company
      )
      && (
        filters.operation === "all"
        || row.operation === filters.operation
      )
      && (!filters.dateFrom || row.changed_date >= filters.dateFrom)
      && (!filters.dateTo || row.changed_date <= filters.dateTo)
    );
  });
}

function populateWorkflowActivityFilters() {
  const register = state.workflowActivity;
  if (
    state.workflowActivityFilters.company !== "all"
    && !register.companies.includes(state.workflowActivityFilters.company)
  ) {
    state.workflowActivityFilters.company = "all";
  }
  if (
    state.workflowActivityFilters.operation !== "all"
    && !register.activity_operations.includes(
      state.workflowActivityFilters.operation
    )
  ) {
    state.workflowActivityFilters.operation = "all";
  }
  elements.workflowActivityCompanyFilter.replaceChildren(
    new Option("All companies", "all")
  );
  register.companies.forEach(company => {
    elements.workflowActivityCompanyFilter.append(new Option(company, company));
  });
  elements.workflowActivityOperationFilter.replaceChildren(
    new Option("All operations", "all")
  );
  register.activity_operations.forEach(operation => {
    elements.workflowActivityOperationFilter.append(
      new Option(workflowActivityOperationLabel(operation), operation)
    );
  });
  elements.workflowActivityScopeFilter.value = (
    state.workflowActivityFilters.scope
  );
  elements.workflowActivityCompanyFilter.value = (
    state.workflowActivityFilters.company
  );
  elements.workflowActivityOperationFilter.value = (
    state.workflowActivityFilters.operation
  );
}

function renderWorkflowActivityRegister(message = null) {
  elements.workflowActivitySummary.replaceChildren();
  elements.workflowActivityContent.replaceChildren();
  const register = state.workflowActivity;
  if (message || !register) {
    elements.workflowActivityResultCount.textContent = "";
    elements.workflowActivityExportButton.disabled = true;
    const empty = document.createElement("div");
    empty.className = "workflow-activity-message";
    empty.textContent = message || "No workflow activity is available.";
    elements.workflowActivityContent.append(empty);
    return;
  }

  const scopedRows = workflowActivityRowsForScope();
  const scopedCompanies = new Set(
    scopedRows.map(row => row.company_name.toLocaleLowerCase())
  );
  const scopedPacks = new Set(scopedRows.map(row => row.answers_path));
  const latestUpdate = scopedRows[0]?.changed_at
    ? new Date(scopedRows[0].changed_at).toLocaleDateString()
    : "None";
  [
    ["Activity events", scopedRows.length],
    ["Companies", scopedCompanies.size],
    ["Packs with activity", scopedPacks.size],
    ["Latest update", latestUpdate],
  ].forEach(([label, value]) => {
    const item = document.createElement("div");
    const name = document.createElement("span");
    name.textContent = label;
    const count = document.createElement("strong");
    count.textContent = value;
    item.append(name, count);
    elements.workflowActivitySummary.append(item);
  });

  const rows = filteredWorkflowActivityRows();
  elements.workflowActivityResultCount.textContent = (
    `${rows.length} of ${scopedRows.length} activity events shown`
  );
  elements.workflowActivityExportButton.disabled = rows.length === 0;
  if (!rows.length) {
    const empty = document.createElement("div");
    empty.className = "workflow-activity-message";
    empty.textContent = "No workflow activity matches these filters.";
    elements.workflowActivityContent.append(empty);
    return;
  }

  const table = document.createElement("table");
  table.className = "workflow-activity-table";
  const header = document.createElement("thead");
  header.innerHTML = "<tr><th>Changed</th><th>Company / pack</th><th>Finding</th><th>Operation</th><th>Changes</th><th></th></tr>";
  const body = document.createElement("tbody");
  rows.forEach(row => {
    const tableRow = document.createElement("tr");
    tableRow.classList.toggle(
      "workflow-activity-row-archived",
      row.record_scope === "archived"
    );

    const changed = document.createElement("td");
    changed.className = "workflow-activity-changed-cell";
    changed.dataset.label = "Changed";
    const changedTime = document.createElement("time");
    changedTime.dateTime = row.changed_at;
    changedTime.textContent = new Date(row.changed_at).toLocaleString();
    changed.append(changedTime);

    const company = document.createElement("td");
    company.className = "workflow-activity-company-cell";
    company.dataset.label = "Company / pack";
    const companyName = document.createElement("strong");
    companyName.textContent = row.company_name;
    const pack = document.createElement("span");
    pack.textContent = `${row.folder_name} · ${row.record_scope}`;
    pack.className = `workflow-activity-scope ${row.record_scope}`;
    company.append(companyName, pack);

    const finding = document.createElement("td");
    finding.className = "workflow-activity-finding-cell";
    finding.dataset.label = "Finding";
    const findingName = document.createElement("strong");
    findingName.textContent = row.finding_area;
    finding.append(findingName);

    const operation = document.createElement("td");
    operation.className = "workflow-activity-operation-cell";
    operation.dataset.label = "Operation";
    const operationChip = document.createElement("span");
    operationChip.className = "workflow-activity-operation-chip";
    operationChip.textContent = row.operation_label;
    operation.append(operationChip);

    const changes = document.createElement("td");
    changes.className = "workflow-activity-changes-cell";
    changes.dataset.label = "Changes";
    const changeList = document.createElement("ul");
    changeList.className = "workflow-activity-change-list";
    Object.entries(row.changes).forEach(([fieldName, values]) => {
      const change = document.createElement("li");
      change.textContent = (
        `${workflowActivityFieldLabel(fieldName)}: `
        + `${workflowActivityValue(values.previous)} to `
        + workflowActivityValue(values.current)
      );
      changeList.append(change);
    });
    changes.append(changeList);

    const open = document.createElement("td");
    open.className = "workflow-activity-row-actions";
    const openButton = document.createElement("button");
    openButton.type = "button";
    openButton.className = "secondary-button workflow-open-button";
    openButton.textContent = "Open finding";
    openButton.addEventListener("click", () => openWorkflowActivityFinding(row));
    open.append(openButton);
    tableRow.append(changed, company, finding, operation, changes, open);
    body.append(tableRow);
  });
  table.append(header, body);
  elements.workflowActivityContent.append(table);
}

function buildWorkflowActivityExportUrl() {
  const filters = state.workflowActivityFilters;
  const query = new URLSearchParams({
    search: filters.search,
    scope: filters.scope,
    company: filters.company,
    operation: filters.operation,
    date_from: filters.dateFrom,
    date_to: filters.dateTo,
  });
  return `/api/workflow-activity/export?${query.toString()}`;
}

function exportWorkflowActivityRegister() {
  const rows = filteredWorkflowActivityRows();
  if (!state.workflowActivity || !rows.length) return;
  window.location.href = buildWorkflowActivityExportUrl();
  showNotice(`Exporting ${rows.length} workflow activity events to CSV.`);
}

async function openWorkflowActivityFinding(row) {
  const archived = row.record_scope === "archived";
  state.catalogueMode = archived ? "archived" : "active";
  state.selectedPath = row.answers_path;
  state.selectedAssessmentSection = "findings";
  renderCatalogueMode();
  await loadCatalogue();
  await loadSelectedAssessmentFindings(row.answers_path, archived);
  const finding = document.querySelector(
    `[data-finding-key="${row.finding_key}"]`
  );
  if (finding) {
    finding.scrollIntoView({ behavior: "smooth", block: "center" });
    finding.querySelector(".workflow-editor")?.setAttribute("open", "");
    finding.querySelector(".workflow-activity-history")?.setAttribute(
      "open",
      ""
    );
  }
}

async function loadWorkflowActivityRegister() {
  clearNotice();
  renderCatalogueMode();
  elements.refreshButton.disabled = true;
  renderWorkflowActivityRegister("Loading workflow activity...");
  try {
    const response = await fetch("/api/workflow-activity", {
      cache: "no-store",
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not load workflow activity."
    );
    state.workflowActivity = payload;
    populateWorkflowActivityFilters();
    renderWorkflowActivityRegister();
    state.invalidRecords = [
      ...payload.invalid_records,
      ...payload.invalid_workflows.map(record => ({
        folder_name: record.folder_name,
        error: record.error,
      })),
    ];
    renderAttentionRecords();
  } catch (error) {
    state.workflowActivity = null;
    state.invalidRecords = [];
    renderWorkflowActivityRegister(error.message);
    renderAttentionRecords();
    showNotice(error.message, true);
  } finally {
    elements.refreshButton.disabled = false;
  }
}

function assessmentComparisonKey(assessment) {
  return JSON.stringify([
    assessment.record_scope,
    assessment.answers_path,
  ]);
}

function assessmentComparisonGroups() {
  const groups = new Map();
  (state.assessmentComparisonCatalogue?.assessments || []).forEach(
    assessment => {
      if (!groups.has(assessment.history_key)) {
        groups.set(assessment.history_key, []);
      }
      groups.get(assessment.history_key).push(assessment);
    }
  );
  return Array.from(groups.entries()).map(([key, assessments]) => ({
    key,
    companyName: assessments[0].company_name,
    assessments: assessments.slice().sort(
      (left, right) => left.history_version - right.history_version
    ),
  }));
}

function selectedAssessmentComparisonGroup() {
  return assessmentComparisonGroups().find(
    group => group.key === state.assessmentComparisonFilters.company
  ) || null;
}

function selectedAssessmentComparisonVersions() {
  const group = selectedAssessmentComparisonGroup();
  if (!group) return { previous: null, current: null };
  return {
    previous: group.assessments.find(
      assessment => assessmentComparisonKey(assessment)
        === state.assessmentComparisonFilters.previousKey
    ) || null,
    current: group.assessments.find(
      assessment => assessmentComparisonKey(assessment)
        === state.assessmentComparisonFilters.currentKey
    ) || null,
  };
}

function assessmentComparisonOptionLabel(assessment) {
  const scope = assessment.record_scope === "archived"
    ? "Archived"
    : "Active";
  return (
    `${assessment.folder_name} · Version ${assessment.history_version} of `
    + `${assessment.history_count} · ${scope} · ${assessment.generated_date}`
  );
}

function populateAssessmentComparisonVersionOptions({ reset = false } = {}) {
  const groups = assessmentComparisonGroups().filter(
    group => group.assessments.length >= 2
  );
  const filters = state.assessmentComparisonFilters;
  if (!groups.some(group => group.key === filters.company)) {
    filters.company = groups[0]?.key || "";
    reset = true;
  }

  elements.assessmentComparisonCompany.replaceChildren();
  groups.forEach(group => {
    const option = document.createElement("option");
    option.value = group.key;
    option.textContent = `${group.companyName} · ${group.assessments.length} versions`;
    elements.assessmentComparisonCompany.append(option);
  });
  elements.assessmentComparisonCompany.value = filters.company;

  const group = selectedAssessmentComparisonGroup();
  const assessments = group?.assessments || [];
  elements.assessmentComparisonPrevious.replaceChildren();
  elements.assessmentComparisonCurrent.replaceChildren();
  assessments.forEach(assessment => {
    const key = assessmentComparisonKey(assessment);
    [
      elements.assessmentComparisonPrevious,
      elements.assessmentComparisonCurrent,
    ].forEach(select => {
      const option = document.createElement("option");
      option.value = key;
      option.textContent = assessmentComparisonOptionLabel(assessment);
      select.append(option);
    });
  });

  const availableKeys = assessments.map(assessmentComparisonKey);
  if (
    reset
    || !availableKeys.includes(filters.previousKey)
    || !availableKeys.includes(filters.currentKey)
    || filters.previousKey === filters.currentKey
  ) {
    filters.currentKey = availableKeys.at(-1) || "";
    filters.previousKey = availableKeys.at(-2) || "";
  }
  elements.assessmentComparisonPrevious.value = filters.previousKey;
  elements.assessmentComparisonCurrent.value = filters.currentKey;
  const disabled = assessments.length < 2;
  elements.assessmentComparisonCompany.disabled = !groups.length;
  elements.assessmentComparisonPrevious.disabled = disabled;
  elements.assessmentComparisonCurrent.disabled = disabled;
}

function populateAssessmentComparisonSectionFilter() {
  const selectedSection = state.assessmentComparisonFilters.section;
  elements.assessmentComparisonSectionFilter.replaceChildren();
  const all = document.createElement("option");
  all.value = "all";
  all.textContent = "All assessment areas";
  elements.assessmentComparisonSectionFilter.append(all);
  (state.assessmentVersionComparison?.sections || []).forEach(section => {
    const option = document.createElement("option");
    option.value = section;
    option.textContent = section;
    elements.assessmentComparisonSectionFilter.append(option);
  });
  const valid = Array.from(
    elements.assessmentComparisonSectionFilter.options
  ).some(option => option.value === selectedSection);
  state.assessmentComparisonFilters.section = valid
    ? selectedSection
    : "all";
  elements.assessmentComparisonSectionFilter.value = (
    state.assessmentComparisonFilters.section
  );
}

function filteredAssessmentComparisonRows() {
  const rows = state.assessmentVersionComparison?.rows || [];
  const filters = state.assessmentComparisonFilters;
  return rows.filter(row => (
    (filters.view === "all" || row.changed)
    && (filters.section === "all" || row.section === filters.section)
    && (
      filters.transition === "all"
      || row.transition === filters.transition
    )
  ));
}

function renderAssessmentComparisonSummary() {
  elements.assessmentComparisonSummary.replaceChildren();
  const summary = state.assessmentVersionComparison?.summary;
  if (!summary) return;
  [
    ["Responses", summary.total],
    ["Changed", summary.changed],
    ["Unchanged", summary.unchanged],
    ["Yes to No", summary.yes_to_no],
    ["No to Yes", summary.no_to_yes],
    ["To confirm involved", summary.involving_to_confirm],
  ].forEach(([label, value]) => {
    const item = document.createElement("div");
    const name = document.createElement("span");
    name.textContent = label;
    const count = document.createElement("strong");
    count.textContent = value;
    item.append(name, count);
    elements.assessmentComparisonSummary.append(item);
  });
}

function renderAssessmentVersionComparison(message = null) {
  elements.assessmentComparisonContent.replaceChildren();
  elements.assessmentComparisonResultCount.textContent = "";
  elements.assessmentComparisonExportButton.disabled = true;
  renderAssessmentComparisonSummary();

  if (message) {
    elements.assessmentComparisonContext.textContent = (
      "Select two versions from the same company."
    );
    const empty = document.createElement("div");
    empty.className = "assessment-comparison-message";
    empty.textContent = message;
    elements.assessmentComparisonContent.append(empty);
    return;
  }

  const comparison = state.assessmentVersionComparison;
  if (!comparison) return;
  elements.assessmentComparisonContext.textContent = (
    `${comparison.previous.folder_name} (${comparison.previous.record_scope}) `
    + `to ${comparison.current.folder_name} `
    + `(${comparison.current.record_scope}). Response transitions are `
    + "descriptive and are not a compliance score."
  );
  const rows = filteredAssessmentComparisonRows();
  elements.assessmentComparisonResultCount.textContent = (
    `${rows.length} of ${comparison.rows.length} responses shown`
  );
  elements.assessmentComparisonExportButton.disabled = rows.length === 0;
  if (!rows.length) {
    const empty = document.createElement("div");
    empty.className = "assessment-comparison-message";
    empty.textContent = "No assessment responses match these filters.";
    elements.assessmentComparisonContent.append(empty);
    return;
  }

  comparison.sections.forEach(section => {
    const sectionRows = rows.filter(row => row.section === section);
    if (!sectionRows.length) return;
    const group = document.createElement("section");
    group.className = "assessment-comparison-group";
    const title = document.createElement("h3");
    title.className = "assessment-comparison-group-title";
    const titleText = document.createElement("span");
    titleText.textContent = section;
    const titleCount = document.createElement("strong");
    titleCount.textContent = `${sectionRows.length} responses`;
    title.append(titleText, titleCount);
    const table = document.createElement("table");
    table.className = "assessment-comparison-table";
    const header = document.createElement("thead");
    header.innerHTML = "<tr><th>Finding</th><th>From</th><th>To</th><th>Transition</th><th>Open finding</th></tr>";
    const body = document.createElement("tbody");
    sectionRows.forEach(row => {
      const tableRow = document.createElement("tr");
      tableRow.classList.toggle("assessment-comparison-row-changed", row.changed);
      const finding = document.createElement("td");
      finding.className = "assessment-comparison-finding";
      finding.dataset.label = "Finding";
      const findingName = document.createElement("strong");
      findingName.textContent = row.area;
      const action = document.createElement("span");
      action.className = "assessment-comparison-row-meta";
      action.textContent = `${row.linked_action} · ${row.priority} priority`;
      finding.append(findingName, action);

      const previous = document.createElement("td");
      previous.className = "assessment-comparison-answer assessment-comparison-answer-from";
      previous.dataset.label = "From";
      const previousAnswer = document.createElement("strong");
      previousAnswer.textContent = row.previous_answer;
      const previousIndication = document.createElement("span");
      previousIndication.className = "assessment-comparison-row-meta";
      previousIndication.textContent = row.previous_review_indication;
      previous.append(previousAnswer, previousIndication);

      const current = document.createElement("td");
      current.className = "assessment-comparison-answer assessment-comparison-answer-to";
      current.dataset.label = "To";
      const currentAnswer = document.createElement("strong");
      currentAnswer.textContent = row.current_answer;
      const currentIndication = document.createElement("span");
      currentIndication.className = "assessment-comparison-row-meta";
      currentIndication.textContent = row.current_review_indication;
      current.append(currentAnswer, currentIndication);

      const transition = document.createElement("td");
      transition.className = "assessment-comparison-transition-cell";
      transition.dataset.label = "Transition";
      const transitionBadge = document.createElement("span");
      transitionBadge.className = (
        "assessment-comparison-transition "
        + row.transition.replaceAll("_", "-")
      );
      transitionBadge.textContent = row.transition_label;
      transition.append(transitionBadge);

      const open = document.createElement("td");
      open.className = "assessment-comparison-actions";
      open.dataset.label = "Open finding";
      [["From", comparison.previous], ["To", comparison.current]].forEach(
        ([label, assessment]) => {
          const button = document.createElement("button");
          button.type = "button";
          button.className = "secondary-button assessment-comparison-version-button";
          button.textContent = label;
          button.addEventListener("click", () => (
            openAssessmentComparisonFinding(assessment, row.finding_key)
          ));
          open.append(button);
        }
      );
      tableRow.append(finding, previous, current, transition, open);
      body.append(tableRow);
    });
    table.append(header, body);
    group.append(title, table);
    elements.assessmentComparisonContent.append(group);
  });
}

function buildAssessmentComparisonQuery() {
  const { previous, current } = selectedAssessmentComparisonVersions();
  if (!previous || !current) return null;
  return new URLSearchParams({
    previous_answers_path: previous.answers_path,
    previous_record_scope: previous.record_scope,
    current_answers_path: current.answers_path,
    current_record_scope: current.record_scope,
  });
}

async function loadSelectedAssessmentVersionComparison() {
  const query = buildAssessmentComparisonQuery();
  state.assessmentVersionComparison = null;
  if (!query) {
    renderAssessmentVersionComparison(
      "At least two valid versions are required for comparison."
    );
    return;
  }
  renderAssessmentVersionComparison("Loading assessment comparison...");
  try {
    const response = await fetch(
      `/api/assessment-version-comparison?${query.toString()}`,
      { cache: "no-store" }
    );
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not compare assessment versions."
    );
    if (!isComparisonMode()) return;
    state.assessmentVersionComparison = payload;
    populateAssessmentComparisonSectionFilter();
    renderAssessmentVersionComparison();
  } catch (error) {
    if (isComparisonMode()) {
      renderAssessmentVersionComparison(error.message);
      showNotice(error.message, true);
    }
  }
}

function buildAssessmentComparisonExportUrl() {
  const query = buildAssessmentComparisonQuery();
  if (!query) return null;
  const filters = state.assessmentComparisonFilters;
  query.set("view", filters.view);
  query.set("section", filters.section);
  query.set("transition", filters.transition);
  return `/api/assessment-version-comparison/export?${query.toString()}`;
}

function exportAssessmentVersionComparison() {
  const rows = filteredAssessmentComparisonRows();
  const url = buildAssessmentComparisonExportUrl();
  if (!url || !rows.length) return;
  window.location.href = url;
  showNotice(`Exporting ${rows.length} compared assessment responses to CSV.`);
}

async function openAssessmentComparisonFinding(assessment, findingKey) {
  const archived = assessment.record_scope === "archived";
  state.catalogueMode = archived ? "archived" : "active";
  state.selectedPath = assessment.answers_path;
  state.selectedAssessmentSection = "findings";
  renderCatalogueMode();
  await loadCatalogue();
  await loadSelectedAssessmentFindings(assessment.answers_path, archived);
  const finding = document.querySelector(
    `[data-finding-key="${findingKey}"]`
  );
  if (finding) {
    finding.scrollIntoView({ behavior: "smooth", block: "center" });
  }
}

async function loadAssessmentVersionComparisonCatalogue() {
  clearNotice();
  renderCatalogueMode();
  elements.refreshButton.disabled = true;
  renderAssessmentVersionComparison("Loading assessment versions...");
  try {
    const response = await fetch(
      "/api/assessment-version-comparison/options",
      { cache: "no-store" }
    );
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not load assessment versions."
    );
    state.assessmentComparisonCatalogue = payload;
    state.invalidRecords = payload.invalid_records;
    populateAssessmentComparisonVersionOptions();
    renderAttentionRecords();
    await loadSelectedAssessmentVersionComparison();
  } catch (error) {
    state.assessmentComparisonCatalogue = null;
    state.assessmentVersionComparison = null;
    state.invalidRecords = [];
    renderAssessmentVersionComparison(error.message);
    renderAttentionRecords();
    showNotice(error.message, true);
  } finally {
    elements.refreshButton.disabled = false;
  }
}

function dueDateRowMatchesFilters(row, { ignoreBucket = false } = {}) {
  const filters = state.dueDateFilters;
  const query = filters.search.trim().toLocaleLowerCase();
  const searchable = [
    row.company_name, row.folder_name, row.item_label, row.owner,
    row.status, row.review_indication, row.linked_action,
  ].join(" ").toLocaleLowerCase();
  return (
    (!query || searchable.includes(query))
    && (filters.versionScope === "all" || row.is_latest)
    && (filters.company === "all" || row.company_name === filters.company)
    && (
      filters.owner === "all"
      || (filters.owner === "__unassigned__" && !row.owner)
      || row.owner === filters.owner
    )
    && (filters.itemType === "all" || row.item_type === filters.itemType)
    && (filters.status === "all" || row.status === filters.status)
    && (ignoreBucket || filters.bucket === "all" || row.bucket === filters.bucket)
    && (filters.includeComplete || !row.is_complete)
  );
}

function filteredDueDateRows(options = {}) {
  return (state.dueDatePlanner?.rows || []).filter(
    row => dueDateRowMatchesFilters(row, options)
  );
}

function populateDueDateFilters() {
  const planner = state.dueDatePlanner;
  if (!planner) return;
  const setOptions = (select, firstLabel, values, selected) => {
    select.replaceChildren();
    const all = document.createElement("option");
    all.value = "all";
    all.textContent = firstLabel;
    select.append(all);
    values.forEach(([value, label]) => {
      const option = document.createElement("option");
      option.value = value;
      option.textContent = label;
      select.append(option);
    });
    select.value = Array.from(select.options).some(
      option => option.value === selected
    ) ? selected : "all";
  };
  const companies = Array.from(new Set(
    planner.rows.map(row => row.company_name)
  )).sort((left, right) => left.localeCompare(right));
  setOptions(
    elements.dueDateCompanyFilter,
    "All companies",
    companies.map(value => [value, value]),
    state.dueDateFilters.company
  );
  setOptions(
    elements.dueDateOwnerFilter,
    "All owners",
    [["__unassigned__", "Unassigned"], ...planner.owners.map(
      value => [value, value]
    )],
    state.dueDateFilters.owner
  );
  setOptions(
    elements.dueDateStatusFilter,
    "All statuses",
    planner.statuses.map(value => [value, value]),
    state.dueDateFilters.status
  );
  state.dueDateFilters.company = elements.dueDateCompanyFilter.value;
  state.dueDateFilters.owner = elements.dueDateOwnerFilter.value;
  state.dueDateFilters.status = elements.dueDateStatusFilter.value;
}

function dueDateBucketLabel(bucket) {
  return {
    overdue: "Overdue",
    next_7: "Next 7 days",
    next_30: "Next 30 days",
    later: "Later",
    undated: "Undated",
  }[bucket];
}

function renderDueDatePlanner(message = null) {
  elements.dueDateContent.replaceChildren();
  elements.dueDateSummary.replaceChildren();
  elements.dueDateExportButton.disabled = true;
  elements.dueDateResultCount.textContent = "";
  if (message) {
    const empty = document.createElement("div");
    empty.className = "due-date-message";
    empty.textContent = message;
    elements.dueDateContent.append(empty);
    return;
  }
  const planner = state.dueDatePlanner;
  if (!planner) return;
  const summaryRows = filteredDueDateRows({ ignoreBucket: true });
  [
    ["Overdue", "overdue"],
    ["Next 7 days", "next_7"],
    ["Next 30 days", "next_30"],
    ["Later", "later"],
    ["Undated", "undated"],
  ].forEach(([label, bucket]) => {
    const item = document.createElement("div");
    const name = document.createElement("span");
    name.textContent = label;
    const count = document.createElement("strong");
    count.textContent = summaryRows.filter(row => row.bucket === bucket).length;
    item.append(name, count);
    elements.dueDateSummary.append(item);
  });
  elements.dueDateContext.textContent = (
    `Schedule as of ${planner.as_of_date}. Assessment reviews and finding `
    + "targets are planning dates, not compliance deadlines."
  );
  const rows = filteredDueDateRows();
  elements.dueDateResultCount.textContent = (
    `${rows.length} of ${summaryRows.length} items shown`
  );
  elements.dueDateExportButton.disabled = rows.length === 0;
  if (!rows.length) {
    const empty = document.createElement("div");
    empty.className = "due-date-message";
    empty.textContent = "No planning items match these filters.";
    elements.dueDateContent.append(empty);
    return;
  }
  const table = document.createElement("table");
  table.className = "due-date-table";
  const header = document.createElement("thead");
  header.innerHTML = "<tr><th>Due</th><th>Company / pack</th><th>Item</th><th>Status</th><th>Owner</th><th></th></tr>";
  const body = document.createElement("tbody");
  rows.forEach(row => {
    const tableRow = document.createElement("tr");
    tableRow.classList.toggle("due-date-row-overdue", row.bucket === "overdue");
    tableRow.classList.toggle("due-date-row-undated", row.bucket === "undated");
    tableRow.classList.toggle(
      "due-date-row-workflow",
      row.item_type === "workflow_finding"
    );

    const due = document.createElement("td");
    due.className = "due-date-due-cell";
    due.dataset.label = "Due";
    const bucket = document.createElement("span");
    bucket.className = `due-date-bucket ${row.bucket.replaceAll("_", "-")}`;
    bucket.textContent = dueDateBucketLabel(row.bucket);
    const dueDate = document.createElement("span");
    dueDate.className = "due-date-value";
    dueDate.textContent = row.due_date || "No date";
    due.append(bucket, dueDate);

    const company = document.createElement("td");
    company.className = "due-date-company-cell";
    company.dataset.label = "Company / pack";
    const companyName = document.createElement("strong");
    companyName.textContent = row.company_name;
    const pack = document.createElement("span");
    pack.className = "due-date-row-meta";
    pack.textContent = `${row.folder_name} · Version ${row.history_version} of ${row.history_count}`;
    company.append(companyName, pack);

    const item = document.createElement("td");
    item.className = "due-date-item-cell";
    item.dataset.label = "Item";
    const itemName = document.createElement("strong");
    itemName.textContent = row.item_label;
    const itemType = document.createElement("span");
    itemType.className = row.item_type === "assessment_review"
      ? "due-date-row-meta"
      : "due-date-priority-chip";
    itemType.textContent = row.item_type === "assessment_review"
      ? "Assessment review"
      : `${row.linked_action} · ${row.priority} priority`;
    item.append(itemName, itemType);

    const status = document.createElement("td");
    status.className = "due-date-status-cell";
    status.dataset.label = "Status";
    const statusBadge = document.createElement("span");
    statusBadge.className = "due-date-status-chip";
    statusBadge.textContent = row.status;
    status.append(statusBadge);

    const owner = document.createElement("td");
    owner.className = "due-date-owner-cell";
    owner.dataset.label = "Owner";
    owner.textContent = row.owner || "Unassigned";
    owner.classList.toggle("due-date-muted-cell", !row.owner);

    const open = document.createElement("td");
    open.className = "due-date-row-actions";
    const button = document.createElement("button");
    button.type = "button";
    button.className = "secondary-button workflow-open-button";
    button.textContent = row.item_type === "assessment_review"
      ? "Open assessment"
      : "Open finding";
    button.addEventListener("click", () => openDueDateItem(row));
    open.append(button);
    tableRow.append(due, company, item, status, owner, open);
    body.append(tableRow);
  });
  table.append(header, body);
  elements.dueDateContent.append(table);
}

function buildDueDateExportUrl() {
  const filters = state.dueDateFilters;
  const query = new URLSearchParams({
    search: filters.search,
    version_scope: filters.versionScope,
    company: filters.company,
    owner: filters.owner,
    item_type: filters.itemType,
    status: filters.status,
    bucket: filters.bucket,
    include_complete: String(filters.includeComplete),
  });
  return `/api/due-date-planner/export?${query.toString()}`;
}

function exportDueDatePlanner() {
  const rows = filteredDueDateRows();
  if (!rows.length) return;
  window.location.href = buildDueDateExportUrl();
  showNotice(`Exporting ${rows.length} due-date planning items to CSV.`);
}

async function openDueDateItem(row) {
  state.catalogueMode = "active";
  state.selectedPath = row.answers_path;
  state.selectedAssessmentSection = row.item_type === "workflow_finding"
    ? "findings"
    : "overview";
  renderCatalogueMode();
  await loadCatalogue();
  if (row.item_type !== "workflow_finding") return;
  await loadSelectedAssessmentFindings(row.answers_path, false);
  document.querySelector(
    `[data-finding-key="${row.finding_key}"]`
  )?.scrollIntoView({ behavior: "smooth", block: "center" });
}

async function loadDueDatePlanner() {
  clearNotice();
  renderCatalogueMode();
  elements.refreshButton.disabled = true;
  renderDueDatePlanner("Loading due-date plan...");
  try {
    const response = await fetch("/api/due-date-planner", {
      cache: "no-store",
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not load due-date planning."
    );
    state.dueDatePlanner = payload;
    state.invalidRecords = [
      ...payload.invalid_records,
      ...payload.invalid_workflows,
      ...payload.invalid_dates,
    ];
    populateDueDateFilters();
    renderDueDatePlanner();
    renderAttentionRecords();
  } catch (error) {
    state.dueDatePlanner = null;
    state.invalidRecords = [];
    renderDueDatePlanner(error.message);
    renderAttentionRecords();
    showNotice(error.message, true);
  } finally {
    elements.refreshButton.disabled = false;
  }
}

function assessmentGroups() {
  const groups = new Map();
  state.assessments.forEach(assessment => {
    const key = assessment.history_key
      || assessment.company_name.trim().toLocaleLowerCase();
    if (!groups.has(key)) groups.set(key, []);
    groups.get(key).push(assessment);
  });
  return Array.from(groups.values()).map(assessments =>
    assessments.slice().sort((left, right) =>
      (left.history_version || 1) - (right.history_version || 1)
    )
  );
}

function filteredAssessmentGroups() {
  const query = state.search.trim().toLocaleLowerCase();
  const groups = assessmentGroups();
  if (!query) return groups;
  return groups.filter(assessments =>
    assessments.some(assessment =>
      assessment.company_name.toLocaleLowerCase().includes(query)
      || assessment.folder_name.toLocaleLowerCase().includes(query)
      || assessment.document_owner.toLocaleLowerCase().includes(query)
    )
  );
}

function buildAssessmentRow(
  assessment,
  { showCompany = true, directArchive = false } = {}
) {
  const shell = document.createElement("div");
  shell.className = "assessment-row-shell";

  const button = document.createElement("button");
  button.type = "button";
  button.className = "assessment-row";
  button.classList.toggle(
    "selected",
    assessment.answers_path === state.selectedPath
  );
  button.addEventListener(
    "click",
    () => selectAssessment(assessment.answers_path)
  );

  const identity = document.createElement("span");
  const title = document.createElement("strong");
  title.textContent = showCompany
    ? assessment.company_name
    : assessment.folder_name;
  const details = document.createElement("span");
  details.textContent = showCompany
    ? `${assessment.folder_name} · ${assessment.generated_date}`
    : assessment.generated_date;
  identity.append(title, details);

  const status = document.createElement("span");
  if (isArchivedMode()) {
    status.className = "row-pending";
    status.textContent = assessment.to_confirm
      ? `Archived · ${assessment.to_confirm} pending`
      : "Archived";
  } else {
    status.className = assessment.is_latest
      ? "assessment-version-label"
      : "row-pending";
    status.textContent = assessment.is_latest
      ? assessment.to_confirm
        ? `Latest · ${assessment.to_confirm} pending`
        : "Latest"
      : assessment.to_confirm
        ? `Version ${assessment.history_version || 1} · ${assessment.to_confirm} pending`
        : `Version ${assessment.history_version || 1}`;
  }
  button.append(identity, status);
  shell.append(button);

  if (directArchive) {
    const archiveButton = document.createElement("button");
    archiveButton.type = "button";
    archiveButton.className = "version-archive-button";
    archiveButton.textContent = "Archive";
    archiveButton.title = `Archive ${assessment.folder_name}`;
    archiveButton.addEventListener(
      "click",
      () => archiveAssessment(assessment, archiveButton)
    );
    shell.append(archiveButton);
  }

  return shell;
}

function renderAssessmentList() {
  elements.assessmentList.replaceChildren();
  if (isDraftMode()) {
    const drafts = filteredDrafts();
    elements.resultCount.textContent = `${drafts.length} ${
      drafts.length === 1 ? "draft" : "drafts"
    }`;
    drafts.forEach(draft => {
      const button = document.createElement("button");
      button.type = "button";
      button.className = "assessment-row";
      button.classList.toggle(
        "selected",
        draft.draft_id === state.selectedDraftId
      );
      button.addEventListener("click", () => {
        state.selectedDraftId = draft.draft_id;
        renderAssessmentList();
        renderDetail();
      });
      const identity = document.createElement("span");
      const company = document.createElement("strong");
      company.textContent = draft.company_name;
      const saved = document.createElement("span");
      saved.textContent = new Date(draft.saved_at).toLocaleString();
      identity.append(company, saved);
      const progress = document.createElement("span");
      progress.className = "row-pending";
      progress.textContent = `${draft.answered_count}/${draft.total_questions}`;
      button.append(identity, progress);
      elements.assessmentList.append(button);
    });

    if (!drafts.length) {
      const empty = document.createElement("div");
      empty.className = "list-empty";
      empty.textContent = state.drafts.length
        ? "No drafts match this search."
        : "No assessment drafts found.";
      elements.assessmentList.append(empty);
    }
    return;
  }

  const assessments = filteredAssessments();

  if (isArchivedMode()) {
    elements.resultCount.textContent = `${assessments.length} packs`;
    if (assessments.length) {
      assessments.forEach(assessment => {
        elements.assessmentList.append(buildAssessmentRow(assessment));
      });
      return;
    }
  } else {
    const groups = filteredAssessmentGroups();
    const packCount = groups.reduce(
      (total, assessmentsInGroup) => total + assessmentsInGroup.length,
      0
    );
    elements.resultCount.textContent = (
      `${groups.length} ${groups.length === 1 ? "company" : "companies"} · `
      + `${packCount} ${packCount === 1 ? "pack" : "packs"}`
    );

    if (groups.length) {
      groups.forEach(assessmentsInGroup => {
        const latest = assessmentsInGroup.find(item => item.is_latest)
          || assessmentsInGroup[assessmentsInGroup.length - 1];
        const older = assessmentsInGroup
          .filter(item => item.answers_path !== latest.answers_path)
          .reverse();
        const group = document.createElement("section");
        group.className = "assessment-history-group";

        const heading = document.createElement("div");
        heading.className = "assessment-history-heading";
        const company = document.createElement("strong");
        company.textContent = latest.company_name;
        const count = document.createElement("span");
        count.textContent = `${assessmentsInGroup.length} ${
          assessmentsInGroup.length === 1 ? "version" : "versions"
        }`;
        heading.append(company, count);
        group.append(heading);
        group.append(buildAssessmentRow(latest, { showCompany: false }));

        if (older.length) {
          const details = document.createElement("details");
          details.className = "assessment-history-details";
          details.open = older.some(
            assessment => assessment.answers_path === state.selectedPath
          );
          const summary = document.createElement("summary");
          summary.textContent = `Show ${older.length} older ${
            older.length === 1 ? "version" : "versions"
          }`;
          const versions = document.createElement("div");
          versions.className = "assessment-history-versions";
          older.forEach(assessment => {
            versions.append(buildAssessmentRow(
              assessment,
              { showCompany: false, directArchive: true }
            ));
          });
          details.append(summary, versions);
          group.append(details);
        }

        elements.assessmentList.append(group);
      });
      return;
    }
  }

  if (!elements.assessmentList.children.length) {
    const empty = document.createElement("div");
    empty.className = "list-empty";
    empty.textContent = state.assessments.length
      ? "No assessments match this search."
      : isArchivedMode()
        ? "No archived assessments found."
        : "No saved assessments found.";
    elements.assessmentList.append(empty);
  }
}

function selectAssessment(answersPath, section = "overview") {
  state.selectedPath = answersPath;
  state.selectedAssessmentSection = section;
  renderAssessmentList();
  renderDetail();
}

function generatedAssessmentSelectionPath(result) {
  if (result?.answers_path) return result.answers_path;

  const outputFolder = result?.output_folder || "";
  const outputFolderName = outputFolder
    .replace(/\\/g, "/")
    .split("/")
    .filter(Boolean)
    .pop();
  if (outputFolderName) {
    const folderMatch = state.assessments.find(
      assessment => assessment.folder_name === outputFolderName
    );
    if (folderMatch) return folderMatch.answers_path;
  }

  if (result?.company_name) {
    const companyMatches = state.assessments.filter(
      assessment => assessment.company_name === result.company_name
    );
    const latestCompanyMatch = companyMatches.find(
      assessment => assessment.is_latest
    ) || companyMatches[0];
    if (latestCompanyMatch) return latestCompanyMatch.answers_path;
  }

  return state.selectedPath;
}

function formatFileSize(sizeBytes) {
  if (sizeBytes < 1024) return `${sizeBytes} B`;
  if (sizeBytes < 1024 * 1024) return `${(sizeBytes / 1024).toFixed(1)} KB`;
  return `${(sizeBytes / (1024 * 1024)).toFixed(1)} MB`;
}

function buildPackFileUrl(answersPath, filename) {
  const query = new URLSearchParams({
    answers_path: answersPath,
    filename,
    archived: String(isArchivedMode()),
  });
  return `/api/assessment-file?${query.toString()}`;
}

const PACK_FILE_REVIEW_ORDER = [
  "START_HERE.txt",
  "document_pack_summary.docx",
  "implementation_action_plan.docx",
  "compliance_evidence_register.xlsx",
];

const PACK_FILE_REVIEW_DETAILS = {
  "START_HERE.txt": {
    label: "Start here",
    purpose: "Front door for the pack: scope, limitations, first review path, and safe use reminders.",
  },
  "document_pack_summary.docx": {
    label: "Pack summary",
    purpose: "Management-friendly overview of the generated documents, workbook, assumptions, and review boundaries.",
  },
  "implementation_action_plan.docx": {
    label: "Action plan",
    purpose: "Working record for gaps, owners, target dates, decisions, exceptions, and linked evidence needs.",
  },
  "compliance_evidence_register.xlsx": {
    label: "Evidence tracker",
    purpose: "Excel workbook for day-to-day evidence rows, review dates, linked actions, and dashboard follow-up.",
  },
};

function normalisePackFilename(filename) {
  return filename.toLocaleLowerCase();
}

function packFileReviewName(filename) {
  const normalised = normalisePackFilename(filename);
  return PACK_FILE_REVIEW_ORDER.find(
    item => item.toLocaleLowerCase() === normalised
  );
}

function packFileReviewSortKey(filename) {
  const normalised = normalisePackFilename(filename);
  const index = PACK_FILE_REVIEW_ORDER.findIndex(
    reviewFile => reviewFile.toLocaleLowerCase() === normalised
  );
  return index === -1 ? PACK_FILE_REVIEW_ORDER.length : index;
}

function packFileReviewLabel(filename) {
  const reviewFile = packFileReviewName(filename);
  return reviewFile ? PACK_FILE_REVIEW_DETAILS[reviewFile].label : "";
}

function packFileReviewPurpose(filename) {
  const reviewFile = packFileReviewName(filename);
  return reviewFile ? PACK_FILE_REVIEW_DETAILS[reviewFile].purpose : "";
}

function packFileReviewStep(filename) {
  const index = packFileReviewSortKey(filename);
  return index < PACK_FILE_REVIEW_ORDER.length ? `Step ${index + 1}` : "";
}

function packFilesForDisplay(files) {
  return files.slice().sort((left, right) => {
    const leftPriority = packFileReviewSortKey(left.filename);
    const rightPriority = packFileReviewSortKey(right.filename);
    if (leftPriority !== rightPriority) {
      return leftPriority - rightPriority;
    }
    return left.filename.localeCompare(right.filename);
  });
}

async function openLocalPackFile(filename, button) {
  const assessment = currentAssessment();
  if (!assessment) return;

  const originalText = button.textContent;
  button.disabled = true;
  button.textContent = "Opening...";
  clearNotice();

  try {
    const response = await fetch("/api/assessment-file/open", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        answers_path: assessment.answers_path,
        filename,
        archived: isArchivedMode(),
      }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "The file could not be opened locally."
    );
    showNotice(`Opened ${payload.filename} using its local application.`);
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    button.disabled = false;
    button.textContent = originalText;
  }
}

async function openSelectedPackFolder() {
  const assessment = currentAssessment();
  if (!assessment) return;

  elements.openPackFolderButton.disabled = true;
  elements.openPackFolderButton.textContent = "Opening...";
  clearNotice();

  try {
    const response = await fetch("/api/assessment-folder/open", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        answers_path: assessment.answers_path,
        archived: isArchivedMode(),
      }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "The pack folder could not be opened."
    );
    showNotice(`Opened ${payload.folder_name} in the local file browser.`);
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.openPackFolderButton.disabled = false;
    elements.openPackFolderButton.textContent = "Open pack folder";
  }
}

function renderPackFiles(message = null) {
  elements.packFilesList.replaceChildren();
  elements.openPackFolderButton.disabled = Boolean(message);
  elements.packFileCount.textContent = message
    ? ""
    : `${state.packFiles.length} files`;

  if (message || !state.packFiles.length) {
    const empty = document.createElement("div");
    empty.className = "pack-files-message";
    empty.textContent = message || "No files found in this pack.";
    elements.packFilesList.append(empty);
    return;
  }

  const assessment = currentAssessment();
  packFilesForDisplay(state.packFiles).forEach(file => {
    const reviewLabel = packFileReviewLabel(file.filename);
    const row = document.createElement("div");
    row.className = "pack-file-row";
    row.classList.toggle("priority-pack-file", Boolean(reviewLabel));

    const name = document.createElement("span");
    name.className = "pack-file-name";
    const nameBlock = document.createElement("span");
    nameBlock.className = "pack-file-name-block";
    const fileName = document.createElement("span");
    fileName.className = "pack-file-name-text";
    fileName.textContent = file.filename;
    nameBlock.append(fileName);
    if (reviewLabel) {
      const badge = document.createElement("span");
      badge.className = "pack-file-review-badge";
      badge.textContent = reviewLabel;
      const hint = document.createElement("small");
      hint.className = "pack-file-review-hint";
      hint.textContent = packFileReviewStep(file.filename);
      nameBlock.append(badge, hint);
    }
    name.append(nameBlock);
    const reviewPurpose = packFileReviewPurpose(file.filename);
    if (reviewPurpose) {
      const purpose = document.createElement("small");
      purpose.className = "pack-file-review-purpose";
      purpose.textContent = reviewPurpose;
      name.append(purpose);
    }

    const size = document.createElement("span");
    size.className = "pack-file-size";
    size.textContent = formatFileSize(file.size_bytes);

    const openButton = document.createElement("button");
    openButton.type = "button";
    openButton.className = "pack-file-action";
    if (reviewLabel) {
      openButton.classList.add("recommended-pack-file-action");
    }
    openButton.textContent = "Open locally";
    openButton.addEventListener("click", () => {
      openLocalPackFile(file.filename, openButton);
    });

    const link = document.createElement("a");
    link.className = "pack-file-action";
    link.href = buildPackFileUrl(assessment.answers_path, file.filename);
    link.textContent = "Download";

    const actions = document.createElement("span");
    actions.className = "pack-file-actions";
    actions.append(openButton, link);

    row.append(name, size, actions);
    elements.packFilesList.append(row);
  });
}

async function loadSelectedAssessmentFiles(answersPath, archivedMode) {
  state.packFiles = [];
  renderPackFiles("Loading pack files...");
  elements.packFileCount.textContent = "Loading...";

  const query = new URLSearchParams({
    answers_path: answersPath,
    archived: String(archivedMode),
  });

  try {
    const response = await fetch(`/api/assessment-files?${query.toString()}`, {
      cache: "no-store",
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(payload.error || "Could not load pack files.");
    if (
      state.selectedPath !== answersPath
      || isArchivedMode() !== archivedMode
    ) return;
    state.packFiles = payload.files;
    renderPackFiles();
  } catch (error) {
    if (
      state.selectedPath === answersPath
      && isArchivedMode() === archivedMode
    ) {
      state.packFiles = [];
      renderPackFiles(error.message);
    }
  }
}

function renderComparison(message = null) {
  elements.comparisonContent.replaceChildren();
  elements.comparisonCount.textContent = "";

  if (message) {
    elements.comparisonSummary.textContent = "";
    const row = document.createElement("div");
    row.className = "comparison-message";
    row.textContent = message;
    elements.comparisonContent.append(row);
    return;
  }

  const comparison = state.comparison;
  if (!comparison?.has_predecessor) {
    elements.comparisonSummary.textContent = (
      "This is the first active version for the company."
    );
    const row = document.createElement("div");
    row.className = "comparison-message";
    row.textContent = "No earlier active version is available for comparison.";
    elements.comparisonContent.append(row);
    return;
  }

  elements.comparisonSummary.textContent = (
    `Compared with ${comparison.previous_folder_name} `
    + `(${comparison.previous_generated_date}).`
  );
  elements.comparisonCount.textContent = `${comparison.changes.length} ${
    comparison.changes.length === 1 ? "change" : "changes"
  }`;

  if (!comparison.changes.length) {
    const row = document.createElement("div");
    row.className = "comparison-message";
    row.textContent = "No company details or questionnaire answers changed.";
    elements.comparisonContent.append(row);
    return;
  }

  comparison.changes.forEach(change => {
    const row = document.createElement("div");
    row.className = "comparison-row";
    const field = document.createElement("strong");
    field.textContent = change.field;

    const previous = document.createElement("div");
    previous.className = "comparison-value";
    const previousLabel = document.createElement("span");
    previousLabel.textContent = "Previous";
    const previousValue = document.createElement("div");
    previousValue.textContent = change.previous_value;
    previous.append(previousLabel, previousValue);

    const current = document.createElement("div");
    current.className = "comparison-value";
    const currentLabel = document.createElement("span");
    currentLabel.textContent = "Current";
    const currentValue = document.createElement("div");
    currentValue.textContent = change.current_value;
    current.append(currentLabel, currentValue);

    row.append(field, previous, current);
    elements.comparisonContent.append(row);
  });
}

async function loadSelectedAssessmentComparison(answersPath) {
  state.comparison = null;
  renderComparison("Loading comparison...");
  const query = new URLSearchParams({ answers_path: answersPath });

  try {
    const response = await fetch(
      `/api/assessment-comparison?${query.toString()}`,
      { cache: "no-store" }
    );
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not compare assessment versions."
    );
    if (state.selectedPath !== answersPath || isArchivedMode()) return;
    state.comparison = payload;
    renderComparison();
  } catch (error) {
    if (state.selectedPath === answersPath && !isArchivedMode()) {
      renderComparison(error.message);
    }
  }
}

function findingIndicationClass(indication) {
  const classes = {
    "Reported in place": "reported",
    "Follow-up recommended": "follow-up",
    "Confirm response": "confirm",
    "Applies": "context",
    "Not reported as used": "not-used",
  };
  return classes[indication] || "context";
}

function workflowStatusClass(status) {
  return status.toLocaleLowerCase().replaceAll(" ", "-");
}

function workflowActivityOperationLabel(operation) {
  const labels = {
    individual_update: "Finding updated",
    bulk_owner: "Owner assigned in bulk",
    bulk_status: "Status assigned in bulk",
    bulk_target_date: "Target date assigned in bulk",
    bulk_note: "Note appended in bulk",
    carry_forward: "Workflow carried forward",
  };
  return labels[operation] || "Workflow updated";
}

function workflowActivityFieldLabel(fieldName) {
  const labels = {
    status: "Status",
    owner: "Owner",
    target_date: "Target date",
    notes: "Notes",
  };
  return labels[fieldName] || fieldName;
}

function workflowActivityValue(value) {
  const text = String(value || "").replaceAll(/\s+/g, " ").trim();
  if (!text) return "Not set";
  return text.length > 120 ? `${text.slice(0, 117)}...` : text;
}

function buildWorkflowActivityHistory(finding) {
  const activity = finding.workflow_activity || [];
  const history = document.createElement("details");
  history.className = "workflow-activity-history";
  const summary = document.createElement("summary");
  summary.textContent = `Activity history (${activity.length})`;
  history.append(summary);

  if (!activity.length) {
    const empty = document.createElement("p");
    empty.className = "workflow-activity-empty";
    empty.textContent = "No recorded workflow changes for this finding.";
    history.append(empty);
    return history;
  }

  const list = document.createElement("ol");
  list.className = "workflow-activity-list";
  activity.slice().reverse().forEach(activityEntry => {
    const item = document.createElement("li");
    const heading = document.createElement("div");
    const operation = document.createElement("strong");
    operation.textContent = workflowActivityOperationLabel(
      activityEntry.operation
    );
    const changedAt = document.createElement("time");
    changedAt.dateTime = activityEntry.changed_at;
    changedAt.textContent = new Date(activityEntry.changed_at).toLocaleString();
    heading.append(operation, changedAt);

    const changes = document.createElement("ul");
    Object.entries(activityEntry.changes).forEach(([fieldName, values]) => {
      const change = document.createElement("li");
      change.textContent = (
        `${workflowActivityFieldLabel(fieldName)}: `
        + `${workflowActivityValue(values.previous)} to `
        + workflowActivityValue(values.current)
      );
      changes.append(change);
    });
    item.append(heading, changes);
    list.append(item);
  });
  history.append(list);
  return history;
}

function renderWorkflowSummary() {
  elements.workflowSummary.replaceChildren();
  const summary = state.findings?.workflow_summary;
  if (!summary) {
    elements.workflowSummary.hidden = true;
    return;
  }

  elements.workflowSummary.hidden = false;
  const items = [
    ["Complete", summary.status_counts["Complete"]],
    ["In progress", summary.status_counts["In progress"]],
    ["Blocked", summary.status_counts["Blocked"]],
    ["Assigned", summary.assigned],
    ["Overdue", summary.overdue],
  ];
  items.forEach(([label, value]) => {
    const item = document.createElement("span");
    const count = document.createElement("strong");
    count.textContent = value;
    item.append(count, ` ${label}`);
    elements.workflowSummary.append(item);
  });
}

async function saveFindingWorkflow(finding, controls) {
  const assessment = currentAssessment();
  if (!assessment || isArchivedMode()) return;

  controls.button.disabled = true;
  controls.button.textContent = "Saving...";
  clearNotice();
  try {
    const response = await fetch("/api/assessment-workflow", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        answers_path: assessment.answers_path,
        finding_key: finding.key,
        status: controls.status.value,
        owner: controls.owner.value,
        target_date: controls.targetDate.value,
        notes: controls.notes.value,
      }),
    });
    const result = await response.json();
    if (!response.ok) throw new Error(
      result.error || "The finding workflow could not be saved."
    );
    finding.workflow = result.workflow;
    finding.workflow_activity = result.workflow_activity;
    state.findings.workflow_summary = result.workflow_summary;
    renderFindings();
    await loadSelectedAssessmentFiles(assessment.answers_path, false);
    showNotice(`Updated workflow for ${finding.area}.`);
  } catch (error) {
    showNotice(error.message, true);
    controls.button.disabled = false;
    controls.button.textContent = "Save workflow";
  }
}

function renderFindings(message = null) {
  elements.findingsContent.replaceChildren();
  renderWorkflowSummary();
  elements.findingsFilters.forEach(button => {
    button.classList.toggle(
      "selected",
      button.dataset.findingFilter === state.findingsFilter
    );
  });

  if (message) {
    elements.findingsSummary.textContent = "";
    const row = document.createElement("div");
    row.className = "findings-message";
    row.textContent = message;
    elements.findingsContent.append(row);
    return;
  }

  const findings = state.findings;
  if (!findings) return;
  const visibleSections = findings.sections.map(section => ({
    title: section.title,
    findings: section.findings.filter(finding =>
      state.findingsFilter === "all"
      || finding.review_indication === state.findingsFilter
    ),
  })).filter(section => section.findings.length);
  const visibleCount = visibleSections.reduce(
    (total, section) => total + section.findings.length,
    0
  );
  elements.findingsSummary.textContent = (
    `${visibleCount} of ${findings.total_findings} findings shown`
  );

  if (!visibleCount) {
    const row = document.createElement("div");
    row.className = "findings-message";
    row.textContent = "No findings match this filter.";
    elements.findingsContent.append(row);
    return;
  }

  visibleSections.forEach(section => {
    const group = document.createElement("details");
    group.className = "findings-group";
    group.open = true;
    const summary = document.createElement("summary");
    const title = document.createElement("strong");
    title.textContent = section.title;
    const count = document.createElement("span");
    count.textContent = `${section.findings.length} findings`;
    summary.append(title, count);
    group.append(summary);

    section.findings.forEach(finding => {
      const row = document.createElement("div");
      row.className = "finding-row";
      row.dataset.findingKey = finding.key;

      const area = document.createElement("div");
      area.className = "finding-area";
      const areaName = document.createElement("strong");
      areaName.textContent = finding.area;
      const responseType = document.createElement("span");
      responseType.textContent = finding.response_type;
      area.append(areaName, responseType);

      const status = document.createElement("div");
      status.className = "finding-status";
      const indication = document.createElement("span");
      indication.className = (
        `finding-indication ${
          findingIndicationClass(finding.review_indication)
        }`
      );
      indication.textContent = finding.review_indication;
      const answer = document.createElement("span");
      answer.className = "finding-answer";
      answer.textContent = `Answer: ${finding.reported_answer}`;
      status.append(indication, answer);

      const action = document.createElement("div");
      action.className = "finding-action";
      const linkedAction = document.createElement("strong");
      linkedAction.textContent = finding.linked_action;
      const priority = document.createElement("span");
      priority.textContent = `${finding.priority} priority`;
      action.append(linkedAction, priority);

      const nextStep = document.createElement("p");
      nextStep.className = "finding-next-step";
      nextStep.textContent = finding.recommended_next_step;

      const workflow = document.createElement("div");
      workflow.className = "finding-workflow";
      const workflowHeading = document.createElement("div");
      workflowHeading.className = "finding-workflow-heading";
      const workflowStatus = document.createElement("span");
      workflowStatus.className = (
        `workflow-status ${workflowStatusClass(finding.workflow.status)}`
      );
      workflowStatus.textContent = finding.workflow.status;
      const workflowOwner = document.createElement("span");
      workflowOwner.textContent = finding.workflow.owner
        ? `Owner: ${finding.workflow.owner}`
        : "Unassigned";
      workflowHeading.append(workflowStatus, workflowOwner);

      const workflowEditor = document.createElement("details");
      workflowEditor.className = "workflow-editor";
      const workflowEditorSummary = document.createElement("summary");
      workflowEditorSummary.textContent = isArchivedMode()
        ? "View workflow"
        : "Manage workflow";
      const workflowFields = document.createElement("div");
      workflowFields.className = "workflow-fields";

      const statusLabel = document.createElement("label");
      statusLabel.textContent = "Status";
      const statusSelect = document.createElement("select");
      state.findings.workflow_statuses.forEach(workflowStatusOption => {
        const option = document.createElement("option");
        option.value = workflowStatusOption;
        option.textContent = workflowStatusOption;
        option.selected = workflowStatusOption === finding.workflow.status;
        statusSelect.append(option);
      });
      statusLabel.append(statusSelect);

      const ownerLabel = document.createElement("label");
      ownerLabel.textContent = "Owner";
      const ownerInput = document.createElement("input");
      ownerInput.type = "text";
      ownerInput.maxLength = 200;
      ownerInput.value = finding.workflow.owner;
      ownerInput.placeholder = "Assign an owner";
      ownerLabel.append(ownerInput);

      const targetDateLabel = document.createElement("label");
      targetDateLabel.textContent = "Target date";
      const targetDateInput = document.createElement("input");
      targetDateInput.type = "date";
      targetDateInput.value = finding.workflow.target_date;
      targetDateLabel.append(targetDateInput);

      const notesLabel = document.createElement("label");
      notesLabel.className = "workflow-notes-field";
      notesLabel.textContent = "Notes";
      const notesInput = document.createElement("textarea");
      notesInput.maxLength = 2000;
      notesInput.rows = 3;
      notesInput.value = finding.workflow.notes;
      notesInput.placeholder = "Record progress, dependencies, or next actions";
      notesLabel.append(notesInput);

      const saveButton = document.createElement("button");
      saveButton.type = "button";
      saveButton.className = "primary-button workflow-save-button";
      saveButton.textContent = "Save workflow";
      saveButton.addEventListener("click", () => {
        saveFindingWorkflow(finding, {
          status: statusSelect,
          owner: ownerInput,
          targetDate: targetDateInput,
          notes: notesInput,
          button: saveButton,
        });
      });

      if (isArchivedMode()) {
        [statusSelect, ownerInput, targetDateInput, notesInput].forEach(
          control => { control.disabled = true; }
        );
        saveButton.hidden = true;
      }

      workflowFields.append(
        statusLabel,
        ownerLabel,
        targetDateLabel,
        notesLabel,
        saveButton
      );
      workflowEditor.append(
        workflowEditorSummary,
        workflowFields,
        buildWorkflowActivityHistory(finding)
      );
      workflow.append(workflowHeading, workflowEditor);

      row.append(area, status, action, nextStep, workflow);
      group.append(row);
    });
    elements.findingsContent.append(group);
  });
}

async function loadSelectedAssessmentFindings(answersPath, archivedMode) {
  state.findings = null;
  state.findingsFilter = "all";
  renderFindings("Loading assessment findings...");
  const query = new URLSearchParams({
    answers_path: answersPath,
    archived: String(archivedMode),
  });

  try {
    const response = await fetch(
      `/api/assessment-findings?${query.toString()}`,
      { cache: "no-store" }
    );
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not load assessment findings."
    );
    if (
      state.selectedPath !== answersPath
      || isArchivedMode() !== archivedMode
    ) return;
    state.findings = payload;
    renderFindings();
  } catch (error) {
    if (
      state.selectedPath === answersPath
      && isArchivedMode() === archivedMode
    ) {
      renderFindings(error.message);
    }
  }
}

function setEmptyStateCopy(eyebrow, title, intro) {
  elements.emptyStateEyebrow.textContent = eyebrow;
  elements.emptyState.querySelector("#detail-title").textContent = title;
  elements.emptyStateIntro.textContent = intro;
}

function renderEmptyStateContext() {
  const hasSearch = Boolean(state.search.trim());
  const hasAttention = state.invalidRecords.length > 0;
  const active = !(isDraftMode() || isArchivedMode());
  const activeEmpty = active && !state.assessments.length;
  const draftEmpty = isDraftMode() && !state.drafts.length;
  const archivedEmpty = isArchivedMode() && !state.assessments.length;
  const trueFirstRun = activeEmpty && !hasSearch && !hasAttention;

  elements.firstRunEmptyState.hidden = !trueFirstRun;
  elements.selectedEmptyStateGrid.hidden = trueFirstRun;

  if (trueFirstRun) {
    setEmptyStateCopy(
      "First run",
      "Start your first assessment",
      "There are no saved packs yet. Create a sample assessment first, review the generated files, and open the Start here guide before entering real business details."
    );
    return;
  }

  if (activeEmpty && hasAttention && !hasSearch) {
    setEmptyStateCopy(
      "Assessment workspace",
      "No valid saved assessments yet",
      "The library has records needing attention but no valid packs to select. Review the attention messages, or start a new sample assessment while keeping local backup guidance in mind."
    );
    return;
  }

  if (activeEmpty && hasSearch) {
    setEmptyStateCopy(
      "Search results",
      "No assessments match this search",
      "Clear or change the search term to see saved packs, or start a new assessment if this is a new company."
    );
    return;
  }

  if (draftEmpty && !hasSearch) {
    setEmptyStateCopy(
      "Assessment drafts",
      "No saved drafts yet",
      "Drafts appear here after you start a questionnaire and choose Save draft and exit. Use New assessment when you are ready to begin."
    );
    return;
  }

  if (draftEmpty && hasSearch) {
    setEmptyStateCopy(
      "Search results",
      "No drafts match this search",
      "Clear or change the search term to see saved questionnaire drafts."
    );
    return;
  }

  if (archivedEmpty && !hasSearch) {
    setEmptyStateCopy(
      "Archived assessments",
      "No archived packs yet",
      "Archived packs appear here after an active pack is archived. Active records remain in the main assessment library."
    );
    return;
  }

  if (archivedEmpty && hasSearch) {
    setEmptyStateCopy(
      "Search results",
      "No archived packs match this search",
      "Clear or change the search term to see archived assessment packs."
    );
    return;
  }

  setEmptyStateCopy(
    "Assessment workspace",
    "Choose an assessment to inspect",
    "Select a saved assessment from the left list to review metadata, findings, version changes, pack files, and generation actions."
  );
}

function handleEmptyStateAction(action) {
  if (action === "new-assessment") {
    openNewAssessment();
  }
  if (action === "operator-checklist") {
    showOperatorChecklistView();
  }
}

function renderActionStateNotes(assessment) {
  const archived = isArchivedMode();
  const pendingCount = Number(assessment.to_confirm || 0);

  elements.createPackActionState.classList.toggle("archived", archived);
  elements.createPackActionState.classList.toggle(
    "pending",
    !archived && pendingCount > 0
  );
  elements.localRecordActionState.classList.toggle("archived", archived);
  elements.localRecordActionState.classList.toggle("pending", false);

  if (archived) {
    elements.createPackActionState.textContent = (
      "Archived record: create-next-pack actions are unavailable until the pack is restored."
    );
    elements.localRecordActionState.textContent = (
      "Archived record: restore brings the pack back; permanent delete is local and final."
    );
    return;
  }

  elements.createPackActionState.textContent = pendingCount > 0
    ? "Pending answers available: resolve only pending, review/edit all answers, or generate an updated pack as needed."
    : "No pending answers: review/edit or generate updated pack creates a new numbered pack.";
  elements.localRecordActionState.textContent = (
    "Active record: copy the record path or archive this pack; use Backup data before major local changes."
  );
}

function renderDetail() {
  renderEmptyStateContext();
  if (isDraftMode()) {
    const draft = currentDraft();
    elements.emptyState.hidden = Boolean(draft);
    elements.detail.hidden = true;
    elements.draftDetail.hidden = !draft;
    if (!draft) return;

    setText("#draft-company-name", draft.company_name);
    setText(
      "#draft-saved-at",
      `Last saved ${new Date(draft.saved_at).toLocaleString()}`
    );
    setText("#draft-document-owner", draft.document_owner || "Not entered");
    setText("#draft-employee-count", draft.employee_count || "Not entered");
    setText(
      "#draft-answered-count",
      `${draft.answered_count} of ${draft.total_questions}`
    );
    setText(
      "#draft-progress-badge",
      `${draft.answered_count}/${draft.total_questions} answered`
    );
    return;
  }

  const assessment = currentAssessment();
  elements.emptyState.hidden = Boolean(assessment);
  elements.detail.hidden = !assessment;
  elements.draftDetail.hidden = true;
  if (!assessment) {
    state.packFiles = [];
    state.comparison = null;
    state.findings = null;
    state.selectedAssessmentSection = "overview";
    return;
  }

  setText("#company-name", assessment.company_name);
  setText("#folder-name", assessment.folder_name);
  setText("#document-owner", assessment.document_owner);
  setText("#employee-count", assessment.employee_count);
  setText("#generated-date", assessment.generated_date);
  setText("#review-due", assessment.review_due);
  setText("#schema-version", assessment.questionnaire_schema_version);
  setText("#yes-count", assessment.yes);
  setText("#no-count", assessment.no);
  setText("#pending-count", assessment.to_confirm);
  setText("#record-path", assessment.answers_path);
  setText("#record-status", isArchivedMode() ? "Archived" : "Valid");
  elements.versionBadge.hidden = isArchivedMode();
  elements.versionBadge.textContent = assessment.is_latest
    ? `Latest · Version ${assessment.history_version} of ${assessment.history_count}`
    : `Older · Version ${assessment.history_version} of ${assessment.history_count}`;

  const badge = document.querySelector("#pending-badge");
  if (isArchivedMode()) {
    badge.textContent = "Archived";
    badge.className = "pending-badge pending";
  } else {
    badge.textContent = assessment.to_confirm
      ? `${assessment.to_confirm} to confirm`
      : "No pending answers";
    badge.className = `pending-badge ${assessment.to_confirm ? "pending" : "complete"}`;
  }

  elements.generateButton.hidden = isArchivedMode();
  elements.reviewButton.hidden = isArchivedMode();
  elements.pendingReviewButton.hidden = (
    isArchivedMode()
    || assessment.to_confirm === 0
  );
  elements.archiveButton.hidden = isArchivedMode();
  elements.restoreButton.hidden = !isArchivedMode();
  elements.deleteButton.hidden = !isArchivedMode();
  renderActionStateNotes(assessment);
  renderAssessmentDetailSections();
  if (!isArchivedMode()) {
    loadSelectedAssessmentComparison(assessment.answers_path);
  }
  loadSelectedAssessmentFindings(
    assessment.answers_path,
    isArchivedMode()
  );
  loadSelectedAssessmentFiles(
    assessment.answers_path,
    isArchivedMode()
  );
}

function renderAttentionRecords() {
  elements.attentionPanel.hidden = !state.invalidRecords.length;
  elements.attentionList.replaceChildren();
  state.invalidRecords.forEach(record => {
    const row = document.createElement("div");
    row.className = "attention-record";
    const folder = document.createElement("strong");
    folder.textContent = record.folder_name;
    const error = document.createElement("span");
    error.textContent = record.error;
    row.append(folder, error);
    elements.attentionList.append(row);
  });
}

function setNavigationButtonState(button, selected) {
  if (!button) return;
  button.classList.toggle("selected", selected);
  if (selected) {
    button.setAttribute("aria-current", "page");
  } else {
    button.removeAttribute("aria-current");
  }
}

function renderCatalogueMode() {
  const archived = isArchivedMode();
  const drafts = isDraftMode();
  const workflow = isWorkflowMode();
  const activity = isActivityMode();
  const comparison = isComparisonMode();
  const due = isDueDateMode();
  const backupPreview = isBackupPreviewMode();
  const productReadiness = isProductReadinessMode();
  const localData = isLocalDataMode();
  const operatorChecklist = isOperatorChecklistMode();
  const operatorGuides = productReadiness || localData || operatorChecklist;
  const activeLibrary = !(
    archived || drafts || workflow || activity || comparison || due
    || backupPreview || operatorGuides
  );

  elements.catalogueEyebrow.textContent = operatorGuides
    ? "Guides"
    : backupPreview
    ? "Backup restore preview"
    : due
    ? "Due-date planning"
    : comparison
    ? "Assessment comparison"
    : activity
    ? "Workflow activity"
    : workflow
    ? "Workflow dashboard"
    : drafts
    ? "Assessment drafts"
    : archived
      ? "Archived assessments"
      : "Assessment library";
  elements.catalogueTitle.textContent = operatorGuides
    ? "Guides"
    : backupPreview
    ? "Preview portable backup contents"
    : due
    ? "Assessment and workflow due dates"
    : comparison
    ? "Compare assessment versions"
    : activity
    ? "Workflow activity register"
    : workflow
    ? "Active finding workflow"
    : drafts
    ? "Saved assessment drafts"
    : archived
      ? "Archived compliance assessments"
      : "Saved compliance assessments";
  elements.catalogueIntro.textContent = operatorGuides
    ? "Use Start here, Compliance route, and Local data & backups together before real business use."
    : backupPreview
    ? "Validate a backup ZIP, checksums, conflicts and records before using controlled restore execution."
    : due
    ? "Plan assessment reviews and active finding target dates across companies."
    : comparison
    ? "Review questionnaire response changes between any two active or archived packs for one company."
    : activity
    ? "Review timestamped workflow changes across active and archived assessment packs."
    : workflow
    ? "Review overdue, blocked, in-progress and unassigned findings across active assessment packs."
    : drafts
    ? "Resume incomplete questionnaires saved on this computer."
    : archived
      ? "Review archived assessment records and restore packs to the active library when needed."
      : "Review existing assessment records and generate a refreshed document pack.";

  elements.archiveViewButton.textContent = "Archived";
  elements.draftViewButton.textContent = "Drafts";
  elements.workflowViewButton.textContent = "Workflow";
  elements.activityViewButton.textContent = "Activity";
  elements.comparisonViewButton.textContent = "Compare versions";
  elements.dueViewButton.textContent = "Due dates";
  elements.restorePreviewViewButton.textContent = "Preview restore";
  elements.operatorGuidesViewButton.textContent = "Guides";
  elements.productReadinessViewButton.textContent = "Compliance route";
  elements.localDataViewButton.textContent = "Local data & backups";
  elements.operatorChecklistViewButton.textContent = "Start here";

  setNavigationButtonState(elements.assessmentLibraryButton, activeLibrary);
  setNavigationButtonState(elements.archiveViewButton, archived);
  setNavigationButtonState(elements.draftViewButton, drafts);
  setNavigationButtonState(elements.workflowViewButton, workflow);
  setNavigationButtonState(elements.activityViewButton, activity);
  setNavigationButtonState(elements.comparisonViewButton, comparison);
  setNavigationButtonState(elements.dueViewButton, due);
  setNavigationButtonState(elements.restorePreviewViewButton, backupPreview);
  setNavigationButtonState(elements.operatorGuidesViewButton, operatorGuides);
  setNavigationButtonState(elements.productReadinessViewButton, productReadiness);
  setNavigationButtonState(elements.localDataViewButton, localData);
  setNavigationButtonState(elements.operatorChecklistViewButton, operatorChecklist);

  elements.newAssessmentButton.hidden = false;
  elements.archiveViewButton.hidden = false;
  elements.draftViewButton.hidden = false;
  elements.summaryStrip.hidden = (
    drafts || workflow || activity || comparison || due || backupPreview
    || operatorGuides
  );
  elements.assessmentWorkspace.hidden = (
    workflow || activity || comparison || due || backupPreview
    || operatorGuides
  );
  elements.workflowDashboard.hidden = !workflow;
  elements.workflowActivityRegister.hidden = !activity;
  elements.assessmentVersionComparison.hidden = !comparison;
  elements.dueDatePlanner.hidden = !due;
  elements.backupRestorePreview.hidden = !backupPreview;
  elements.operatorGuidesPanel.hidden = !operatorGuides;
  elements.productReadinessPanel.hidden = !productReadiness;
  elements.localDataPanel.hidden = !localData;
  elements.operatorChecklistPanel.hidden = !operatorChecklist;
  elements.searchInput.disabled = (
    workflow || activity || comparison || due || backupPreview
    || operatorGuides
  );
}

async function loadCatalogue({ preserveSelection = true } = {}) {
  clearNotice();
  renderCatalogueMode();
  if (isWorkflowMode()) {
    await loadWorkflowDashboard();
    return;
  }
  if (isActivityMode()) {
    await loadWorkflowActivityRegister();
    return;
  }
  if (isComparisonMode()) {
    await loadAssessmentVersionComparisonCatalogue();
    return;
  }
  if (isDueDateMode()) {
    await loadDueDatePlanner();
    return;
  }
  if (isBackupPreviewMode()) {
    renderBackupRestorePreview();
    return;
  }
  if (isProductReadinessMode() || isLocalDataMode() || isOperatorChecklistMode()) {
    state.invalidRecords = [];
    renderAttentionRecords();
    return;
  }
  elements.refreshButton.disabled = true;
  elements.resultCount.textContent = "Loading...";

  try {
    if (isDraftMode()) {
      const response = await fetch("/api/assessment-drafts", {
        cache: "no-store",
      });
      const payload = await response.json();
      if (!response.ok) throw new Error(
        payload.error || "Could not load assessment drafts."
      );
      const previousDraft = preserveSelection ? state.selectedDraftId : null;
      state.drafts = payload.drafts;
      state.invalidRecords = payload.invalid_drafts.map(draft => ({
        folder_name: draft.filename,
        error: draft.error,
      }));
      state.selectedDraftId = state.drafts.some(
        draft => draft.draft_id === previousDraft
      )
        ? previousDraft
        : state.drafts[0]?.draft_id || null;
      state.assessments = [];
      state.selectedPath = null;
      renderAssessmentList();
      renderDetail();
      renderAttentionRecords();
      return;
    }

    const endpoint = isArchivedMode()
      ? "/api/assessments/archived"
      : "/api/assessments";
    const response = await fetch(endpoint, { cache: "no-store" });
    const payload = await response.json();
    if (!response.ok) throw new Error(payload.error || "Could not load assessments.");

    const previousSelection = preserveSelection ? state.selectedPath : null;
    state.assessments = payload.assessments;
    state.invalidRecords = payload.invalid_records;
    const defaultAssessment = isArchivedMode()
      ? state.assessments[0]
      : state.assessments.find(item => item.is_latest)
        || state.assessments[0];
    state.selectedPath = state.assessments.some(item => item.answers_path === previousSelection)
      ? previousSelection
      : defaultAssessment?.answers_path || null;

    elements.packCount.textContent = payload.pack_folder_count;
    elements.validCount.textContent = payload.valid_count;
    elements.attentionCount.textContent = payload.attention_count;
    renderAssessmentList();
    renderDetail();
    renderAttentionRecords();
  } catch (error) {
    state.assessments = [];
    state.drafts = [];
    state.invalidRecords = [];
    state.selectedPath = null;
    state.selectedDraftId = null;
    renderAssessmentList();
    renderDetail();
    renderAttentionRecords();
    showNotice(error.message, true);
  } finally {
    elements.refreshButton.disabled = false;
  }
}

async function showActiveLibrary() {
  state.catalogueMode = "active";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  elements.searchInput.value = "";
  await loadCatalogue({ preserveSelection: false });
}

async function toggleArchiveView() {
  state.catalogueMode = isArchivedMode() ? "active" : "archived";
  state.selectedPath = null;
  state.search = "";
  elements.searchInput.value = "";
  await loadCatalogue({ preserveSelection: false });
}

async function toggleDraftView() {
  state.catalogueMode = isDraftMode() ? "active" : "drafts";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  elements.searchInput.value = "";
  await loadCatalogue({ preserveSelection: false });
}

async function toggleWorkflowView() {
  state.catalogueMode = isWorkflowMode() ? "active" : "workflow";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  elements.searchInput.value = "";
  await loadCatalogue({ preserveSelection: false });
}

async function toggleActivityView() {
  state.catalogueMode = isActivityMode() ? "active" : "activity";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  elements.searchInput.value = "";
  await loadCatalogue({ preserveSelection: false });
}

async function toggleComparisonView() {
  state.catalogueMode = isComparisonMode() ? "active" : "comparison";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  elements.searchInput.value = "";
  await loadCatalogue({ preserveSelection: false });
}

async function toggleDueDateView() {
  state.catalogueMode = isDueDateMode() ? "active" : "due";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  elements.searchInput.value = "";
  await loadCatalogue({ preserveSelection: false });
}

async function selectAssessmentComparisonCompany() {
  state.assessmentComparisonFilters.company = (
    elements.assessmentComparisonCompany.value
  );
  populateAssessmentComparisonVersionOptions({ reset: true });
  clearNotice();
  await loadSelectedAssessmentVersionComparison();
}

async function selectAssessmentComparisonVersions(changedControl) {
  const filters = state.assessmentComparisonFilters;
  filters.previousKey = elements.assessmentComparisonPrevious.value;
  filters.currentKey = elements.assessmentComparisonCurrent.value;
  if (filters.previousKey === filters.currentKey) {
    const keys = (selectedAssessmentComparisonGroup()?.assessments || [])
      .map(assessmentComparisonKey);
    if (changedControl === "previous") {
      filters.currentKey = keys.find(key => key !== filters.previousKey) || "";
    } else {
      filters.previousKey = keys.find(key => key !== filters.currentKey) || "";
    }
    elements.assessmentComparisonPrevious.value = filters.previousKey;
    elements.assessmentComparisonCurrent.value = filters.currentKey;
  }
  clearNotice();
  await loadSelectedAssessmentVersionComparison();
}

async function generateSelectedAssessment() {
  const assessment = currentAssessment();
  if (!assessment) return;

  clearNotice();
  elements.generateButton.disabled = true;
  elements.generateButton.textContent = "Generating pack...";

  try {
    const response = await fetch("/api/generate", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ answers_path: assessment.answers_path }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(payload.error || "Generation failed.");
    await loadCatalogue({ preserveSelection: false });
    const generatedPath = generatedAssessmentSelectionPath(payload);
    if (generatedPath) {
      selectAssessment(generatedPath, "files");
    } else {
      state.selectedAssessmentSection = "files";
      renderDetail();
    }
    showGenerationSuccessNotice(payload);
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.generateButton.disabled = false;
    elements.generateButton.textContent = "Generate updated pack";
  }
}

async function downloadPortableBackup() {
  clearNotice();
  elements.backupButton.disabled = true;
  elements.backupButton.textContent = "Building backup...";
  try {
    const response = await fetch("/api/backup", { cache: "no-store" });
    if (!response.ok) {
      const payload = await response.json();
      throw new Error(payload.error || "The backup could not be created.");
    }
    const disposition = response.headers.get("Content-Disposition") || "";
    const filenameMatch = disposition.match(/filename\*=UTF-8''([^;]+)/i);
    const filename = filenameMatch
      ? decodeURIComponent(filenameMatch[1])
      : "ai_compliance_copilot_backup.zip";
    const blob = await response.blob();
    const downloadUrl = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = downloadUrl;
    link.download = filename;
    document.body.append(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(downloadUrl);
    showNotice(`Created portable backup ${filename}.`);
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.backupButton.disabled = false;
    elements.backupButton.textContent = "Backup data";
  }
}

function showBackupPreviewView() {
  state.catalogueMode = "backup-preview";
  state.selectedPath = null;
  state.selectedDraftId = null;
  renderCatalogueMode();
  renderBackupRestorePreview();
}

function showProductReadinessView() {
  state.catalogueMode = "product-readiness";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  state.invalidRecords = [];
  elements.searchInput.value = "";
  clearNotice();
  renderCatalogueMode();
  renderAttentionRecords();
}

function showLocalDataView() {
  state.catalogueMode = "local-data";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  state.invalidRecords = [];
  elements.searchInput.value = "";
  clearNotice();
  renderCatalogueMode();
  renderAttentionRecords();
}

function showOperatorChecklistView() {
  state.catalogueMode = "operator-checklist";
  state.selectedPath = null;
  state.selectedDraftId = null;
  state.search = "";
  state.invalidRecords = [];
  elements.searchInput.value = "";
  clearNotice();
  renderCatalogueMode();
  renderAttentionRecords();
}

function resetBackupRestorePreview({ showMessage = true } = {}) {
  state.backupPreview = null;
  state.backupRestoreFile = null;
  state.backupRestoreConfirmation = "";
  state.backupRestoreConfirmationResetReason = "";
  state.backupRestoreCompleted = false;
  state.selectedBackupRestoreStrategy = "";
  elements.backupRestoreFile.value = "";
  elements.backupRestoreConfirmation.value = "";
  renderBackupRestorePreview();
  if (showMessage) {
    showNotice(
      "Cleared selected backup ZIP and restore preview. Choose another backup ZIP to preview."
    );
  }
}

function backupPreviewStatusClass(value) {
  if (value === "Failed") return "failed";
  if (value) return "warning";
  return "";
}

function backupPreviewReadinessClass(preview) {
  if (preview?.readiness_level === "blocked") return "failed";
  if (preview?.readiness_level === "warning") return "warning";
  if (preview?.readiness_level === "ready") return "ready";
  return "";
}

function backupPreviewRestorePlanClass(planItem) {
  if (planItem?.status === "blocked") return "failed";
  if (planItem?.status === "warning") return "warning";
  if (planItem?.status === "ready") return "ready";
  return "";
}

function backupPreviewConflictStrategyClass(strategy) {
  if (strategy?.status === "blocked") return "failed";
  if (strategy?.status === "warning") return "warning";
  if (strategy?.status === "ready") return "ready";
  return "";
}

function backupPreviewSafetyClass(value) {
  const status = value?.status_level || value?.status || value;
  if (status === "blocked") return "failed";
  if (status === "warning") return "warning";
  if (status === "pending" || status === "disabled") return "pending";
  if (status === "ready" || status === "passed") return "ready";
  return "";
}

function backupPreviewSafetyCounts(summary) {
  if (!summary) return "No safety checklist summary was returned.";
  return (
    `Passed: ${summary.passed_count || 0}; `
    + `needs decision: ${summary.warning_count || 0}; `
    + `blocked: ${summary.blocked_count || 0}; `
    + `pending: ${summary.pending_count || 0}.`
  );
}

function backupPreviewConflictStrategyCounts(strategy) {
  const counts = strategy?.action_counts || {};
  return (
    `Creates: ${counts.would_create || 0}; `
    + `numbered copies: ${counts.would_create_numbered || 0}; `
    + `skips: ${counts.would_skip || 0}; `
    + `blocked: ${counts.blocked || 0}.`
  );
}

function backupPreviewSelectedStrategy(preview = state.backupPreview) {
  const simulations = preview?.conflict_strategy_simulations || [];
  if (!simulations.length) return null;
  const selectedKey = (
    state.selectedBackupRestoreStrategy
    || preview?.restore_strategy_selection?.selected_strategy
    || simulations[0].strategy
  );
  return (
    simulations.find(strategy => strategy.strategy === selectedKey)
    || simulations[0]
  );
}

function selectBackupPreviewConflictStrategy(strategyKey) {
  const previousStrategy = state.selectedBackupRestoreStrategy;
  state.selectedBackupRestoreStrategy = strategyKey;
  state.backupRestoreConfirmation = "";
  state.backupRestoreConfirmationResetReason = previousStrategy
    && previousStrategy !== strategyKey
      ? "The restore approach changed. Retype RESTORE BACKUP to confirm this selected approach."
      : "";
  state.backupRestoreCompleted = false;
  elements.backupRestoreConfirmation.value = "";
  renderBackupRestorePreview();
}

function backupRestoreConfirmationPhrase(preview = state.backupPreview) {
  return (
    preview?.future_restore_confirmation?.confirmation_phrase
    || "RESTORE BACKUP"
  );
}

function backupRestorePreflightStatus() {
  const preview = state.backupPreview;
  const selectedStrategy = backupPreviewSelectedStrategy(preview);
  const confirmationPhrase = backupRestoreConfirmationPhrase(preview);
  const problems = [];

  if (!preview) {
    problems.push("Preview a portable backup ZIP before restoring.");
  }
  if (!state.backupRestoreFile) {
    problems.push("The previewed backup ZIP must still be selected for restore.");
  }
  if (!selectedStrategy) {
    problems.push("Choose a restore approach before restoring.");
  } else if (selectedStrategy.status === "blocked") {
    problems.push("The selected restore approach is blocked by the preview.");
  }
  if ((preview?.integrity_issues || []).length) {
    problems.push("Resolve backup integrity issues before restoring.");
  }
  if ((preview?.invalid_records || []).length) {
    problems.push("Resolve invalid questionnaire records before restoring.");
  }
  if (state.backupRestoreCompleted) {
    problems.push("This backup restore has already completed in this session.");
  }

  const confirmationMatches = (
    state.backupRestoreConfirmation === confirmationPhrase
  );
  const ready = problems.length === 0 && confirmationMatches;
  return {
    ready,
    problems,
    confirmationMatches,
    confirmationPhrase,
    selectedStrategy,
  };
}

function renderBackupRestoreExecutionPanel() {
  const preview = state.backupPreview;
  const status = backupRestorePreflightStatus();
  elements.backupRestoreExecution.hidden = !preview;
  if (!preview) {
    elements.backupRestoreSelectedFile.textContent = "No preview loaded";
    elements.backupRestoreSelectedStrategy.textContent = "Not selected";
    elements.backupRestoreRequiredPhrase.textContent = "RESTORE BACKUP";
    elements.backupRestoreConfirmation.value = "";
    elements.backupRestoreConfirmation.disabled = true;
    elements.backupRestoreExecuteButton.disabled = true;
    elements.backupRestoreExecutionStatus.textContent = (
      "Restore is disabled until preview and confirmation checks pass."
    );
    return;
  }

  if (
    elements.backupRestoreConfirmation.value
    !== state.backupRestoreConfirmation
  ) {
    elements.backupRestoreConfirmation.value = state.backupRestoreConfirmation;
  }
  elements.backupRestoreSelectedFile.textContent = (
    state.backupRestoreFile?.name || preview.filename || "No backup file retained"
  );
  elements.backupRestoreSelectedStrategy.textContent = (
    status.selectedStrategy?.strategy_label || "Not selected"
  );
  elements.backupRestoreRequiredPhrase.textContent = status.confirmationPhrase;
  elements.backupRestoreConfirmation.disabled = (
    !state.backupRestoreFile || state.backupRestoreCompleted
  );
  elements.backupRestoreExecuteButton.disabled = !status.ready;
  elements.backupRestoreExecution.classList.toggle("ready", status.ready);
  elements.backupRestoreExecution.classList.toggle(
    "blocked",
    status.problems.length > 0
  );

  if (state.backupRestoreCompleted) {
    elements.backupRestoreExecutionStatus.textContent = (
      "Restore completed. Select and preview another backup ZIP before running another restore."
    );
  } else if (status.ready) {
    elements.backupRestoreExecutionStatus.textContent = (
      `Ready to restore ${elements.backupRestoreSelectedFile.textContent} using ${elements.backupRestoreSelectedStrategy.textContent}. Existing targets will not be overwritten by default.`
    );
  } else if (
    state.backupRestoreConfirmationResetReason
    && !state.backupRestoreConfirmation
  ) {
    elements.backupRestoreExecutionStatus.textContent = (
      state.backupRestoreConfirmationResetReason
    );
  } else if (!status.confirmationMatches) {
    elements.backupRestoreExecutionStatus.textContent = (
      `Type ${status.confirmationPhrase} exactly after reviewing the preview and selected restore approach.`
    );
  } else {
    elements.backupRestoreExecutionStatus.textContent = status.problems[0];
  }
}

function renderBackupPreviewStrategySelection(preview) {
  const section = document.createElement("section");
  section.className = "backup-preview-section backup-preview-strategy-selection";
  const heading = document.createElement("h3");
  heading.textContent = "Choose restore approach";
  const intro = document.createElement("p");
  intro.className = "backup-preview-section-intro";
  intro.textContent = (
    preview.restore_strategy_selection?.selection_prompt
    || "Choose which read-only restore approach to model before any future restore writes are enabled."
  );
  const selectedStrategy = backupPreviewSelectedStrategy(preview);
  const simulations = preview.conflict_strategy_simulations || [];
  const options = document.createElement("div");
  options.className = "backup-preview-strategy-options";
  simulations.forEach(strategy => {
    const option = document.createElement("label");
    option.className = "backup-preview-strategy-option";
    option.classList.toggle(
      "selected",
      selectedStrategy?.strategy === strategy.strategy
    );
    const input = document.createElement("input");
    input.type = "radio";
    input.name = "backup-restore-strategy";
    input.value = strategy.strategy;
    input.checked = selectedStrategy?.strategy === strategy.strategy;
    input.addEventListener("change", () => {
      if (input.checked) {
        selectBackupPreviewConflictStrategy(strategy.strategy);
      }
    });

    const body = document.createElement("span");
    const title = document.createElement("strong");
    title.textContent = strategy.strategy_label;
    const detail = document.createElement("span");
    detail.textContent = (
      `${strategy.strategy_summary} `
      + backupPreviewConflictStrategyCounts(strategy)
    );
    body.append(title, detail);
    option.append(input, body);
    options.append(option);
  });

  const selectedList = document.createElement("div");
  selectedList.className = "backup-preview-list backup-preview-selected-strategy";
  if (selectedStrategy) {
    selectedList.append(
      buildBackupPreviewRow(
        "Selected restore approach",
        `${selectedStrategy.strategy_summary} ${backupPreviewConflictStrategyCounts(selectedStrategy)}`,
        selectedStrategy.strategy_label,
        backupPreviewConflictStrategyClass(selectedStrategy)
      )
    );
  }
  selectedList.append(
    buildBackupPreviewRow(
      "Restore execution",
      "This choice changes the preview and report only. No files will be restored, moved, deleted, extracted, or overwritten.",
      "Disabled",
      "warning"
    )
  );

  section.append(heading, intro, options, selectedList);
  return section;
}

function backupPreviewFutureConfirmationStatus(preview, selectedStrategy) {
  const confirmation = preview?.future_restore_confirmation || {};
  const actionCounts = selectedStrategy?.action_counts || {};
  if ((preview?.integrity_issues || []).length || (preview?.invalid_records || []).length) {
    return {
      status: "Blocked by preview issues",
      status_level: "blocked",
      explanation: (
        "A future restore cannot proceed while integrity issues or invalid questionnaire records are present."
      ),
    };
  }
  if (selectedStrategy?.status === "blocked") {
    return {
      status: "Blocked by selected approach",
      status_level: "blocked",
      explanation: (
        "The selected approach would stop before writing files. This remains safe for preview, but a future restore would need a different confirmed approach or conflict-free backup."
      ),
    };
  }
  if (actionCounts.would_skip) {
    return {
      status: "Needs skipped-item confirmation",
      status_level: "warning",
      explanation: (
        "The selected approach would leave existing local data in place and skip conflicting backup items. A future restore would need explicit confirmation that those backup items should not be restored from this ZIP."
      ),
    };
  }
  if (actionCounts.would_create_numbered) {
    return {
      status: "Needs numbered-copy confirmation",
      status_level: "warning",
      explanation: (
        "The selected approach would preserve existing local data and restore conflicting backup items into next available numbered targets. A future restore would need explicit confirmation of those new target names before writing files."
      ),
    };
  }
  if (selectedStrategy?.status === "warning" || preview?.conflict_count) {
    return {
      status: "Needs conflict confirmation",
      status_level: "warning",
      explanation: (
        "A future restore would need explicit confirmation of how restore-target conflicts should be handled before any files are written."
      ),
    };
  }
  return {
    status: confirmation.status || "Preview ready - restore disabled",
    status_level: confirmation.status_level || "ready",
    explanation: (
      confirmation.explanation
      || "The backup is ready for future restore confirmation, but restore execution is still disabled."
    ),
  };
}

function backupPreviewFutureConfirmationItems(preview) {
  const confirmation = preview?.future_restore_confirmation || {};
  const selectedStrategy = backupPreviewSelectedStrategy(preview);
  const actionCounts = selectedStrategy?.action_counts || {};
  const confirmationPhrase = confirmation.confirmation_phrase || "RESTORE BACKUP";
  const items = [
    {
      label: "Restore backup button",
      detail: (
        `${confirmation.restore_button_label || "Restore backup"} stays disabled in this version. Preview restore cannot write, move, delete, extract, or overwrite files.`
      ),
      status: "pending",
      status_label: "Disabled",
    },
    {
      label: "Preview report reviewed",
      detail: (
        "A future restore should require the preview report to be reviewed before any files are written."
      ),
      status: "pending",
      status_label: "Pending confirmation",
    },
    {
      label: "Selected restore approach confirmed",
      detail: selectedStrategy
        ? `${selectedStrategy.strategy_label}: ${selectedStrategy.strategy_summary} ${backupPreviewConflictStrategyCounts(selectedStrategy)}`
        : "No selected restore approach is available for confirmation.",
      status: selectedStrategy?.status || "blocked",
      status_label: selectedStrategy?.strategy_label || "No approach selected",
    },
    {
      label: "Existing local data protected",
      detail: (
        "A future restore should confirm that existing active packs, archived packs, and draft files will not be overwritten by default."
      ),
      status: "pending",
      status_label: "Pending confirmation",
    },
    {
      label: "Typed confirmation required",
      detail: (
        `A future write-capable restore should require an explicit confirmation such as typing ${confirmationPhrase}.`
      ),
      status: "pending",
      status_label: "Pending confirmation",
    },
  ];

  if ((preview?.integrity_issues || []).length || (preview?.invalid_records || []).length) {
    items.splice(1, 0, {
      label: "Preview blockers resolved",
      detail: (
        "Integrity issues or invalid questionnaire records must be resolved before a future restore can proceed."
      ),
      status: "blocked",
      status_label: "Blocked",
    });
  }

  if (preview?.conflict_count) {
    items.splice(3, 0, {
      label: "Conflict handling confirmed",
      detail: (
        "The preview found local restore-target conflicts. A future restore should require a confirmed conflict approach before writing files."
      ),
      status: "warning",
      status_label: "Needs review",
    });
  }

  if (actionCounts.would_create_numbered) {
    items.splice(4, 0, {
      label: "Numbered-copy targets reviewed",
      detail: (
        "The selected approach would create one or more numbered restore targets. A future restore should ask you to review those names before writing files."
      ),
      status: "warning",
      status_label: "Needs review",
    });
  }

  if (actionCounts.would_skip) {
    items.splice(4, 0, {
      label: "Skipped backup items accepted",
      detail: (
        "The selected approach would skip one or more backup items. A future restore should ask you to confirm that skipped items are acceptable."
      ),
      status: "warning",
      status_label: "Needs review",
    });
  }

  return items;
}

function renderBackupPreviewFutureRestoreConfirmation(preview) {
  const confirmation = preview.future_restore_confirmation || {};
  const selectedStrategy = backupPreviewSelectedStrategy(preview);
  const confirmationStatus = backupPreviewFutureConfirmationStatus(
    preview,
    selectedStrategy
  );
  const section = document.createElement("section");
  section.className = "backup-preview-section backup-preview-future-confirmation";
  const heading = document.createElement("h3");
  heading.textContent = confirmation.title || "Future restore confirmation";
  const intro = document.createElement("p");
  intro.className = "backup-preview-section-intro";
  intro.textContent = (
    "This is the confirmation model a future Restore backup button would use before writing files. It is still preview-only and cannot restore anything."
  );

  const action = document.createElement("div");
  action.className = "backup-preview-future-confirmation-action";
  const restoreButton = document.createElement("button");
  restoreButton.type = "button";
  restoreButton.className = "primary-button";
  restoreButton.disabled = true;
  restoreButton.textContent = confirmation.restore_button_label || "Restore backup";
  const actionText = document.createElement("span");
  actionText.textContent = (
    `${confirmationStatus.status}. Restore execution is disabled and no files have been restored.`
  );
  action.append(restoreButton, actionText);

  const list = document.createElement("div");
  list.className = "backup-preview-list";
  list.append(
    buildBackupPreviewRow(
      "Future restore status",
      confirmationStatus.explanation,
      confirmationStatus.status,
      backupPreviewSafetyClass(confirmationStatus)
    )
  );
  backupPreviewFutureConfirmationItems(preview).forEach(item => {
    list.append(
      buildBackupPreviewRow(
        item.label,
        item.detail,
        item.status_label,
        backupPreviewSafetyClass(item)
      )
    );
  });

  section.append(heading, intro, action, list);
  return section;
}


function buildBackupPreviewSummaryItem(label, value) {
  const item = document.createElement("div");
  const name = document.createElement("span");
  name.textContent = label;
  const count = document.createElement("strong");
  count.textContent = value;
  item.append(name, count);
  return item;
}

function renderBackupPreviewSection(title, rows, emptyMessage, buildRow) {
  const section = document.createElement("section");
  section.className = "backup-preview-section";
  const heading = document.createElement("h3");
  heading.textContent = title;
  section.append(heading);

  if (!rows.length) {
    const empty = document.createElement("div");
    empty.className = "backup-preview-empty";
    empty.textContent = emptyMessage;
    section.append(empty);
    return section;
  }

  const list = document.createElement("div");
  list.className = "backup-preview-list";
  rows.forEach(row => {
    list.append(buildRow(row));
  });
  section.append(list);
  return section;
}

function buildBackupPreviewRow(title, detail, statusText = "", statusClass = "") {
  const row = document.createElement("div");
  row.className = "backup-preview-row";
  const identity = document.createElement("div");
  const titleElement = document.createElement("strong");
  titleElement.textContent = title;
  const detailElement = document.createElement("span");
  detailElement.textContent = detail;
  identity.append(titleElement, detailElement);

  const status = document.createElement("div");
  const statusDetail = document.createElement("span");
  statusDetail.textContent = statusText || "Ready for preview only.";
  status.append(statusDetail);
  if (statusText) {
    const badge = document.createElement("span");
    badge.className = `backup-preview-status ${statusClass}`.trim();
    badge.textContent = statusText;
    status.append(badge);
  }

  row.append(identity, status);
  return row;
}

function renderBackupRestorePreview(message = null) {
  elements.backupRestoreSummary.replaceChildren();
  elements.backupRestoreContent.replaceChildren();
  const preview = state.backupPreview;
  elements.backupRestoreReportButton.disabled = Boolean(message) || !preview;
  elements.backupRestoreClearButton.disabled = !preview;
  renderBackupRestoreExecutionPanel();

  if (message || !preview) {
    const empty = document.createElement("div");
    empty.className = "backup-preview-message";
    empty.textContent = (
      message
      || "Choose a portable backup ZIP to check whether this app's saved assessments, drafts, archived packs, and workflow records could be restored later. Preview restore is read-only and will not restore files."
    );
    elements.backupRestoreContent.append(empty);
    return;
  }

  [
    ["Readiness", preview.readiness_status || "Preview only"],
    ["Schema", preview.backup_schema_version],
    ["Verified files", `${preview.verified_file_count}/${preview.file_count}`],
    ["Active packs", preview.active_pack_count],
    ["Archived packs", preview.archived_pack_count],
    ["Draft files", preview.draft_count],
    ["Conflicts", preview.conflict_count],
    ["Plan items", preview.restore_plan_count ?? (preview.restore_plan || []).length],
    [
      "Strategies",
      preview.conflict_strategy_count
        ?? (preview.conflict_strategy_simulations || []).length,
    ],
    [
      "Selected approach",
      backupPreviewSelectedStrategy(preview)?.strategy_label || "Not selected",
    ],
  ].forEach(([label, value]) => {
    elements.backupRestoreSummary.append(
      buildBackupPreviewSummaryItem(label, value)
    );
  });

  const source = document.createElement("div");
  source.className = "backup-preview-section";
  const sourceTitle = document.createElement("h3");
  sourceTitle.textContent = "Backup summary";
  const sourceList = document.createElement("div");
  sourceList.className = "backup-preview-list";
  sourceList.append(
    buildBackupPreviewRow(
      "Restore readiness",
      preview.readiness_explanation
        || "Preview loaded. No restore writes are available.",
      preview.readiness_status || "Preview only",
      backupPreviewReadinessClass(preview)
    )
  );
  sourceList.append(
    buildBackupPreviewRow(
      "Restore safety checklist",
      `${preview.restore_safety_summary?.explanation || "Safety checks are not available."} ${backupPreviewSafetyCounts(preview.restore_safety_summary)}`,
      preview.restore_safety_summary?.status || "Checklist unavailable",
      backupPreviewSafetyClass(preview.restore_safety_summary)
    )
  );
  sourceList.append(
    buildBackupPreviewRow(
      "Restore approach selection",
      `${preview.conflict_strategy_count ?? 0} read-only strategies model block, skip, and next-available-name handling for ${preview.conflict_count || 0} conflicts. Selected: ${backupPreviewSelectedStrategy(preview)?.strategy_label || "Not selected"}.`,
      preview.restore_strategy_selection?.selection_status || "Preview only",
      backupPreviewSafetyClass(preview.restore_strategy_selection?.selection_level)
    )
  );
  sourceList.append(
    buildBackupPreviewRow(
      "Future restore confirmation",
      preview.future_restore_confirmation?.explanation
        || "Future restore confirmation guidance is not available.",
      preview.future_restore_confirmation?.status || "Preview only",
      backupPreviewSafetyClass(preview.future_restore_confirmation)
    )
  );
  sourceList.append(
    buildBackupPreviewRow(
      preview.filename || "Uploaded backup",
      `Created ${preview.created_at}. Checksums ${preview.checksum_status.toLowerCase()}.`,
      preview.preview_only
        ? "Preview only - no restore writes are available"
        : "",
      "warning"
    )
  );
  source.append(sourceTitle, sourceList);
  elements.backupRestoreContent.append(source);

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Restore safety checklist",
      preview.restore_safety_checklist || [],
      "No restore safety checklist was returned for this backup.",
      check => buildBackupPreviewRow(
        check.label,
        check.detail,
        check.status_label,
        backupPreviewSafetyClass(check)
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewStrategySelection(preview)
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewFutureRestoreConfirmation(preview)
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Selected restore approach item outcomes",
      backupPreviewSelectedStrategy(preview)?.items || [],
      "No selected restore approach item outcomes were returned for this backup.",
      item => buildBackupPreviewRow(
        item.source_path,
        `Target: ${item.target_path}. Original target: ${item.original_target_path}. ${item.reason}`,
        item.action_label,
        backupPreviewConflictStrategyClass(item)
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Dry-run restore plan",
      preview.restore_plan || [],
      "No restore-plan items were produced for this backup.",
      planItem => buildBackupPreviewRow(
        planItem.source_path,
        `${planItem.action_label}. Target: ${planItem.target_path}. ${planItem.reason}`,
        planItem.action_label,
        backupPreviewRestorePlanClass(planItem)
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Conflict strategy simulations",
      preview.conflict_strategy_simulations || [],
      "No conflict strategy simulations were produced for this backup.",
      strategy => buildBackupPreviewRow(
        strategy.strategy_label,
        `${strategy.strategy_summary} ${backupPreviewConflictStrategyCounts(strategy)}`,
        "Read-only simulation",
        backupPreviewConflictStrategyClass(strategy)
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Active packs",
      preview.active_packs || [],
      "No active packs are listed in this backup.",
      pack => buildBackupPreviewRow(
        pack.folder_name,
        `${pack.file_count} files · ${formatFileSize(pack.size_bytes)}`,
        pack.conflict ? "Folder conflict" : pack.questionnaire_status,
        pack.conflict || !pack.has_questionnaire_record ? "warning" : ""
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Archived packs",
      preview.archived_packs || [],
      "No archived packs are listed in this backup.",
      pack => buildBackupPreviewRow(
        pack.folder_name,
        `${pack.file_count} files · ${formatFileSize(pack.size_bytes)}`,
        pack.conflict ? "Folder conflict" : pack.questionnaire_status,
        pack.conflict || !pack.has_questionnaire_record ? "warning" : ""
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Draft files",
      preview.drafts || [],
      "No saved drafts are listed in this backup.",
      draft => buildBackupPreviewRow(
        draft.path,
        `${draft.file_count} file · ${formatFileSize(draft.size_bytes)}`,
        draft.conflict ? "Draft conflict" : "Draft file present",
        draft.conflict ? "warning" : ""
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Records needing attention",
      preview.invalid_records || [],
      "No invalid questionnaire records were detected by the preview.",
      record => buildBackupPreviewRow(
        `${record.record_scope}/${record.folder_name}`,
        record.error,
        "Needs attention",
        "failed"
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Conflicts",
      preview.conflicts || [],
      "No restore target conflicts were found.",
      conflict => buildBackupPreviewRow(
        conflict.path,
        conflict.reason,
        "Conflict",
        "warning"
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Integrity issues",
      preview.integrity_issues || [],
      "All manifest-listed files matched their recorded size and checksum.",
      issue => buildBackupPreviewRow(
        issue.path,
        issue.issue,
        "Integrity issue",
        backupPreviewStatusClass(preview.checksum_status)
      )
    )
  );

  elements.backupRestoreContent.append(
    renderBackupPreviewSection(
      "Excluded source items",
      preview.excluded || [],
      "The backup manifest did not list excluded source items.",
      excluded => buildBackupPreviewRow(
        excluded.path,
        excluded.reason,
        "Excluded from backup",
        "warning"
      )
    )
  );
}

function backupPreviewReportValue(value, fallback = "Not recorded") {
  if (value === null || value === undefined || value === "") return fallback;
  return String(value);
}

function backupPreviewReportSize(sizeBytes) {
  return formatFileSize(Number(sizeBytes) || 0);
}

function backupPreviewReportRow(title, detail, statusText = "") {
  const status = statusText ? ` [${statusText}]` : "";
  return `- ${backupPreviewReportValue(title)}: ${backupPreviewReportValue(detail)}${status}`;
}

function appendBackupPreviewReportSection(
  lines,
  title,
  rows,
  emptyMessage,
  buildRow
) {
  lines.push("", `${title}:`);
  if (!rows.length) {
    lines.push(`- ${emptyMessage}`);
    return;
  }
  rows.forEach(row => {
    lines.push(buildRow(row));
  });
}

function buildBackupRestorePreviewReport() {
  const preview = state.backupPreview;
  const selectedStrategy = backupPreviewSelectedStrategy(preview);
  const lines = [
    "AI Compliance Copilot backup restore preview report",
    `Report generated: ${new Date().toISOString()}`,
    "Preview only: no files were restored, moved, deleted, extracted, or overwritten.",
    "Use this report when moving this app to another computer or checking recovery from a lost or damaged generated_documents folder.",
    "Backup data creates the ZIP. Preview restore checks the ZIP and shows what a future restore would do.",
    "Plain English: readiness means whether the backup is usable; dry-run plan means what would be restored; conflict strategy means what would happen when names already exist; selected restore approach means the read-only strategy currently chosen for the report; future restore confirmation means what the future Restore backup button would ask you to confirm before writing files.",
    "",
    "Backup summary:",
    `- Backup filename: ${backupPreviewReportValue(preview.filename || "Uploaded backup")}`,
    `- Backup schema version: ${backupPreviewReportValue(preview.backup_schema_version)}`,
    `- Backup created at: ${backupPreviewReportValue(preview.created_at)}`,
    `- Restore readiness: ${backupPreviewReportValue(preview.readiness_status, "Preview only")}`,
    `- Readiness explanation: ${backupPreviewReportValue(preview.readiness_explanation, "No readiness explanation recorded.")}`,
    `- Readiness recommendation: ${backupPreviewReportValue(preview.readiness_recommendation, "No readiness recommendation recorded.")}`,
    `- Restore safety status: ${backupPreviewReportValue(preview.restore_safety_summary?.status, "Checklist unavailable")}`,
    `- Restore safety explanation: ${backupPreviewReportValue(preview.restore_safety_summary?.explanation, "No safety explanation recorded.")}`,
    `- Restore safety checks: ${backupPreviewSafetyCounts(preview.restore_safety_summary)}`,
    `- Restore execution enabled: ${preview.restore_safety_summary?.restore_execution_enabled ? "Yes" : "No"}`,
    `- Explicit confirmation required: ${preview.restore_safety_summary?.explicit_confirmation_required ? "Yes" : "No"}`,
    `- Checksum status: ${backupPreviewReportValue(preview.checksum_status)}`,
    `- Verified files: ${backupPreviewReportValue(preview.verified_file_count)}/${backupPreviewReportValue(preview.file_count)}`,
    `- Active packs: ${backupPreviewReportValue(preview.active_pack_count)}`,
    `- Archived packs: ${backupPreviewReportValue(preview.archived_pack_count)}`,
    `- Draft files: ${backupPreviewReportValue(preview.draft_count)}`,
    `- Conflicts: ${backupPreviewReportValue(preview.conflict_count)}`,
    `- Restore-plan items: ${backupPreviewReportValue(preview.restore_plan_count, (preview.restore_plan || []).length)}`,
    `- Restore-plan creates: ${backupPreviewReportValue(preview.restore_plan_action_counts?.would_create, "0")}`,
    `- Restore-plan conflicts: ${backupPreviewReportValue(preview.restore_plan_action_counts?.would_conflict, "0")}`,
    `- Restore-plan blocked items: ${backupPreviewReportValue(preview.restore_plan_action_counts?.blocked, "0")}`,
    `- Conflict strategy simulations: ${backupPreviewReportValue(preview.conflict_strategy_count, (preview.conflict_strategy_simulations || []).length)}`,
    `- Selected restore approach: ${backupPreviewReportValue(selectedStrategy?.strategy_label, "Not selected")}`,
    `- Selected restore approach status: ${backupPreviewReportValue(selectedStrategy?.status, "Not selected")}`,
    `- Selected restore approach summary: ${backupPreviewReportValue(selectedStrategy?.strategy_summary, "No selected approach summary recorded.")}`,
    `- Restore approach selection required: ${preview.restore_strategy_selection?.selection_required ? "Yes" : "No"}`,
    `- Restore approach selection summary: ${backupPreviewReportValue(preview.restore_strategy_selection?.selection_summary, "No selection summary recorded.")}`,
    `- Future restore confirmation status: ${backupPreviewReportValue(backupPreviewFutureConfirmationStatus(preview, selectedStrategy).status, "Preview only")}`,
    `- Future restore confirmation explanation: ${backupPreviewReportValue(backupPreviewFutureConfirmationStatus(preview, selectedStrategy).explanation, "No future confirmation explanation recorded.")}`,
    `- Future restore button enabled: ${preview.future_restore_confirmation?.restore_button_enabled ? "Yes" : "No"}`,
    `- Future restore confirmation phrase: ${backupPreviewReportValue(preview.future_restore_confirmation?.confirmation_phrase, "RESTORE BACKUP")}`,
    `- Invalid questionnaire records: ${backupPreviewReportValue(preview.invalid_record_count)}`,
    `- Integrity issues: ${(preview.integrity_issues || []).length}`,
    `- Excluded source items: ${backupPreviewReportValue(preview.excluded_count)}`,
  ];

  appendBackupPreviewReportSection(
    lines,
    "Restore safety checklist",
    preview.restore_safety_checklist || [],
    "No restore safety checklist was returned for this backup.",
    check => backupPreviewReportRow(
      check.label,
      check.detail,
      backupPreviewReportValue(check.status_label)
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Future restore confirmation",
    backupPreviewFutureConfirmationItems(preview),
    "No future restore confirmation items were produced.",
    item => backupPreviewReportRow(
      item.label,
      item.detail,
      backupPreviewReportValue(item.status_label)
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Active packs",
    preview.active_packs || [],
    "No active packs are listed in this backup.",
    pack => backupPreviewReportRow(
      pack.folder_name,
      `${backupPreviewReportValue(pack.file_count, "0")} files, ${backupPreviewReportSize(pack.size_bytes)}, ${backupPreviewReportValue(pack.questionnaire_status)}`,
      pack.conflict ? "Folder conflict" : ""
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Archived packs",
    preview.archived_packs || [],
    "No archived packs are listed in this backup.",
    pack => backupPreviewReportRow(
      pack.folder_name,
      `${backupPreviewReportValue(pack.file_count, "0")} files, ${backupPreviewReportSize(pack.size_bytes)}, ${backupPreviewReportValue(pack.questionnaire_status)}`,
      pack.conflict ? "Folder conflict" : ""
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Draft files",
    preview.drafts || [],
    "No saved drafts are listed in this backup.",
    draft => backupPreviewReportRow(
      draft.path,
      `${backupPreviewReportValue(draft.file_count, "0")} file, ${backupPreviewReportSize(draft.size_bytes)}`,
      draft.conflict ? "Draft conflict" : "Draft file present"
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Selected restore approach item outcomes",
    selectedStrategy?.items || [],
    "No selected restore approach item outcomes were returned for this backup.",
    item => backupPreviewReportRow(
      item.source_path,
      `Target: ${backupPreviewReportValue(item.target_path)}; original target: ${backupPreviewReportValue(item.original_target_path)}; ${backupPreviewReportValue(item.reason)}`,
      backupPreviewReportValue(item.action_label)
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Dry-run restore plan",
    preview.restore_plan || [],
    "No restore-plan items were produced for this backup.",
    planItem => backupPreviewReportRow(
      planItem.source_path,
      `Target: ${backupPreviewReportValue(planItem.target_path)}; ${backupPreviewReportValue(planItem.reason)}`,
      backupPreviewReportValue(planItem.action_label)
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Conflict strategy simulations",
    preview.conflict_strategy_simulations || [],
    "No conflict strategy simulations were produced for this backup.",
    strategy => backupPreviewReportRow(
      strategy.strategy_label,
      `${backupPreviewReportValue(strategy.strategy_summary)} ${backupPreviewConflictStrategyCounts(strategy)}`,
      "Read-only simulation"
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Conflict strategy simulation items",
    (preview.conflict_strategy_simulations || []).flatMap(strategy =>
      (strategy.items || []).map(item => ({
        ...item,
        strategy_label: strategy.strategy_label,
      }))
    ),
    "No conflict strategy simulation item details were produced.",
    item => backupPreviewReportRow(
      `${backupPreviewReportValue(item.strategy_label)} - ${backupPreviewReportValue(item.source_path)}`,
      `Target: ${backupPreviewReportValue(item.target_path)}; original target: ${backupPreviewReportValue(item.original_target_path)}; ${backupPreviewReportValue(item.reason)}`,
      backupPreviewReportValue(item.action_label)
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Records needing attention",
    preview.invalid_records || [],
    "No invalid questionnaire records were detected by the preview.",
    record => backupPreviewReportRow(
      `${backupPreviewReportValue(record.record_scope)}/${backupPreviewReportValue(record.folder_name)}`,
      record.error,
      "Needs attention"
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Conflicts",
    preview.conflicts || [],
    "No restore target conflicts were found.",
    conflict => backupPreviewReportRow(
      conflict.path,
      conflict.reason,
      "Conflict"
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Integrity issues",
    preview.integrity_issues || [],
    "All manifest-listed files matched their recorded size and checksum.",
    issue => backupPreviewReportRow(
      issue.path,
      issue.issue,
      "Integrity issue"
    )
  );

  appendBackupPreviewReportSection(
    lines,
    "Excluded source items",
    preview.excluded || [],
    "The backup manifest did not list excluded source items.",
    excluded => backupPreviewReportRow(
      excluded.path,
      excluded.reason,
      "Excluded from backup"
    )
  );

  lines.push("");
  return `${lines.join("\n")}\n`;
}

function backupPreviewReportFilename() {
  const preview = state.backupPreview;
  const base = (preview?.filename || "backup")
    .replace(/\.[^.]+$/, "")
    .replace(/[^A-Za-z0-9._-]+/g, "_")
    .replace(/^_+|_+$/g, "");
  return `${base || "backup"}_restore_preview_report.txt`;
}

function exportBackupRestorePreviewReport() {
  if (!state.backupPreview) return;

  const blob = new Blob(
    [buildBackupRestorePreviewReport()],
    { type: "text/plain;charset=utf-8" }
  );
  const downloadUrl = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = downloadUrl;
  link.download = backupPreviewReportFilename();
  document.body.append(link);
  link.click();
  link.remove();
  URL.revokeObjectURL(downloadUrl);
  showNotice(
    `Exported restore preview report for ${state.backupPreview.filename || "uploaded backup"}. No files were restored.`
  );
}

async function previewPortableBackupRestore() {
  const file = elements.backupRestoreFile.files[0];
  if (!file) return;

  state.catalogueMode = "backup-preview";
  state.backupPreview = null;
  state.backupRestoreFile = null;
  state.backupRestoreConfirmation = "";
  state.backupRestoreConfirmationResetReason = "";
  state.backupRestoreCompleted = false;
  state.selectedBackupRestoreStrategy = "";
  elements.backupRestoreConfirmation.value = "";
  renderCatalogueMode();
  renderBackupRestorePreview("Validating backup ZIP...");
  clearNotice();

  const formData = new FormData();
  formData.append("backup", file);
  elements.backupRestoreFile.disabled = true;

  try {
    const response = await fetch("/api/backup/preview", {
      method: "POST",
      body: formData,
    });
    const payload = await response.json();
    if (!response.ok) {
      throw new Error(payload.error || "The backup preview could not be created.");
    }
    state.backupPreview = payload;
    state.backupRestoreFile = file;
    state.backupRestoreConfirmation = "";
    state.backupRestoreConfirmationResetReason = "";
    state.backupRestoreCompleted = false;
    elements.backupRestoreConfirmation.value = "";
    state.selectedBackupRestoreStrategy = (
      payload.restore_strategy_selection?.selected_strategy
      || payload.conflict_strategy_simulations?.[0]?.strategy
      || ""
    );
    renderBackupRestorePreview();
    showNotice(
      `Previewed ${payload.filename || file.name}. Readiness: ${payload.readiness_status || "Preview only"}. Safety: ${payload.restore_safety_summary?.status || "Checklist unavailable"}. Selected restore approach: ${backupPreviewSelectedStrategy(payload)?.strategy_label || "Not selected"}. Confirmation: ${payload.future_restore_confirmation?.status || "Preview only"}. No files were restored.`
    );
  } catch (error) {
    state.backupPreview = null;
    state.backupRestoreFile = null;
    state.backupRestoreConfirmation = "";
    state.backupRestoreConfirmationResetReason = "";
    state.backupRestoreCompleted = false;
    state.selectedBackupRestoreStrategy = "";
    elements.backupRestoreConfirmation.value = "";
    renderBackupRestorePreview();
    showNotice(error.message, true);
  } finally {
    elements.backupRestoreFile.disabled = false;
    elements.backupRestoreFile.value = "";
  }
}

async function restorePortableBackup() {
  state.backupRestoreConfirmation = elements.backupRestoreConfirmation.value;
  const status = backupRestorePreflightStatus();
  if (!status.ready) {
    renderBackupRestoreExecutionPanel();
    showNotice(
      status.problems[0] || `Type ${status.confirmationPhrase} exactly before restoring.`,
      true
    );
    return;
  }

  const confirmed = window.confirm(
    `Restore ${state.backupRestoreFile.name} using ${status.selectedStrategy.strategy_label}? Existing restore targets will not be overwritten by default.`
  );
  if (!confirmed) return;

  const formData = new FormData();
  formData.append("backup", state.backupRestoreFile, state.backupRestoreFile.name);
  formData.append("confirmation", state.backupRestoreConfirmation);
  formData.append("conflict_strategy", status.selectedStrategy.strategy);

  elements.backupRestoreExecuteButton.disabled = true;
  elements.backupRestoreExecuteButton.textContent = "Restoring...";
  elements.backupRestoreConfirmation.disabled = true;
  clearNotice();

  try {
    const response = await fetch("/api/backup/restore", {
      method: "POST",
      body: formData,
    });
    const payload = await response.json();
    if (!response.ok) {
      throw new Error(payload.error || "The backup restore could not be completed.");
    }

    const restoredFileName = payload.filename || state.backupRestoreFile.name;
    const restoredItemCount = payload.restore_item_count || 0;
    const skippedItemCount = payload.skipped_item_count || 0;
    const activePackCount = payload.active_pack_count || 0;
    const archivedPackCount = payload.archived_pack_count || 0;
    const draftCount = payload.draft_count || 0;
    const writtenFileCount = payload.written_file_count || 0;

    state.backupRestoreCompleted = true;
    state.backupPreview = null;
    state.backupRestoreFile = null;
    state.backupRestoreConfirmation = "";
    state.backupRestoreConfirmationResetReason = "";
    state.selectedBackupRestoreStrategy = "";
    elements.backupRestoreConfirmation.value = "";
    state.catalogueMode = "active";
    state.selectedPath = null;
    state.selectedDraftId = null;
    state.search = "";
    elements.searchInput.value = "";
    await loadCatalogue({ preserveSelection: false });
    showNotice(
      `Restore completed from ${restoredFileName}. Restored ${restoredItemCount} items (${activePackCount} active packs, ${archivedPackCount} archived packs, ${draftCount} drafts; ${writtenFileCount} files written) and skipped ${skippedItemCount}. The active library has been refreshed.`
    );
  } catch (error) {
    state.backupRestoreConfirmation = elements.backupRestoreConfirmation.value;
    renderBackupRestoreExecutionPanel();
    showNotice(error.message, true);
  } finally {
    elements.backupRestoreExecuteButton.textContent = "Restore backup";
    renderBackupRestoreExecutionPanel();
  }
}

async function copyRecordPath() {
  const assessment = currentAssessment();
  if (!assessment) return;
  try {
    await navigator.clipboard.writeText(assessment.answers_path);
    showNotice("Questionnaire record path copied.");
  } catch {
    showNotice("The record path could not be copied automatically.", true);
  }
}

async function archiveAssessment(assessment, actionButton = elements.archiveButton) {
  if (!assessment) return;

  const confirmed = window.confirm(
    `Archive ${assessment.company_name} (${assessment.folder_name})? The pack will be moved to _archived and hidden from the main catalogue.`
  );
  if (!confirmed) return;

  clearNotice();
  const originalText = actionButton.textContent;
  actionButton.disabled = true;
  actionButton.textContent = "Archiving...";

  try {
    const response = await fetch("/api/assessments/archive", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ answers_path: assessment.answers_path }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(payload.error || "Archive failed.");
    state.selectedPath = null;
    await loadCatalogue({ preserveSelection: false });
    showNotice(`Archived ${payload.folder_name} to ${payload.archived_folder}`);
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    actionButton.disabled = false;
    actionButton.textContent = originalText;
  }
}

async function archiveSelectedAssessment() {
  await archiveAssessment(currentAssessment());
}

async function restoreSelectedAssessment() {
  const assessment = currentAssessment();
  if (!assessment) return;

  const confirmed = window.confirm(
    `Restore ${assessment.company_name} (${assessment.folder_name}) to the active assessment library?`
  );
  if (!confirmed) return;

  clearNotice();
  elements.restoreButton.disabled = true;
  elements.restoreButton.textContent = "Restoring...";

  try {
    const response = await fetch("/api/assessments/restore", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ answers_path: assessment.answers_path }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(payload.error || "Restore failed.");
    state.selectedPath = null;
    await loadCatalogue({ preserveSelection: false });
    showNotice(`Restored ${payload.folder_name} to ${payload.restored_folder}`);
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.restoreButton.disabled = false;
    elements.restoreButton.textContent = "Restore assessment";
  }
}

async function deleteSelectedAssessment() {
  const assessment = currentAssessment();
  if (!assessment || !isArchivedMode()) return;

  const confirmation = window.prompt(
    `Permanently delete ${assessment.company_name}?\n\n`
    + `This removes the entire archived pack and cannot be undone.\n`
    + `Type ${assessment.folder_name} to confirm.`
  );
  if (confirmation === null) return;
  if (confirmation !== assessment.folder_name) {
    showNotice("The folder name did not match. Nothing was deleted.", true);
    return;
  }

  clearNotice();
  elements.deleteButton.disabled = true;
  elements.deleteButton.textContent = "Deleting...";

  try {
    const response = await fetch("/api/assessments/delete", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        answers_path: assessment.answers_path,
        confirmation,
      }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(payload.error || "Deletion failed.");
    state.selectedPath = null;
    await loadCatalogue({ preserveSelection: false });
    showNotice(`Permanently deleted archived pack ${payload.folder_name}.`);
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.deleteButton.disabled = false;
    elements.deleteButton.textContent = "Delete permanently";
  }
}

async function loadQuestionnaireDefinition() {
  const response = await fetch("/api/questionnaire", { cache: "no-store" });
  const payload = await response.json();
  if (!response.ok) throw new Error(payload.error || "Could not load questionnaire.");
  questionnaireDefinition = payload;
  renderQuestionnaire();
}

async function ensureQuestionnaireDefinition() {
  if (!questionnaireDefinition) {
    await loadQuestionnaireDefinition();
  }
}

function renderQuestionnaire() {
  elements.questionnaireSections.replaceChildren();
  let visibleQuestionCount = 0;
  questionnaireDefinition.sections.forEach(section => {
    const questions = state.formMode === "pending"
      ? section.questions.filter(question => state.pendingKeys.includes(question.key))
      : section.questions;
    if (!questions.length) return;

    const group = document.createElement("section");
    group.className = "question-group";
    const heading = document.createElement("h3");
    heading.textContent = section.title;
    group.append(heading);

    questions.forEach(question => {
      visibleQuestionCount += 1;
      const row = document.createElement("div");
      row.className = "question-row";
      const text = document.createElement("div");
      text.className = "question-text";
      text.textContent = question.text;
      const options = document.createElement("div");
      options.className = "answer-options";
      ["yes", "no"].forEach(value => {
        const label = document.createElement("label");
        const input = document.createElement("input");
        input.type = "radio";
        input.name = question.key;
        input.value = value;
        input.required = true;
        input.addEventListener("change", updateQuestionProgress);
        const optionText = document.createElement("span");
        optionText.textContent = value === "yes" ? "Yes" : "No";
        label.append(input, optionText);
        options.append(label);
      });
      row.append(text, options);
      group.append(row);
    });
    elements.questionnaireSections.append(group);
  });
  elements.assessmentForm.dataset.questionCount = visibleQuestionCount;
  updateQuestionProgress();
}

function updateQuestionProgress() {
  if (!questionnaireDefinition) return;
  const answered = new Set(
    Array.from(elements.assessmentForm.querySelectorAll('input[type="radio"]:checked'))
      .map(input => input.name)
  ).size;
  const total = Number(elements.assessmentForm.dataset.questionCount || 0);
  elements.questionProgress.textContent = `${answered} of ${total}`;
}

function setAssessmentFormContext(scope, save, output) {
  elements.formContextScope.textContent = scope;
  elements.formContextSave.textContent = save;
  elements.formContextOutput.textContent = output;
}

function setAssessmentFormMode(mode) {
  state.formMode = mode;
  state.reviewPath = null;
  if (mode !== "pending") state.pendingKeys = [];
  elements.assessmentForm.dataset.mode = mode;
  const identityInputs = elements.identityFields.querySelectorAll("input");
  const pendingMode = mode === "pending";
  elements.identityFields.hidden = pendingMode;
  elements.workflowCarryForwardOption.hidden = !["review", "pending"].includes(
    mode
  );
  elements.workflowCarryForward.checked = false;
  identityInputs.forEach(input => {
    input.disabled = pendingMode;
  });
  elements.saveDraftButton.hidden = !["new", "draft"].includes(mode);

  if (mode === "review") {
    elements.formEyebrow.textContent = "Review assessment";
    elements.formTitle.textContent = "Review questionnaire";
    elements.formIntro.textContent = "Update saved answers and generate a refreshed document pack.";
    setAssessmentFormContext(
      "Review all 48 answers",
      "Source pack stays unchanged",
      "Creates a new reviewed pack"
    );
    elements.createAssessmentButton.textContent = "Generate reviewed pack";
    renderQuestionnaire();
    return;
  }

  if (mode === "pending") {
    elements.formEyebrow.textContent = "Pending review";
    elements.formTitle.textContent = "Resolve pending answers";
    elements.formIntro.textContent = "Answer only the unresolved questions. Existing company details and completed answers will be preserved.";
    setAssessmentFormContext(
      "Only unresolved answers",
      "Company details preserved",
      "Creates a new resolved pack"
    );
    elements.createAssessmentButton.textContent = "Generate resolved pack";
    renderQuestionnaire();
    return;
  }

  if (mode === "draft") {
    elements.formEyebrow.textContent = "Assessment draft";
    elements.formTitle.textContent = "Continue questionnaire";
    elements.formIntro.textContent = "Continue the saved questionnaire, save it again, or complete all questions to generate the document pack.";
    setAssessmentFormContext(
      "Resume saved progress",
      "Save again if interrupted",
      "Generate when complete"
    );
    elements.createAssessmentButton.textContent = "Generate document pack";
    renderQuestionnaire();
    return;
  }

  elements.formEyebrow.textContent = "New assessment";
  elements.formTitle.textContent = "Company questionnaire";
  elements.formIntro.textContent = "Complete all questions to generate the first document pack.";
  setAssessmentFormContext(
    "48 yes/no questions",
    "Save a draft anytime",
    "Generates a numbered local pack"
  );
  elements.createAssessmentButton.textContent = "Generate document pack";
  renderQuestionnaire();
}

function showAssessmentForm() {
  elements.libraryPage.hidden = true;
  elements.assessmentDialog.hidden = false;
  window.scrollTo({ top: 0, behavior: "auto" });
  const firstInput = elements.assessmentForm.querySelector(
    state.formMode === "pending"
      ? 'input[type="radio"]'
      : 'input:not([disabled])'
  );
  if (firstInput) firstInput.focus();
}

function populateAssessmentForm(answers) {
  elements.assessmentForm.elements.company_name.value = answers.company_name || "";
  elements.assessmentForm.elements.document_owner.value = answers.document_owner || "";
  elements.assessmentForm.elements.employee_count.value = answers.employee_count || "";

  questionnaireDefinition.sections.forEach(section => {
    section.questions.forEach(question => {
      const value = answers[question.key];
      if (value === "yes" || value === "no") {
        const input = elements.assessmentForm.querySelector(
          `input[name="${question.key}"][value="${value}"]`
        );
        if (input) input.checked = true;
      }
    });
  });
  updateQuestionProgress();
}

async function openNewAssessment() {
  await ensureQuestionnaireDefinition();
  state.draftId = null;
  setAssessmentFormMode("new");
  elements.assessmentForm.reset();
  elements.formError.hidden = true;
  updateQuestionProgress();
  showAssessmentForm();
}

async function resumeSelectedDraft() {
  const draft = currentDraft();
  if (!draft) return;

  clearNotice();
  await ensureQuestionnaireDefinition();
  elements.resumeDraftButton.disabled = true;
  elements.resumeDraftButton.textContent = "Loading draft...";
  elements.formError.hidden = true;

  try {
    const response = await fetch("/api/assessment-draft", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ draft_id: draft.draft_id }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not load assessment draft."
    );

    elements.assessmentForm.reset();
    state.draftId = payload.draft_id;
    setAssessmentFormMode("draft");
    populateAssessmentForm(payload.answers);
    showAssessmentForm();
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.resumeDraftButton.disabled = false;
    elements.resumeDraftButton.textContent = "Resume draft";
  }
}

async function saveAssessmentDraft() {
  elements.formError.hidden = true;
  const payload = Object.fromEntries(
    new FormData(elements.assessmentForm).entries()
  );
  if (state.draftId) payload.draft_id = state.draftId;

  elements.saveDraftButton.disabled = true;
  elements.saveDraftButton.textContent = "Saving draft...";

  try {
    const response = await fetch("/api/assessment-drafts", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });
    const result = await response.json();
    if (!response.ok) throw new Error(
      result.error || "The assessment draft could not be saved."
    );

    state.draftId = result.draft_id;
    state.catalogueMode = "drafts";
    state.selectedPath = null;
    state.selectedDraftId = result.draft_id;
    elements.assessmentDialog.hidden = true;
    elements.libraryPage.hidden = false;
    await loadCatalogue();
    showNotice(
      `Saved ${result.company_name} with ${result.answered_count} of ${result.total_questions} questions answered.`
    );
  } catch (error) {
    elements.formError.textContent = error.message;
    elements.formError.hidden = false;
    elements.formError.scrollIntoView({ behavior: "smooth", block: "center" });
  } finally {
    elements.saveDraftButton.disabled = false;
    elements.saveDraftButton.textContent = "Save draft and exit";
  }
}

async function deleteSelectedDraft() {
  const draft = currentDraft();
  if (!draft) return;
  if (!window.confirm(
    `Delete the saved draft for ${draft.company_name}? This cannot be undone.`
  )) return;

  clearNotice();
  elements.deleteDraftButton.disabled = true;
  elements.deleteDraftButton.textContent = "Deleting...";

  try {
    const response = await fetch("/api/assessment-drafts/delete", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ draft_id: draft.draft_id }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "The assessment draft could not be deleted."
    );
    state.selectedDraftId = null;
    await loadCatalogue({ preserveSelection: false });
    showNotice(`Deleted the assessment draft for ${draft.company_name}.`);
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.deleteDraftButton.disabled = false;
    elements.deleteDraftButton.textContent = "Delete draft";
  }
}

async function openReviewAssessment() {
  const assessment = currentAssessment();
  if (!assessment) return;

  clearNotice();
  await ensureQuestionnaireDefinition();
  elements.reviewButton.disabled = true;
  elements.reviewButton.textContent = "Loading review...";
  elements.formError.hidden = true;

  try {
    const response = await fetch("/api/assessment-record", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ answers_path: assessment.answers_path }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(payload.error || "Could not load assessment.");

    elements.assessmentForm.reset();
    setAssessmentFormMode("review");
    state.reviewPath = payload.answers_path;
    populateAssessmentForm(payload.answers);
    showAssessmentForm();
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.reviewButton.disabled = false;
    elements.reviewButton.textContent = "Review/edit assessment";
  }
}

async function openPendingReviewAssessment() {
  const assessment = currentAssessment();
  if (!assessment || !assessment.to_confirm) return;

  clearNotice();
  await ensureQuestionnaireDefinition();
  elements.pendingReviewButton.disabled = true;
  elements.pendingReviewButton.textContent = "Loading pending answers...";
  elements.formError.hidden = true;

  try {
    const response = await fetch("/api/assessment-record", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ answers_path: assessment.answers_path }),
    });
    const payload = await response.json();
    if (!response.ok) throw new Error(
      payload.error || "Could not load pending answers."
    );

    state.pendingKeys = questionnaireDefinition.sections
      .flatMap(section => section.questions)
      .filter(question => payload.answers[question.key] === "To confirm")
      .map(question => question.key);
    if (!state.pendingKeys.length) {
      throw new Error("The selected assessment has no pending answers.");
    }

    elements.assessmentForm.reset();
    setAssessmentFormMode("pending");
    state.reviewPath = payload.answers_path;
    showAssessmentForm();
  } catch (error) {
    showNotice(error.message, true);
  } finally {
    elements.pendingReviewButton.disabled = false;
    elements.pendingReviewButton.textContent = "Resolve pending answers";
  }
}

function closeNewAssessment() {
  if (
    !elements.createAssessmentButton.disabled
    && !elements.saveDraftButton.disabled
  ) {
    elements.assessmentDialog.hidden = true;
    elements.libraryPage.hidden = false;
    window.scrollTo({ top: 0, behavior: "auto" });
  }
}

async function submitAssessment(event) {
  event.preventDefault();
  elements.formError.hidden = true;
  if (!elements.assessmentForm.reportValidity()) return;

  const payload = Object.fromEntries(new FormData(elements.assessmentForm).entries());
  const isReview = state.formMode === "review";
  const isPending = state.formMode === "pending";
  const isDraft = state.formMode === "draft";
  if (isReview || isPending) {
    payload.answers_path = state.reviewPath;
    payload.carry_forward_workflow = elements.workflowCarryForward.checked;
  }
  if (isDraft) payload.draft_id = state.draftId;

  elements.createAssessmentButton.disabled = true;
  elements.createAssessmentButton.textContent = isPending
    ? "Generating resolved pack..."
    : isReview
      ? "Generating reviewed pack..."
      : "Generating pack...";
  try {
    const endpoint = isPending
      ? "/api/assessments/review-pending"
      : isReview
        ? "/api/assessments/review"
        : "/api/assessments";
    const response = await fetch(endpoint, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });
    const result = await response.json();
    if (!response.ok) throw new Error(result.error || "Generation failed.");
    state.draftId = null;
    state.catalogueMode = "active";
    state.selectedDraftId = null;
    elements.assessmentDialog.hidden = true;
    elements.libraryPage.hidden = false;
    await loadCatalogue({ preserveSelection: false });
    const generatedPath = generatedAssessmentSelectionPath(result);
    if (generatedPath) {
      selectAssessment(generatedPath, "files");
    } else {
      state.selectedAssessmentSection = "files";
      renderDetail();
    }
    let workflowMessage = "";
    if (payload.carry_forward_workflow) {
      const carriedCount = result.workflow_carried_forward_count || 0;
      workflowMessage = carriedCount
        ? `Carried forward workflow for ${carriedCount} unchanged ${carriedCount === 1 ? "finding" : "findings"}.`
        : "No existing workflow entries were eligible to carry forward.";
    }
    showGenerationSuccessNotice(result, workflowMessage);
  } catch (error) {
    elements.formError.textContent = error.message;
    elements.formError.hidden = false;
    elements.formError.scrollIntoView({ behavior: "smooth", block: "center" });
  } finally {
    elements.createAssessmentButton.disabled = false;
    elements.createAssessmentButton.textContent = isPending
      ? "Generate resolved pack"
      : isReview
        ? "Generate reviewed pack"
        : "Generate document pack";
  }
}

elements.refreshButton.addEventListener("click", () => loadCatalogue());
elements.backupButton.addEventListener("click", downloadPortableBackup);
elements.restorePreviewViewButton.addEventListener("click", showBackupPreviewView);
elements.operatorGuidesViewButton.addEventListener("click", showOperatorChecklistView);
elements.productReadinessViewButton.addEventListener("click", showProductReadinessView);
elements.localDataViewButton.addEventListener("click", showLocalDataView);
elements.operatorChecklistViewButton.addEventListener("click", showOperatorChecklistView);
elements.backupRestoreFile.addEventListener("change", previewPortableBackupRestore);
elements.backupRestoreConfirmation.addEventListener("input", () => {
  state.backupRestoreConfirmation = elements.backupRestoreConfirmation.value;
  state.backupRestoreConfirmationResetReason = "";
  renderBackupRestoreExecutionPanel();
});
elements.backupRestoreExecuteButton.addEventListener("click", restorePortableBackup);
elements.backupRestoreReportButton.addEventListener("click", exportBackupRestorePreviewReport);
elements.backupRestoreClearButton.addEventListener("click", () => {
  resetBackupRestorePreview();
});
elements.assessmentLibraryButton.addEventListener("click", showActiveLibrary);
elements.archiveViewButton.addEventListener("click", toggleArchiveView);
elements.draftViewButton.addEventListener("click", toggleDraftView);
elements.workflowViewButton.addEventListener("click", toggleWorkflowView);
elements.activityViewButton.addEventListener("click", toggleActivityView);
elements.comparisonViewButton.addEventListener("click", toggleComparisonView);
elements.dueViewButton.addEventListener("click", toggleDueDateView);
elements.assessmentComparisonCompany.addEventListener(
  "change",
  selectAssessmentComparisonCompany
);
elements.assessmentComparisonPrevious.addEventListener(
  "change",
  () => selectAssessmentComparisonVersions("previous")
);
elements.assessmentComparisonCurrent.addEventListener(
  "change",
  () => selectAssessmentComparisonVersions("current")
);
elements.assessmentComparisonExportButton.addEventListener(
  "click",
  exportAssessmentVersionComparison
);
elements.dueDateExportButton.addEventListener("click", exportDueDatePlanner);
elements.workflowSelectVisible.addEventListener("change", () => {
  filteredWorkflowRows().forEach(row => {
    const key = workflowRowKey(row);
    if (elements.workflowSelectVisible.checked) {
      state.selectedWorkflowRows.add(key);
    } else {
      state.selectedWorkflowRows.delete(key);
    }
  });
  renderWorkflowDashboard();
});
elements.workflowBulkOwner.addEventListener("input", () => {
  renderWorkflowSelection(filteredWorkflowRows());
});
elements.workflowBulkStatus.addEventListener("input", () => {
  renderWorkflowSelection(filteredWorkflowRows());
});
elements.workflowBulkStatus.addEventListener("change", () => {
  renderWorkflowSelection(filteredWorkflowRows());
});
elements.workflowBulkTargetDate.addEventListener("input", () => {
  renderWorkflowSelection(filteredWorkflowRows());
});
elements.workflowBulkTargetDate.addEventListener("change", () => {
  renderWorkflowSelection(filteredWorkflowRows());
});
elements.workflowBulkNote.addEventListener("input", () => {
  renderWorkflowSelection(filteredWorkflowRows());
});
elements.workflowBulkNote.addEventListener("change", () => {
  renderWorkflowSelection(filteredWorkflowRows());
});
elements.workflowAssignOwnerButton.addEventListener(
  "click",
  assignSelectedWorkflowOwner
);
elements.workflowAssignStatusButton.addEventListener(
  "click",
  assignSelectedWorkflowStatus
);
elements.workflowAssignTargetDateButton.addEventListener(
  "click",
  assignSelectedWorkflowTargetDate
);
elements.workflowAppendNoteButton.addEventListener(
  "click",
  appendSelectedWorkflowNote
);
elements.workflowExportButton.addEventListener("click", exportWorkflowDashboard);
elements.workflowActivityExportButton.addEventListener(
  "click",
  exportWorkflowActivityRegister
);
[
  elements.workflowSearch,
  elements.workflowVersionFilter,
  elements.workflowStatusFilter,
  elements.workflowReviewIndicationFilter,
  elements.workflowOwnerFilter,
  elements.workflowPriorityFilter,
  elements.workflowOverdueFilter,
].forEach(control => {
  control.addEventListener("input", () => {
    state.workflowFilters = {
      search: elements.workflowSearch.value,
      versionScope: elements.workflowVersionFilter.value,
      status: elements.workflowStatusFilter.value,
      reviewIndication: elements.workflowReviewIndicationFilter.value,
      owner: elements.workflowOwnerFilter.value,
      priority: elements.workflowPriorityFilter.value,
      overdue: elements.workflowOverdueFilter.checked,
    };
    renderWorkflowDashboard();
  });
});
[
  elements.workflowActivitySearch,
  elements.workflowActivityScopeFilter,
  elements.workflowActivityCompanyFilter,
  elements.workflowActivityOperationFilter,
  elements.workflowActivityDateFrom,
  elements.workflowActivityDateTo,
].forEach(control => {
  control.addEventListener("input", () => {
    state.workflowActivityFilters = {
      search: elements.workflowActivitySearch.value,
      scope: elements.workflowActivityScopeFilter.value,
      company: elements.workflowActivityCompanyFilter.value,
      operation: elements.workflowActivityOperationFilter.value,
      dateFrom: elements.workflowActivityDateFrom.value,
      dateTo: elements.workflowActivityDateTo.value,
    };
    renderWorkflowActivityRegister();
  });
});
[
  elements.assessmentComparisonViewFilter,
  elements.assessmentComparisonSectionFilter,
  elements.assessmentComparisonTransitionFilter,
].forEach(control => {
  control.addEventListener("input", () => {
    state.assessmentComparisonFilters.view = (
      elements.assessmentComparisonViewFilter.value
    );
    state.assessmentComparisonFilters.section = (
      elements.assessmentComparisonSectionFilter.value
    );
    state.assessmentComparisonFilters.transition = (
      elements.assessmentComparisonTransitionFilter.value
    );
    renderAssessmentVersionComparison();
  });
});
[
  elements.dueDateSearch,
  elements.dueDateVersionFilter,
  elements.dueDateCompanyFilter,
  elements.dueDateOwnerFilter,
  elements.dueDateTypeFilter,
  elements.dueDateStatusFilter,
  elements.dueDateBucketFilter,
  elements.dueDateCompleteFilter,
].forEach(control => {
  control.addEventListener("input", () => {
    state.dueDateFilters = {
      search: elements.dueDateSearch.value,
      versionScope: elements.dueDateVersionFilter.value,
      company: elements.dueDateCompanyFilter.value,
      owner: elements.dueDateOwnerFilter.value,
      itemType: elements.dueDateTypeFilter.value,
      status: elements.dueDateStatusFilter.value,
      bucket: elements.dueDateBucketFilter.value,
      includeComplete: elements.dueDateCompleteFilter.checked,
    };
    renderDueDatePlanner();
  });
});
elements.assessmentDetailTabs.forEach(button => {
  button.addEventListener("click", () => {
    state.selectedAssessmentSection = button.dataset.assessmentSection;
    renderAssessmentDetailSections();
  });
});
elements.assessmentSectionJumpButtons.forEach(button => {
  button.addEventListener("click", () => {
    openAssessmentSection(button.dataset.assessmentSectionJump);
  });
});
elements.searchInput.addEventListener("input", event => {
  state.search = event.target.value;
  renderAssessmentList();
  renderDetail();
});
elements.generateButton.addEventListener("click", generateSelectedAssessment);
elements.reviewButton.addEventListener("click", openReviewAssessment);
elements.pendingReviewButton.addEventListener("click", openPendingReviewAssessment);
elements.copyButton.addEventListener("click", copyRecordPath);
elements.archiveButton.addEventListener("click", archiveSelectedAssessment);
elements.restoreButton.addEventListener("click", restoreSelectedAssessment);
elements.deleteButton.addEventListener("click", deleteSelectedAssessment);
elements.openPackFolderButton.addEventListener("click", openSelectedPackFolder);
elements.findingsFilters.forEach(button => {
  button.addEventListener("click", () => {
    state.findingsFilter = button.dataset.findingFilter;
    renderFindings();
  });
});
elements.emptyStateActions.forEach(button => {
  button.addEventListener(
    "click",
    () => handleEmptyStateAction(button.dataset.emptyStateAction)
  );
});
elements.newAssessmentButton.addEventListener("click", openNewAssessment);
elements.saveDraftButton.addEventListener("click", saveAssessmentDraft);
elements.resumeDraftButton.addEventListener("click", resumeSelectedDraft);
elements.deleteDraftButton.addEventListener("click", deleteSelectedDraft);
document.querySelector("#close-dialog-button").addEventListener("click", closeNewAssessment);
document.querySelector("#cancel-assessment-button").addEventListener("click", closeNewAssessment);
elements.assessmentForm.addEventListener("submit", submitAssessment);
document.addEventListener("keydown", event => {
  if (event.key === "Escape" && !elements.assessmentDialog.hidden) closeNewAssessment();
});

Promise.all([loadCatalogue({ preserveSelection: false }), loadQuestionnaireDefinition()])
  .catch(error => showNotice(error.message, true));
