import { useEffect, useMemo, useRef, useState } from "react";
import { Link, useNavigate, useSearchParams } from "react-router-dom";
import { ArrowLeft, Download, Edit3, Grid, Layout, Table, Image } from "react-feather";

import { API_BASE, postForm, putJson, request } from "../lib/api";
import { formatDocNumber } from "../lib/report";
import {
  ensureSections,
  flattenSections,
  insertPage,
  removePage,
  replacePage,
} from "../lib/sections";
import { buildSessionQuery, getSessionId, setStoredSessionId } from "../lib/session";
import type { Heading, JobsheetSection, Session, TemplateFields } from "../types/session";
import { PageFooter } from "../components/PageFooter";
import { PageHeader } from "../components/PageHeader";
import { PageShell } from "../components/PageShell";
import { InfoMenu } from "../components/InfoMenu";
import { LoadingBar } from "../components/LoadingBar";
import { SaveBeforeLeaveDialog } from "../components/SaveBeforeLeaveDialog";

type FieldDef = {
  key: keyof TemplateFields;
  label: string;
  multiline?: boolean;
};

const normalizeHeadings = (raw?: Heading[] | Record<string, string>): Heading[] => {
  if (Array.isArray(raw)) {
    return raw
      .filter(Boolean)
      .map((heading) => ({
        number: String(heading.number ?? "").trim(),
        name: String(heading.name ?? "").trim(),
      }));
  }
  if (raw && typeof raw === "object") {
    return Object.entries(raw).map(([number, name]) => ({
      number: String(number).trim(),
      name: String(name ?? "").trim(),
    }));
  }
  return [];
};

const GENERAL_FIELDS: FieldDef[] = [
  { key: "inspection_date", label: "Inspection Date" },
  { key: "inspector", label: "Inspector" },
  { key: "document_no", label: "Document No" },
  { key: "company_logo", label: "Company Logo" },
];

const ITEM_FIELDS: FieldDef[] = [
  { key: "area", label: "Area" },
  { key: "reference", label: "Reference" },
  { key: "functional_location", label: "Location" },
  { key: "item_description", label: "Item Description", multiline: true },
  { key: "category", label: "Category" },
  { key: "priority", label: "Priority" },
  { key: "required_action", label: "Required Action", multiline: true },
  { key: "figure_caption", label: "Figure Caption" },
];

export default function InputDataPage() {
  const [searchParams] = useSearchParams();
  const sessionId = getSessionId(searchParams.toString());
  const sessionQuery = buildSessionQuery(sessionId);
  const navigate = useNavigate();

  const [session, setSession] = useState<Session | null>(null);
  const [sections, setSections] = useState<JobsheetSection[]>([]);
  const [status, setStatus] = useState("");
  const [isSaving, setIsSaving] = useState(false);
  const [saveState, setSaveState] = useState<
    "saved" | "saving" | "pending" | "error"
  >("saved");
  const [isUploading, setIsUploading] = useState(false);
  const [copySourceIndex, setCopySourceIndex] = useState(0);
  const [copyTargets, setCopyTargets] = useState("");
  const [addSectionId, setAddSectionId] = useState<string>("");
  const [sectionsCollapsed, setSectionsCollapsed] = useState(true);
  const [headings, setHeadings] = useState<Heading[]>([]);
  const [showGeneralColumns, setShowGeneralColumns] = useState(false);
  const [generalDirty, setGeneralDirty] = useState(false);
  const [generalTemplate, setGeneralTemplate] = useState<TemplateFields>({});
  const [uploadProgress, setUploadProgress] = useState<number | null>(null);
  const [pendingNavigationTarget, setPendingNavigationTarget] = useState<
    string | null
  >(null);
  const [photoSelections, setPhotoSelections] = useState<Record<number, string>>(
    {},
  );
  const saveTimerRef = useRef<number | null>(null);
  const generalApplyTimerRef = useRef<number | null>(null);
  const headingsSaveTimerRef = useRef<number | null>(null);
  const lastSavedHeadingsRef = useRef<string>(JSON.stringify([]));
  const headingsLoadedRef = useRef(false);
  const lastSavedRef = useRef<string>("");
  const pendingSaveRef = useRef<string>("");
  const savePromiseRef = useRef<Promise<void> | null>(null);
  const excelInputRef = useRef<HTMLInputElement | null>(null);
  const jsonInputRef = useRef<HTMLInputElement | null>(null);
  const uploadInputRefs = useRef<Record<number, HTMLInputElement | null>>({});
  const uploadTimerRef = useRef<number | null>(null);

  function clearUploadTimer() {
    if (uploadTimerRef.current !== null) {
      window.clearInterval(uploadTimerRef.current);
      uploadTimerRef.current = null;
    }
  }

  function startUploadProgress() {
    clearUploadTimer();
    setUploadProgress(6);
    uploadTimerRef.current = window.setInterval(() => {
      setUploadProgress((prev) => {
        const value = typeof prev === "number" ? prev : 0;
        if (value >= 92) return value;
        return Math.min(92, value + Math.max(1, (92 - value) * 0.08));
      });
    }, 350);
  }

  useEffect(() => {
    if (!sessionId) {
      setStatus("No active session found. Return to upload to continue.");
      return;
    }
    setStoredSessionId(sessionId);
    async function load() {
      try {
        const data = await request<Session>(`/sessions/${sessionId}`);
        setSession(data);
        const sectionResp = await request<{ sections: JobsheetSection[] }>(
          `/sessions/${sessionId}/sections`,
        );
        const normalized = ensureSections(sectionResp.sections);
        setSections(normalized);
        const initialHeadings = normalizeHeadings(data.headings);
        setHeadings(initialHeadings);
        headingsLoadedRef.current = true;
        lastSavedHeadingsRef.current = JSON.stringify(initialHeadings);
        lastSavedRef.current = JSON.stringify(normalized);
        pendingSaveRef.current = lastSavedRef.current;
        setSaveState("saved");
      } catch (err) {
        const message =
          err instanceof Error ? err.message : "Failed to load session.";
        setStatus(message);
      }
    }
    load();
  }, [sessionId]);

  useEffect(
    () => () => {
      clearUploadTimer();
    },
    [],
  );

  async function refreshSession() {
    if (!sessionId) return;
    const data = await request<Session>(`/sessions/${sessionId}`);
    setSession(data);
    const sectionResp = await request<{ sections: JobsheetSection[] }>(
      `/sessions/${sessionId}/sections`,
    );
    const normalized = ensureSections(sectionResp.sections);
    setSections(normalized);
    const nextHeadings = normalizeHeadings(data.headings);
    setHeadings(nextHeadings);
    headingsLoadedRef.current = true;
    lastSavedHeadingsRef.current = JSON.stringify(nextHeadings);
    lastSavedRef.current = JSON.stringify(normalized);
    pendingSaveRef.current = lastSavedRef.current;
    setSaveState("saved");
  }

  const flatPages = useMemo(
    () => flattenSections(ensureSections(sections)),
    [sections],
  );
  const totalPages = useMemo(() => {
    if (flatPages.length > 0) return flatPages.length;
    return Math.max(1, session?.page_count ?? 0);
  }, [flatPages.length, session?.page_count]);

  useEffect(() => {
    if (copySourceIndex >= flatPages.length) {
      setCopySourceIndex(Math.max(0, flatPages.length - 1));
    }
  }, [copySourceIndex, flatPages.length]);

  useEffect(() => {
    if (!addSectionId && sections.length > 0) {
      setAddSectionId(sections[0].id);
    }
  }, [addSectionId, sections]);

  useEffect(() => {
    if (generalDirty) return;
    const source = flatPages[0]?.page?.template ?? {};
    const next: TemplateFields = {};
    GENERAL_FIELDS.forEach((field) => {
      const value = source[field.key] ?? getFallbackValue(field.key);
      if (value !== undefined) {
        next[field.key] = value;
      }
    });
    setGeneralTemplate(next);
  }, [generalDirty, flatPages, session]);

  function updateField(
    entryIndex: number,
    key: keyof TemplateFields,
    value: string,
  ) {
    const entry = flatPages[entryIndex];
    if (!entry) return;
    setSections((prev) => {
      const template = { ...(entry.page.template ?? {}) };
      template[key] = value;
      const nextPage = { ...entry.page, template };
      return replacePage(prev, entry.sectionIndex, entry.pageIndex, nextPage);
    });
  }

  function updateGeneralField(key: keyof TemplateFields, value: string) {
    setGeneralTemplate((prev) => ({ ...prev, [key]: value }));
    setGeneralDirty(true);
  }

  function updateHeadingField(index: number, key: keyof Heading, value: string) {
    setHeadings((prev) =>
      prev.map((heading, idx) =>
        idx === index ? { ...heading, [key]: value } : heading,
      ),
    );
  }

  function addHeadingRow() {
    setHeadings((prev) => [...prev, { number: "", name: "" }]);
  }

  function removeHeadingRow(index: number) {
    setHeadings((prev) => prev.filter((_, idx) => idx !== index));
  }

  function applyGeneralToSections(
    source: JobsheetSection[],
    template: TemplateFields,
  ): JobsheetSection[] {
    return source.map((section) => ({
      ...section,
      pages: (section.pages ?? []).map((page) => {
        const nextTemplate = { ...(page.template ?? {}) };
        GENERAL_FIELDS.forEach((field) => {
          const value = template[field.key];
          if (value !== undefined) {
            nextTemplate[field.key] = value;
          }
        });
        return { ...page, template: nextTemplate };
      }),
    }));
  }

  function applyRowToAll(pageIndex: number) {
    const entry = flatPages[pageIndex];
    if (!entry) return;
    const source = entry.page.template ?? {};
    setSections((prev) =>
      prev.map((section) => ({
        ...section,
        pages: (section.pages ?? []).map((page) => ({
          ...page,
          template: { ...source },
        })),
      })),
    );
  }

  function applyGeneralToAll(silent = false) {
    if (!flatPages.length) return;
    setSections((prev) => applyGeneralToSections(prev, generalTemplate));
    setGeneralDirty(false);
    if (!silent) {
      setStatus("Applied general info to all pages.");
    }
  }

  function insertPageAt(index: number, templateSource?: TemplateFields) {
    const atEnd = index >= flatPages.length;
    const entry = flatPages[index] ?? flatPages[flatPages.length - 1];
    if (!entry) return;
    setSections((prev) => {
      const fallbackTemplate =
        templateSource ??
        entry.page.template ??
        {};
      const pageIndex = atEnd ? entry.pageIndex + 1 : entry.pageIndex;
      return insertPage(prev, entry.sectionIndex, pageIndex, {
        items: [],
        template: { ...fallbackTemplate },
      });
    });
  }

  function addSection() {
    const baseTemplate = { ...generalTemplate };
    setSections((prev) => [
      ...prev,
      {
        id: crypto.randomUUID(),
        title: `Section ${prev.length + 1}`,
        pages: [{ items: [], template: baseTemplate }],
      },
    ]);
  }

  function updateSectionTitle(sectionId: string, value: string) {
    setSections((prev) =>
      prev.map((section) =>
        section.id === sectionId ? { ...section, title: value } : section,
      ),
    );
  }

  function insertPageIntoSection(sectionId: string, templateSource?: TemplateFields) {
    const sectionIndex = sections.findIndex((section) => section.id === sectionId);
    if (sectionIndex === -1) return;
    const section = sections[sectionIndex];
    const fallbackTemplate =
      templateSource ??
      section.pages?.[section.pages.length - 1]?.template ??
      generalTemplate ??
      {};
    setSections((prev) =>
      insertPage(prev, sectionIndex, section.pages?.length ?? 0, {
        items: [],
        template: { ...fallbackTemplate },
      }),
    );
  }

  function removePageAt(index: number) {
    const entry = flatPages[index];
    if (!entry) return;
    setSections((prev) =>
      removePage(prev, entry.sectionIndex, entry.pageIndex),
    );
  }

  function updatePhotoSelection(pageIndex: number, value: string) {
    setPhotoSelections((prev) => ({ ...prev, [pageIndex]: value }));
  }

  function updatePagePhotos(pageIndex: number, nextIds: string[]) {
    const entry = flatPages[pageIndex];
    if (!entry) return;
    setSections((prev) => {
      const nextPage = { ...entry.page, photo_ids: nextIds };
      return replacePage(prev, entry.sectionIndex, entry.pageIndex, nextPage);
    });
  }

  function setPhotoOrderLocked(pageIndex: number, locked: boolean) {
    const entry = flatPages[pageIndex];
    if (!entry) return;
    setSections((prev) => {
      const nextPage = { ...entry.page, photo_order_locked: locked };
      return replacePage(prev, entry.sectionIndex, entry.pageIndex, nextPage);
    });
  }

  function movePhoto(pageIndex: number, from: number, to: number) {
    const entry = flatPages[pageIndex];
    if (!entry) return;
    setSections((prev) => {
      const ids = [...(entry.page.photo_ids ?? [])];
      if (from < 0 || from >= ids.length || to < 0 || to >= ids.length) {
        return prev;
      }
      const [moved] = ids.splice(from, 1);
      ids.splice(to, 0, moved);
      const nextPage = { ...entry.page, photo_ids: ids };
      return replacePage(prev, entry.sectionIndex, entry.pageIndex, nextPage);
    });
  }

  function removePhoto(pageIndex: number, index: number) {
    const entry = flatPages[pageIndex];
    if (!entry) return;
    setSections((prev) => {
      const ids = [...(entry.page.photo_ids ?? [])];
      ids.splice(index, 1);
      const nextPage = { ...entry.page, photo_ids: ids };
      return replacePage(prev, entry.sectionIndex, entry.pageIndex, nextPage);
    });
  }

  function addPhotoToPage(pageIndex: number, photoId: string) {
    if (!photoId) return;
    const entry = flatPages[pageIndex];
    if (!entry) return;
    setSections((prev) => {
      const ids = [...(entry.page.photo_ids ?? [])];
      if (!ids.includes(photoId)) ids.push(photoId);
      const nextPage = { ...entry.page, photo_ids: ids };
      return replacePage(prev, entry.sectionIndex, entry.pageIndex, nextPage);
    });
  }

  function parseTargetPages(value: string, max: number): number[] {
    const results = new Set<number>();
    const parts = value
      .split(",")
      .map((part) => part.trim())
      .filter(Boolean);
    parts.forEach((part) => {
      if (part.includes("-")) {
        const [startRaw, endRaw] = part.split("-").map((chunk) => chunk.trim());
        const start = Number.parseInt(startRaw, 10);
        const end = Number.parseInt(endRaw, 10);
        if (Number.isNaN(start) || Number.isNaN(end)) return;
        const min = Math.min(start, end);
        const maxRange = Math.max(start, end);
        for (let idx = min; idx <= maxRange; idx += 1) {
          if (idx >= 1 && idx <= max) results.add(idx - 1);
        }
      } else {
        const pageNum = Number.parseInt(part, 10);
        if (!Number.isNaN(pageNum) && pageNum >= 1 && pageNum <= max) {
          results.add(pageNum - 1);
        }
      }
    });
    return Array.from(results).sort((a, b) => a - b);
  }

  function copyPageToTargets() {
    if (!flatPages.length) return;
    const targets = parseTargetPages(copyTargets, flatPages.length).filter(
      (idx) => idx !== copySourceIndex,
    );
    if (!targets.length) {
      setStatus("No valid target pages selected for copy.");
      return;
    }
    const sourceTemplate = flatPages[copySourceIndex]?.page?.template ?? {};
    setSections((prev) =>
      prev.map((section, sIdx) => ({
        ...section,
        pages: (section.pages ?? []).map((page, pIdx) => {
          const flatIndex =
            flattenSections(prev).find(
              (entry) =>
                entry.sectionIndex === sIdx && entry.pageIndex === pIdx,
            )?.flatIndex ?? -1;
          if (!targets.includes(flatIndex)) return page;
          return { ...page, template: { ...sourceTemplate } };
        }),
      })),
    );
    setStatus(
      `Copied page ${copySourceIndex + 1} to ${targets
        .map((idx) => idx + 1)
        .join(", ")}.`,
    );
  }

  function getFallbackValue(field: keyof TemplateFields): string {
    if (!session) return "";
    switch (field) {
      case "inspection_date":
        return session.inspection_date || "";
      case "document_no":
        return session.document_no || formatDocNumber(session);
      default:
        return "";
    }
  }

  async function saveAll(
    silent = false,
    overrideSections?: JobsheetSection[],
  ) {
    if (!sessionId) return;
    if (savePromiseRef.current) {
      await savePromiseRef.current;
    }
    const toSave = overrideSections ?? sections;
    const snapshot = JSON.stringify(toSave);
    if (!overrideSections && snapshot === lastSavedRef.current) {
      if (!isSaving) setSaveState("saved");
      return;
    }
    const promise = (async () => {
      setIsSaving(true);
      setSaveState("saving");
      if (!silent) {
        setStatus("Saving input data...");
      }
      try {
        const resp = await putJson<{ sections: JobsheetSection[] }>(
          `/sessions/${sessionId}/sections`,
          { sections: toSave },
        );
        const updated = ensureSections(resp.sections ?? toSave);
        setSections(updated);
        lastSavedRef.current = JSON.stringify(updated);
        pendingSaveRef.current = lastSavedRef.current;
        setSaveState("saved");
        if (!silent) {
          setStatus("Input data saved.");
        } else {
          setStatus("All changes saved.");
        }
      } catch (err) {
        const message =
          err instanceof Error ? err.message : "Failed to save input data.";
        setStatus(message);
        setSaveState("error");
      } finally {
        setIsSaving(false);
        if (
          pendingSaveRef.current &&
          pendingSaveRef.current !== lastSavedRef.current
        ) {
          if (saveTimerRef.current) window.clearTimeout(saveTimerRef.current);
          saveTimerRef.current = window.setTimeout(() => {
            void triggerAutoSave();
          }, 200);
        }
      }
    })();
    savePromiseRef.current = promise;
    try {
      await promise;
    } finally {
      savePromiseRef.current = null;
    }
  }

  async function triggerAutoSave() {
    if (!sessionId || savePromiseRef.current) return;
    const snapshot = pendingSaveRef.current;
    if (!snapshot || snapshot === lastSavedRef.current) return;
    await saveAll(true);
  }

  useEffect(() => {
    if (!sessionId) return;
    const snapshot = JSON.stringify(sections);
    if (snapshot === lastSavedRef.current) {
      if (!generalDirty && !isSaving) {
        setSaveState("saved");
      }
      return;
    }
    pendingSaveRef.current = snapshot;
    if (!isSaving) {
      setSaveState("pending");
    }
    if (saveTimerRef.current) window.clearTimeout(saveTimerRef.current);
    saveTimerRef.current = window.setTimeout(() => {
      void triggerAutoSave();
    }, 800);
  }, [sections, sessionId, generalDirty, isSaving]);

  useEffect(() => {
    if (!generalDirty) return;
    if (!isSaving) {
      setSaveState("pending");
    }
    if (generalApplyTimerRef.current) {
      window.clearTimeout(generalApplyTimerRef.current);
    }
    generalApplyTimerRef.current = window.setTimeout(() => {
      applyGeneralToAll(true);
    }, 800);
  }, [generalDirty, generalTemplate, isSaving]);

  useEffect(() => {
    if (!sessionId) return;
    if (!headingsLoadedRef.current) return;
    const snapshot = JSON.stringify(headings);
    if (snapshot === lastSavedHeadingsRef.current) return;
    if (headingsSaveTimerRef.current) {
      window.clearTimeout(headingsSaveTimerRef.current);
    }
    headingsSaveTimerRef.current = window.setTimeout(() => {
      void saveHeadings(true);
    }, 800);
  }, [headings, sessionId]);

  async function saveHeadings(silent = false) {
    if (!sessionId) return;
    const snapshot = JSON.stringify(headings);
    if (snapshot === lastSavedHeadingsRef.current) return;
    try {
      if (!silent) {
        setStatus("Saving headings...");
      }
      const resp = await putJson<{ headings: Heading[] }>(
        `/sessions/${sessionId}/headings`,
        { headings },
      );
      const normalized = resp.headings ?? headings;
      setHeadings(normalized);
      lastSavedHeadingsRef.current = JSON.stringify(normalized);
      if (!silent) {
        setStatus("Headings saved.");
      }
    } catch (err) {
      const message =
        err instanceof Error ? err.message : "Failed to save headings.";
      setStatus(message);
    }
  }

  async function uploadDataFile(file: File) {
    if (!sessionId) return;
    setIsUploading(true);
    setStatus("Uploading data file...");
    startUploadProgress();
    try {
      const form = new FormData();
      form.append("file", file);
      await postForm<Session>(`/sessions/${sessionId}/data-files`, form);
      await refreshSession();
      setStatus("Data file imported.");
      setGeneralDirty(false);
      clearUploadTimer();
      setUploadProgress(100);
    } catch (err) {
      const message =
        err instanceof Error ? err.message : "Failed to import data file.";
      setStatus(message);
      clearUploadTimer();
      setUploadProgress(null);
    } finally {
      setIsUploading(false);
      window.setTimeout(() => {
        setUploadProgress(null);
      }, 500);
    }
  }

  async function uploadJsonFile(file: File) {
    if (!sessionId) return;
    setIsUploading(true);
    setStatus("Uploading JSON package...");
    startUploadProgress();
    try {
      const form = new FormData();
      form.append("file", file);
      await postForm<Session>(`/sessions/${sessionId}/import-json`, form);
      await refreshSession();
      setStatus("JSON package imported.");
      setGeneralDirty(false);
      clearUploadTimer();
      setUploadProgress(100);
    } catch (err) {
      const message =
        err instanceof Error ? err.message : "Failed to import JSON package.";
      setStatus(message);
      clearUploadTimer();
      setUploadProgress(null);
    } finally {
      setIsUploading(false);
      window.setTimeout(() => {
        setUploadProgress(null);
      }, 500);
    }
  }

  async function uploadPhotoForPage(pageIndex: number, file: File) {
    if (!sessionId) return;
    setIsUploading(true);
    setStatus(`Uploading image ${file.name}...`);
    startUploadProgress();
    const existing = new Set(
      (session?.uploads?.photos ?? []).map((photo) => photo.id),
    );
    try {
      const form = new FormData();
      form.append("file", file);
      const updated = await postForm<Session>(
        `/sessions/${sessionId}/uploads`,
        form,
      );
      setSession(updated);
      const newPhoto =
        (updated.uploads?.photos ?? []).find((photo) => !existing.has(photo.id)) ??
        (updated.uploads?.photos ?? []).find((photo) => photo.name === file.name);
      if (newPhoto) {
        addPhotoToPage(pageIndex, newPhoto.id);
        setStatus(`Uploaded ${newPhoto.name}.`);
      } else {
        setStatus("Uploaded image. Refresh to see new file.");
      }
      clearUploadTimer();
      setUploadProgress(100);
    } catch (err) {
      const message =
        err instanceof Error ? err.message : "Failed to upload image.";
      setStatus(message);
      clearUploadTimer();
      setUploadProgress(null);
    } finally {
      setIsUploading(false);
      window.setTimeout(() => {
        setUploadProgress(null);
      }, 500);
    }
  }

  async function saveAndNavigate(target: string) {
    if (!sessionId) {
      navigate(target);
      return;
    }
    if (generalApplyTimerRef.current) {
      window.clearTimeout(generalApplyTimerRef.current);
      generalApplyTimerRef.current = null;
    }
    let overrideSections: JobsheetSection[] | undefined;
    if (generalDirty) {
      overrideSections = applyGeneralToSections(sections, generalTemplate);
      setSections(overrideSections);
      setGeneralDirty(false);
    }
    if (saveTimerRef.current) {
      window.clearTimeout(saveTimerRef.current);
      saveTimerRef.current = null;
    }
    const snapshot = JSON.stringify(overrideSections ?? sections);
    pendingSaveRef.current = snapshot;
    if (snapshot !== lastSavedRef.current || savePromiseRef.current) {
      await saveAll(true, overrideSections);
    }
    navigate(target);
  }

  function requestNavigation(target: string) {
    if (saveState === "saving" || saveState === "pending" || isSaving) {
      setPendingNavigationTarget(target);
      return;
    }
    void saveAndNavigate(target);
  }

  async function waitForSaveAndContinue() {
    if (!pendingNavigationTarget) return;
    const target = pendingNavigationTarget;
    setPendingNavigationTarget(null);
    await saveAndNavigate(target);
  }

  function leaveWithoutWaiting() {
    if (!pendingNavigationTarget) return;
    const target = pendingNavigationTarget;
    setPendingNavigationTarget(null);
    navigate(target);
  }

  const saveIndicator = useMemo(() => {
    const base =
      "inline-flex items-center gap-2 rounded-full border px-3 py-1 text-xs font-semibold";
    if (saveState === "saving") {
      return (
        <div className={`${base} border-amber-200 bg-amber-50 text-amber-700`}>
          <span className="h-2 w-2 rounded-full bg-amber-500 animate-pulse" />
          Saving...
        </div>
      );
    }
    if (saveState === "pending") {
      return (
        <div className={`${base} border-amber-200 bg-amber-50 text-amber-700`}>
          <span className="h-2 w-2 rounded-full bg-amber-500" />
          Unsaved changes
        </div>
      );
    }
    if (saveState === "error") {
      return (
        <div className={`${base} border-red-200 bg-red-50 text-red-700`}>
          <span className="h-2 w-2 rounded-full bg-red-500" />
          Save failed
        </div>
      );
    }
    return (
      <div className={`${base} border-emerald-200 bg-emerald-50 text-emerald-700`}>
        <span className="h-2 w-2 rounded-full bg-emerald-500" />
        All changes saved
      </div>
    );
  }, [saveState]);

  return (
    <PageShell className="max-w-6xl">
      <PageHeader
        title="RepEx - Report Express"
        subtitle="Input Data"
        right={
          <div className="flex flex-col items-end gap-2 sm:flex-row sm:items-center">
            {saveIndicator}
            <Link
              to={`/report-viewer${sessionQuery}`}
              onClick={(event) => {
                event.preventDefault();
                requestNavigation(`/report-viewer${sessionQuery}`);
              }}
              className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"
            >
              <ArrowLeft className="h-4 w-4" />
              Back
            </Link>
            <InfoMenu sessionQuery={sessionQuery} />
          </div>
        }
      />

      <nav className="mb-6" aria-label="Report workflow navigation">
        <div className="flex flex-wrap gap-2">
          <span className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-900 px-4 py-2 text-sm font-semibold text-white">
            <Table className="h-4 w-4" />
            Input Data
          </span>

          <Link
            to={`/report-viewer${sessionQuery}`}
            onClick={(event) => {
              event.preventDefault();
              requestNavigation(`/report-viewer${sessionQuery}`);
            }}
            className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"
          >
            <Layout className="h-4 w-4" />
            Report Viewer
          </Link>

          <Link
            to={`/image-placement${sessionQuery}`}
            onClick={(event) => {
              event.preventDefault();
              requestNavigation(`/image-placement${sessionQuery}`);
            }}
            className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"
          >
            <Image className="h-4 w-4" />
            Image Placement
          </Link>

          <Link
            to={`/edit-report${sessionQuery}`}
            onClick={(event) => {
              event.preventDefault();
              requestNavigation(`/edit-report${sessionQuery}`);
            }}
            className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"
          >
            <Edit3 className="h-4 w-4" />
            Edit Report
          </Link>

          <Link
            to={`/edit-layouts${sessionQuery}`}
            onClick={(event) => {
              event.preventDefault();
              requestNavigation(`/edit-layouts${sessionQuery}`);
            }}
            className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"
          >
            <Grid className="h-4 w-4" />
            Edit Page Layouts
          </Link>

          <Link
            to={`/export${sessionQuery}`}
            onClick={(event) => {
              event.preventDefault();
              requestNavigation(`/export${sessionQuery}`);
            }}
            className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"
          >
            <Download className="h-4 w-4" />
            Export
          </Link>
        </div>
      </nav>

      <section className="mb-4 rounded-lg border border-gray-200 bg-white p-4">
        <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
          <div>
            <h2 className="text-lg font-semibold text-gray-900">Job Sheet Data</h2>
            <p className="text-sm text-gray-600">
              Update job sheet fields per page. Use "Apply row to all" to copy a
              page's fields across every job sheet.
            </p>
          </div>
          <div className="flex flex-wrap gap-2">
            <div className="flex items-center gap-2">
              <select
                className="rounded-md border border-gray-200 px-2 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200"
                value={addSectionId}
                onChange={(event) => setAddSectionId(event.target.value)}
                disabled={!sections.length}
              >
                {sections.map((section, idx) => (
                  <option key={section.id} value={section.id}>
                    {section.title || `Section ${idx + 1}`}
                  </option>
                ))}
              </select>
              <button
                type="button"
                onClick={() => insertPageIntoSection(addSectionId, generalTemplate)}
                disabled={!sessionId || !addSectionId}
                className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition disabled:opacity-50 disabled:cursor-not-allowed"
              >
                Add blank page
              </button>
            </div>
            <button
              type="button"
              onClick={addSection}
              disabled={!sessionId}
              className="inline-flex items-center gap-2 rounded-lg bg-emerald-600 px-4 py-2 text-sm font-semibold text-white hover:bg-emerald-700 transition disabled:opacity-50 disabled:cursor-not-allowed"
            >
              Add section
            </button>
          </div>
        </div>
        {status ? <p className="text-sm text-gray-600 mt-3">{status}</p> : null}
        <LoadingBar
          active={isSaving}
          progress={null}
          label="Saving changes"
          className="mt-3"
        />
        <LoadingBar
          active={isUploading}
          progress={uploadProgress}
          label={status || "Uploading files"}
          className="mt-3"
        />
      </section>

      <section className="mb-4 rounded-lg border border-gray-200 bg-white p-4">
        <div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4">
          <div>
            <h3 className="text-base font-semibold text-gray-900">Copy page to targets</h3>
            <p className="text-sm text-gray-600">
              Choose a source page and the pages to overwrite (e.g. 2,4-6).
            </p>
          </div>
          <div className="flex flex-wrap items-end gap-2">
            <label className="text-xs text-gray-600">
              Source page
              <select
                className="mt-1 w-36 rounded-md border border-gray-200 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200"
                value={copySourceIndex}
                onChange={(event) => setCopySourceIndex(Number(event.target.value))}
              >
                {flatPages.map((_, idx) => (
                  <option key={`copy-source-${idx}`} value={idx}>
                    Page {idx + 1}
                  </option>
                ))}
              </select>
            </label>
            <label className="text-xs text-gray-600">
              Target pages
              <input
                type="text"
                placeholder="e.g. 2,4-6"
                className="mt-1 w-44 rounded-md border border-gray-200 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200"
                value={copyTargets}
                onChange={(event) => setCopyTargets(event.target.value)}
              />
            </label>
            <button
              type="button"
              onClick={copyPageToTargets}
              disabled={!flatPages.length}
              className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition disabled:opacity-50 disabled:cursor-not-allowed"
            >
              Copy page data
            </button>
          </div>
        </div>
      </section>

      <section className="mb-4 rounded-lg border border-gray-200 bg-white p-4">
        <div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-3">
          <div>
            <h3 className="text-base font-semibold text-gray-900">
              Import / Export data files
            </h3>
            <p className="text-sm text-gray-600">
              Upload an Excel/CSV data file or a JSON package to populate job sheets.
            </p>
          </div>
          <div className="flex flex-wrap gap-2">
            <button
              type="button"
              onClick={() => excelInputRef.current?.click()}
              disabled={!sessionId || isUploading}
              className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition disabled:opacity-50 disabled:cursor-not-allowed"
            >
              Upload Excel/CSV
            </button>
            <button
              type="button"
              onClick={() => jsonInputRef.current?.click()}
              disabled={!sessionId || isUploading}
              className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition disabled:opacity-50 disabled:cursor-not-allowed"
            >
              Upload JSON
            </button>
            <a
              href={
                sessionId
                  ? `${API_BASE}/sessions/${sessionId}/export.xlsx`
                  : "#"
              }
              className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"
            >
              Download Excel
            </a>
            <a
              href={
                sessionId
                  ? `${API_BASE}/sessions/${sessionId}/export`
                  : "#"
              }
              className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition"
            >
              Download JSON
            </a>
          </div>
        </div>
        <input
          ref={excelInputRef}
          type="file"
          accept=".xlsx,.xls,.csv"
          className="hidden"
          onChange={(event) => {
            const file = event.target.files?.[0];
            if (file) uploadDataFile(file);
            event.target.value = "";
          }}
        />
        <input
          ref={jsonInputRef}
          type="file"
          accept=".json,application/json"
          className="hidden"
          onChange={(event) => {
            const file = event.target.files?.[0];
            if (file) uploadJsonFile(file);
            event.target.value = "";
          }}
        />
      </section>

      <section className="mb-4 rounded-lg border border-gray-200 bg-white p-4">
        <div className="grid grid-cols-1 lg:grid-cols-[minmax(0,1fr),320px] gap-4 items-start">
          <div className="min-w-0">
            <div className="flex flex-wrap items-center justify-between gap-2">
              <div>
                <h3 className="text-base font-semibold text-gray-900">
                  General Information
                </h3>
                <p className="text-sm text-gray-600">
                  Update the global inspection details once, then apply to all pages.
                </p>
              </div>
              <div className="flex flex-wrap gap-2">
                {generalDirty ? (
                  <button
                    type="button"
                    onClick={applyGeneralToAll}
                    className="inline-flex items-center gap-2 rounded-lg bg-emerald-600 px-4 py-2 text-sm font-semibold text-white hover:bg-emerald-700 transition"
                  >
                    Apply general info to all pages
                  </button>
                ) : null}
              </div>
            </div>

            <div className="mt-4 rounded-lg border border-gray-200 bg-white overflow-x-auto w-full max-w-full">
              <table className="min-w-[560px] w-full text-sm">
                <thead className="bg-gray-50 border-b border-gray-200">
                  <tr>
                    <th className="px-3 py-2 text-left text-xs font-semibold text-gray-600 min-w-[180px]">
                      Field
                    </th>
                    <th className="px-3 py-2 text-left text-xs font-semibold text-gray-600">
                      Value
                    </th>
                  </tr>
                </thead>
                <tbody>
                  {GENERAL_FIELDS.map((field) => (
                    <tr key={`general-${field.key}`} className="border-b border-gray-100">
                      <td className="px-3 py-2 text-xs font-semibold text-gray-700">
                        {field.label}
                      </td>
                      <td className="px-3 py-2">
                        <input
                          type="text"
                          className="w-full rounded-md border border-gray-200 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200"
                          value={generalTemplate[field.key] ?? getFallbackValue(field.key)}
                          onChange={(event) =>
                            updateGeneralField(field.key, event.target.value)
                          }
                        />
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>

          <div className="w-full lg:w-[320px] shrink-0">
            <div className="flex flex-wrap items-center justify-between gap-2">
              <div>
                <h3 className="text-base font-semibold text-gray-900">Headings</h3>
                <p className="text-sm text-gray-600">
                  Edit heading numbers or add new rows.
                </p>
              </div>
              <button
                type="button"
                onClick={addHeadingRow}
                className="inline-flex items-center gap-2 rounded-md border border-gray-200 bg-white px-3 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50 transition"
              >
                Add
              </button>
            </div>
            <div className="mt-3 rounded-lg border border-gray-200 bg-white overflow-x-auto">
              <table className="min-w-[360px] w-full text-sm">
                <thead className="bg-gray-50 border-b border-gray-200">
                  <tr>
                    <th className="px-3 py-2 text-left text-xs font-semibold text-gray-600">
                      Number
                    </th>
                    <th className="px-3 py-2 text-left text-xs font-semibold text-gray-600">
                      Heading
                    </th>
                    <th className="px-3 py-2 text-left text-xs font-semibold text-gray-600 w-20">
                      Actions
                    </th>
                  </tr>
                </thead>
                <tbody>
                  {headings.length ? (
                    headings.map((heading, idx) => (
                      <tr key={`heading-${idx}`} className="border-b border-gray-100">
                        <td className="px-3 py-2 text-xs font-semibold text-gray-700">
                          <input
                            type="text"
                            className="w-full rounded-md border border-gray-200 px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-blue-200"
                            value={heading.number}
                            onChange={(event) =>
                              updateHeadingField(idx, "number", event.target.value)
                            }
                          />
                        </td>
                        <td className="px-3 py-2 text-sm text-gray-700">
                          <input
                            type="text"
                            className="w-full rounded-md border border-gray-200 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200"
                            value={heading.name}
                            onChange={(event) =>
                              updateHeadingField(idx, "name", event.target.value)
                            }
                          />
                        </td>
                        <td className="px-3 py-2 text-xs">
                          <button
                            type="button"
                            onClick={() => removeHeadingRow(idx)}
                            className="inline-flex items-center gap-1 rounded-md border border-red-200 bg-red-50 px-2 py-1 text-[11px] font-semibold text-red-700 hover:bg-red-100"
                          >
                            Remove
                          </button>
                        </td>
                      </tr>
                    ))
                  ) : (
                    <tr>
                      <td
                        colSpan={3}
                        className="px-3 py-4 text-sm text-gray-500 text-center"
                      >
                        No headings found.
                      </td>
                    </tr>
                  )}
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </section>

      <section className="mb-4 rounded-lg border border-gray-200 bg-white p-3">
        <div className="flex flex-wrap items-center justify-between gap-2">
          <div>
            <h3 className="text-sm font-semibold text-gray-900">Job Sheet Sections</h3>
            <p className="text-xs text-gray-600">
              Rename sections and add pages directly inside a section.
            </p>
          </div>
          <div className="flex items-center gap-2">
            <button
              type="button"
              onClick={() => setSectionsCollapsed((prev) => !prev)}
              className="inline-flex items-center gap-2 rounded-md border border-gray-200 bg-white px-3 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50 transition"
            >
              {sectionsCollapsed ? "Expand" : "Collapse"}
            </button>
            <button
              type="button"
              onClick={addSection}
              className="inline-flex items-center gap-2 rounded-md border border-gray-200 bg-white px-3 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50 transition"
            >
              Add section
            </button>
          </div>
        </div>
        {sectionsCollapsed ? (
          <div className="mt-2 text-[11px] text-gray-500">
            {sections.length} section{sections.length === 1 ? "" : "s"} ·{" "}
            {sections.reduce((sum, section) => sum + (section.pages?.length ?? 0), 0)}{" "}
            page{sections.length === 1 ? "" : "s"} total
          </div>
        ) : (
          <div className="mt-2 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
            {sections.map((section, idx) => (
              <div
                key={section.id}
                className="rounded-md border border-gray-200 bg-gray-50 p-2"
              >
                <div className="flex items-center justify-between gap-2">
                  <div className="text-[11px] font-semibold text-gray-700">
                    Section {idx + 1}
                  </div>
                  <button
                    type="button"
                    onClick={() => insertPageIntoSection(section.id)}
                    className="inline-flex items-center gap-1 rounded-md border border-gray-200 bg-white px-2 py-0.5 text-[11px] font-semibold text-gray-700 hover:bg-gray-50 transition"
                  >
                    Add page
                  </button>
                </div>
                <div className="mt-1 text-[11px] text-gray-500">
                  Pages: {section.pages?.length ?? 0}
                </div>
                <div className="text-[11px] text-gray-500">
                  Page numbers:{" "}
                  {(() => {
                    const start =
                      sections
                        .slice(0, idx)
                        .reduce((sum, item) => sum + (item.pages?.length ?? 0), 0) +
                      1;
                    const count = section.pages?.length ?? 0;
                    if (count <= 0) return "-";
                    const end = start + count - 1;
                    return start === end ? `${start}` : `${start}-${end}`;
                  })()}
                </div>
                <label className="mt-1 block text-[11px] text-gray-600">
                  Title
                  <input
                    type="text"
                    className="mt-1 w-full rounded-md border border-gray-200 px-2 py-0.5 text-xs focus:outline-none focus:ring-2 focus:ring-blue-200"
                    value={section.title ?? ""}
                    onChange={(event) =>
                      updateSectionTitle(section.id, event.target.value)
                    }
                    placeholder={`Section ${idx + 1}`}
                  />
                </label>
              </div>
            ))}
          </div>
        )}
      </section>

      <div className="rounded-lg border border-gray-200 bg-white overflow-x-auto">
        <div className="px-3 py-2 text-xs text-gray-500 bg-gray-50 border-b border-gray-200">
          General Info (double-click the header below to expand/collapse columns)
        </div>
        <table className="min-w-[2400px] w-full text-sm">
          <thead className="bg-gray-50 border-b border-gray-200">
            <tr>
              <th
                rowSpan={2}
                className="px-3 py-2 text-left text-xs font-semibold text-gray-600"
              >
                Page
              </th>
              <th
                colSpan={showGeneralColumns ? GENERAL_FIELDS.length : 1}
                onDoubleClick={() => setShowGeneralColumns((prev) => !prev)}
                className="px-3 py-2 text-left text-xs font-semibold text-gray-600 cursor-pointer select-none"
                title="Double-click to toggle general columns"
              >
                General Info
              </th>
              <th
                colSpan={ITEM_FIELDS.length + 1}
                className="px-3 py-2 text-left text-xs font-semibold text-gray-600"
              >
                Item Details
              </th>
              <th
                rowSpan={2}
                className="px-3 py-2 text-left text-xs font-semibold text-gray-600 min-w-[520px]"
              >
                Actions
              </th>
            </tr>
            <tr>
              {showGeneralColumns ? (
                GENERAL_FIELDS.map((field) => (
                  <th
                    key={`general-col-${field.key}`}
                    className="px-3 py-2 text-left text-xs font-semibold text-gray-600 min-w-[180px]"
                  >
                    {field.label}
                  </th>
                ))
              ) : (
                <th className="px-3 py-2 text-left text-xs font-semibold text-gray-500 min-w-[180px]">
                  General info hidden
                </th>
              )}
              {ITEM_FIELDS.map((field) => (
                <th
                  key={`item-col-${field.key}`}
                  className="px-3 py-2 text-left text-xs font-semibold text-gray-600 min-w-[180px]"
                >
                  {field.label}
                </th>
              ))}
              <th className="px-3 py-2 text-left text-xs font-semibold text-gray-600 min-w-[320px]">
                Images
              </th>
            </tr>
          </thead>
          <tbody>
            {flatPages.map((entry) => {
              const pageIndex = entry.flatIndex;
              const template = entry.page.template ?? {};
              const photoIds = entry.page.photo_ids ?? [];
              const orderLocked = entry.page.photo_order_locked ?? false;
              const photoLookup = new Map(
                (session?.uploads?.photos ?? []).map((photo) => [photo.id, photo]),
              );
              const availablePhotos = (session?.uploads?.photos ?? []).filter(
                (photo) => !photoIds.includes(photo.id),
              );
              return (
                <tr key={`row-${entry.sectionId}-${entry.pageIndex}`} className="border-b border-gray-100">
                  <td className="px-3 py-2 text-xs font-semibold text-gray-700">
                    Page {entry.flatIndex + 1}
                  </td>
                  {showGeneralColumns ? (
                    GENERAL_FIELDS.map((field) => (
                      <td
                        key={`${pageIndex}-general-${field.key}`}
                        className="px-3 py-2 min-w-[180px]"
                      >
                        <input
                          type="text"
                          className="w-full rounded-md border border-gray-200 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200"
                          value={template[field.key] ?? getFallbackValue(field.key)}
                          onChange={(event) =>
                            updateField(pageIndex, field.key, event.target.value)
                          }
                        />
                      </td>
                    ))
                  ) : (
                    <td className="px-3 py-2 min-w-[180px] text-xs text-gray-500">
                      (hidden)
                    </td>
                  )}
                  {ITEM_FIELDS.map((field) => (
                    <td key={`${pageIndex}-${field.key}`} className="px-3 py-2 min-w-[180px]">
                      {field.multiline ? (
                        <textarea
                          rows={2}
                          className="w-full rounded-md border border-gray-200 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200"
                          value={template[field.key] ?? getFallbackValue(field.key)}
                          onChange={(event) =>
                            updateField(pageIndex, field.key, event.target.value)
                          }
                        />
                      ) : (
                        <input
                          type="text"
                          className="w-full rounded-md border border-gray-200 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-200"
                          value={template[field.key] ?? getFallbackValue(field.key)}
                          onChange={(event) =>
                            updateField(pageIndex, field.key, event.target.value)
                          }
                        />
                      )}
                    </td>
                  ))}
                  <td className="px-3 py-2 min-w-[320px]">
                    <div className="space-y-2">
                      {photoIds.length ? (
                        photoIds.map((photoId, idx) => {
                          const photo = photoLookup.get(photoId);
                          return (
                            <div key={`${pageIndex}-photo-${photoId}`} className="flex flex-wrap items-center gap-2 text-xs">
                              <span className="font-semibold text-gray-700">
                                {photo?.name || photoId}
                              </span>
                              <button
                                type="button"
                                onClick={() => movePhoto(pageIndex, idx, idx - 1)}
                                disabled={idx === 0}
                                className="rounded border border-gray-200 px-2 py-0.5 text-[11px] text-gray-600 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
                              >
                                Up
                              </button>
                              <button
                                type="button"
                                onClick={() => movePhoto(pageIndex, idx, idx + 1)}
                                disabled={idx === photoIds.length - 1}
                                className="rounded border border-gray-200 px-2 py-0.5 text-[11px] text-gray-600 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
                              >
                                Down
                              </button>
                              <button
                                type="button"
                                onClick={() => removePhoto(pageIndex, idx)}
                                className="rounded border border-red-200 bg-red-50 px-2 py-0.5 text-[11px] font-semibold text-red-700 hover:bg-red-100"
                              >
                                Remove
                              </button>
                            </div>
                          );
                        })
                      ) : (
                        <div className="text-xs text-gray-500">No images linked.</div>
                      )}
                      <div className="flex flex-wrap items-center gap-2">
                        <select
                          className="rounded-md border border-gray-200 px-2 py-1 text-xs focus:outline-none focus:ring-2 focus:ring-blue-200"
                          value={photoSelections[pageIndex] ?? ""}
                          onChange={(event) =>
                            updatePhotoSelection(pageIndex, event.target.value)
                          }
                        >
                          <option value="">Select image</option>
                          {availablePhotos.map((photo) => (
                            <option key={`photo-option-${photo.id}`} value={photo.id}>
                              {photo.name}
                            </option>
                          ))}
                        </select>
                        <button
                          type="button"
                          onClick={() =>
                            addPhotoToPage(pageIndex, photoSelections[pageIndex] ?? "")
                          }
                          disabled={!photoSelections[pageIndex]}
                          className="rounded border border-gray-200 px-2 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
                        >
                          Add
                        </button>
                        <button
                          type="button"
                          onClick={() => uploadInputRefs.current[pageIndex]?.click()}
                          disabled={isUploading}
                          className="rounded border border-gray-200 px-2 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
                        >
                          Upload
                        </button>
                        <button
                          type="button"
                          onClick={() => setPhotoOrderLocked(pageIndex, !orderLocked)}
                          disabled={photoIds.length === 0}
                          className="rounded border border-gray-200 px-2 py-1 text-xs font-semibold text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
                        >
                          {orderLocked ? "Auto order" : "Apply order"}
                        </button>
                        <span className="text-[11px] text-gray-500">
                          {orderLocked ? "Manual order" : "Auto order"}
                        </span>
                        <input
                          ref={(node) => {
                            uploadInputRefs.current[pageIndex] = node;
                          }}
                          type="file"
                          accept="image/*"
                          className="hidden"
                          onChange={(event) => {
                            const file = event.target.files?.[0];
                            if (file) uploadPhotoForPage(pageIndex, file);
                            event.target.value = "";
                          }}
                        />
                      </div>
                    </div>
                  </td>
                  <td className="px-3 py-2 min-w-[520px]">
                    <div className="flex flex-wrap items-center gap-2">
                      <button
                        type="button"
                        onClick={() => applyRowToAll(pageIndex)}
                        className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-xs font-semibold text-gray-700 hover:bg-gray-50 transition"
                      >
                        Apply row to all
                      </button>
                      <button
                        type="button"
                        onClick={() => insertPageAt(pageIndex, template)}
                        className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-xs font-semibold text-gray-700 hover:bg-gray-50 transition"
                      >
                        Insert above
                      </button>
                      <button
                        type="button"
                        onClick={() => insertPageAt(pageIndex + 1, template)}
                        className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-xs font-semibold text-gray-700 hover:bg-gray-50 transition"
                      >
                        Insert below
                      </button>
                      <button
                        type="button"
                        onClick={() => removePageAt(pageIndex)}
                        disabled={flatPages.length <= 1}
                        className="inline-flex items-center gap-2 rounded-lg border border-red-200 bg-red-50 px-3 py-1.5 text-xs font-semibold text-red-700 hover:bg-red-100 transition disabled:opacity-50 disabled:cursor-not-allowed"
                      >
                        Delete page
                      </button>
                    </div>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      <div className="mt-3 flex justify-end">
        <button
          type="button"
          onClick={() => insertPageAt(flatPages.length, flatPages[flatPages.length - 1]?.page?.template)}
          disabled={!sessionId}
          className="inline-flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm font-semibold text-gray-800 hover:bg-gray-50 transition disabled:opacity-50 disabled:cursor-not-allowed"
        >
          Add page at end
        </button>
      </div>

      <PageFooter note="Tip: changes save automatically. Use apply row to keep pages consistent." />
      <SaveBeforeLeaveDialog
        open={Boolean(pendingNavigationTarget)}
        onCancel={() => setPendingNavigationTarget(null)}
        onProceed={leaveWithoutWaiting}
        onWait={waitForSaveAndContinue}
      />
    </PageShell>
  );
}
