import React, { Suspense, lazy, useEffect, useMemo, useRef, useState } from "react";
import {
  fetchAssignmentDocumentPages,
  fetchAssignmentForm,
  copyAssignmentPage,
  deleteAssignmentPage,
  reorderAssignmentPages,
  saveAssignmentForm,
  startAssignmentWork,
} from "../../services/swiApi.js";
import {
  canComplete,
  canEditForm,
  canStartWork,
  isCompleted,
  statusBadgeClass,
} from "../../constants/swiDocumentStatus.js";
import { getApiErrorMessage } from "../../utils/apiError.js";
import CopyPageModal from "../../components/swi/CopyPageModal.jsx";
import SubmitDocumentConfirmModal from "../../components/swi/SubmitDocumentConfirmModal.jsx";
import RemovePageConfirmModal from "../../components/swi/RemovePageConfirmModal.jsx";
import LogoImagePicker from "../../components/swi/LogoImagePicker.jsx";

const ImageEditorModal = lazy(() => import("../../components/swi/ImageEditorModal.jsx"));
import PictureCanvasColumn from "../../components/swi/PictureCanvasColumn.jsx";
import PageOrderBar from "../../components/swi/PageOrderBar.jsx";
import DocumentFloatingActions from "../../components/swi/DocumentFloatingActions.jsx";
import {
  clampCanvasItem,
  createCanvasItem,
  fitCanvasItemPercent,
  measureDataUrlDimensions,
  normalizeCanvasItems,
  renumberCanvasLabels,
} from "../../utils/pictureCanvas.js";
import {
  computePictureRowSpans,
  itemsForPictureCell,
  pictureMergeNextFlags,
  previewPictureCellHeightPx,
  replaceRowCanvasItems,
  setPictureMergeNext,
} from "../../utils/pictureRowLayout.js";
import {
  LOGOS_PER_BLOCK,
  normalizeBlockIcons,
  setBlockIconSlot,
} from "../../utils/blockIcons.js";
import {
  MAX_TEXT_BLOCKS,
  MIN_TEXT_BLOCKS,
  normalizeTextBlocks,
} from "../../utils/textBlocks.js";
import {
  addPageRow,
  applyDomTextBlockHeightsToPageForms,
  canAddPageRow,
  MIN_ROW_HEIGHT_PX,
  remainingHeightPercent,
  removePageRow,
  resolvePreviewRowHeightsPx,
  resolvePageContentHeightPx,
  updatePageRowText,
} from "../../utils/pageContentRows.js";
import {
  apiPageRecordToPageForm,
  buildOrderSignature,
  buildPageOrderPayload,
  buildOriginalPositions,
  clonePageFormFromSource,
  createBlankPageForm,
  formPayloadToPageForm,
  isPageOrderDirty,
  mergeSavePayload,
  pageMoveHint,
  pickSharedForm,
  restorePageOrder,
  syncPageNumbers,
} from "../../utils/documentPageForm.js";
import "../../styles/documentBuilder.css";

const MARKER_ITEMS = [
  { key: "tips", label: "TIPS", symbol: "+" },
  { key: "quality", label: "QUALITY", symbol: "●" },
  { key: "safety", label: "SAFETY", symbol: "◆" },
  { key: "visual", label: "VISUAL", symbol: "◉" },
];

function DocumentDetailsHeader({
  form,
  isAdminField,
  onFieldChange,
  hidePageField = false,
}) {
  const locked = (name) => isAdminField.has(name);

  const cellInput = (name, label) => (
    <label className="doc-cell form-doc-cell" key={name}>
      <span className="doc-cell-label">{label}</span>
      <input
        type="text"
        name={name}
        value={form[name] ?? ""}
        readOnly={locked(name)}
        onChange={(e) => onFieldChange(name, e.target.value)}
      />
    </label>
  );

  return (
    <div className="document-wrap document-wrap--form document-wrap--details document-wrap--summary">
      <div className="doc-margin doc-margin--form">
        <label className="form-margin-field">
          <span className="form-field-tag">Top left</span>
          <input
            type="text"
            name="margin_date"
            value={form.margin_date}
            readOnly={locked("margin_date")}
            onChange={(e) => onFieldChange("margin_date", e.target.value)}
          />
        </label>
        <label className="form-margin-field form-margin-field--center">
          <span className="form-field-tag">Top center</span>
          <input
            type="text"
            name="margin_ref"
            value={form.margin_ref}
            readOnly={locked("margin_ref")}
            onChange={(e) => onFieldChange("margin_ref", e.target.value)}
          />
        </label>
        <span className="form-margin-spacer" />
      </div>

      <div className="doc-header-grid">
        <div className="doc-row doc-row--form">
          {cellInput("station", "Station")}
          {cellInput("job_area", "Job Area")}
          {cellInput("document_no", "Document N°")}
          {cellInput("issue_date", "Issue Date")}
          {cellInput("part_number", "Part Number N°")}
          {!hidePageField ? cellInput("page", "Page") : null}
        </div>
        <div className="doc-row-2 doc-row-2--form">
          {cellInput("description", "Description")}
          {cellInput("process", "Process")}
        </div>
      </div>
    </div>
  );
}

function TextBlockField({
  blockIndex,
  value,
  heightPx,
  formLocked,
  onTextChange,
  onHeightChange,
}) {
  const textareaRef = useRef(null);
  const lastMeasuredRef = useRef(null);

  const applyAutoHeight = () => {
    const el = textareaRef.current;
    if (!el) return null;
    // Auto-grow to content while preserving fixed column widths.
    el.style.height = "auto";
    const measured = Math.max(MIN_ROW_HEIGHT_PX, Math.round(el.scrollHeight));
    el.style.height = `${measured}px`;
    return measured;
  };

  const persistHeight = () => {
    const measured = applyAutoHeight();
    if (measured == null || formLocked) return;
    if (lastMeasuredRef.current !== measured || measured !== heightPx) {
      lastMeasuredRef.current = measured;
      onHeightChange(measured);
    }
  };

  useEffect(() => {
    const measured = applyAutoHeight();
    if (measured == null) return;
    lastMeasuredRef.current = measured;
  }, [value, heightPx]);

  return (
    <label className="doc-text-block doc-text-block--form doc-content-row__text">
      <textarea
        ref={textareaRef}
        name={`block${blockIndex}`}
        className="form-textarea-block form-textarea-block--sized"
        value={value ?? ""}
        readOnly={formLocked}
        onChange={(event) => {
          onTextChange(event.target.value);
          requestAnimationFrame(persistHeight);
        }}
        onMouseUp={persistHeight}
        onPointerUp={persistHeight}
        onBlur={persistHeight}
        style={{ minHeight: MIN_ROW_HEIGHT_PX }}
      />
    </label>
  );
}

function PageMarkerPanel({ markers = {}, formLocked, onChange }) {
  return (
    <div className="doc-page-marker-panel" aria-label="Page operation markers">
      <span className="doc-page-marker-title">Operation markers:</span>
      {MARKER_ITEMS.map((item) => (
        <label key={item.key} className="doc-row-marker-option">
          <input
            type="checkbox"
            checked={Boolean(markers[item.key])}
            disabled={formLocked}
            onChange={(event) =>
              onChange?.({
                ...markers,
                [item.key]: event.target.checked,
              })
            }
          />
          <span className={`doc-row-marker-symbol doc-row-marker-symbol--${item.key}`}>
            {item.symbol}
          </span>
          <span className="doc-row-marker-text">{item.label}</span>
        </label>
      ))}
    </div>
  );
}

function BlockLogoSlots({
  blockNum,
  blockIcons,
  slotCount,
  formLocked,
  icons,
  onIconChange,
  onAddSlot,
  onRemoveSlot,
}) {
  const slots = blockIcons[String(blockNum)] || Array(LOGOS_PER_BLOCK).fill("");

  return (
    <div className="doc-block-logo-slots">
      <span className="form-field-tag doc-block-logo-slots__label">
        Logos ({slotCount}/{LOGOS_PER_BLOCK})
      </span>
      <div className="doc-block-logo-grid">
        {Array.from({ length: slotCount }, (_, slotIndex) => (
          <LogoImagePicker
            key={slotIndex}
            value={slots[slotIndex] || ""}
            icons={icons}
            slotLabel={`Block ${blockNum} logo ${slotIndex + 1}`}
            onChange={(iconId) => onIconChange(blockNum, slotIndex, iconId)}
          />
        ))}
      </div>
      {!formLocked ? (
        <div className="doc-block-logo-actions">
          <button
            type="button"
            className="swi-btn swi-btn--ghost swi-btn--compact"
            disabled={slotCount >= LOGOS_PER_BLOCK}
            onClick={onAddSlot}
          >
            + Logo
          </button>
          <button
            type="button"
            className="swi-btn swi-btn--ghost swi-btn--compact"
            disabled={slotCount <= 1}
            onClick={onRemoveSlot}
          >
            - Logo
          </button>
        </div>
      ) : null}
    </div>
  );
}

function DocumentPageSection({
  pageForm,
  pageIndex,
  totalPages,
  sharedForm,
  icons,
  isAdminField,
  onSharedFieldChange,
  onPageChange,
  onCanvasChange,
  onCanvasAddImage,
  onCanvasEditImage,
  onCanvasClear,
  onPictureMergeChange,
  sectionId,
  moveHint,
  onDuplicatePage,
  onRemovePage,
  canRemovePage,
  formLocked,
  removingPage,
}) {
  const textBlocks = normalizeTextBlocks(pageForm);
  const rowCount = textBlocks.length;
  const blockIcons = normalizeBlockIcons(pageForm, pageForm, rowCount);
  const blockLogoCounts = pageForm.block_logo_counts || {};
  const canAddRow = canAddPageRow(pageForm);
  const spaceLeftPct = remainingHeightPercent(textBlocks);
  const canvasItems = normalizeCanvasItems(pageForm);
  const rowHeightsPx = useMemo(
    () => resolvePreviewRowHeightsPx(textBlocks),
    [textBlocks]
  );
  const pageContentHeightPx = useMemo(
    () => resolvePageContentHeightPx(textBlocks),
    [textBlocks]
  );
  const mergeNextFlags = useMemo(
    () => pictureMergeNextFlags(pageForm, rowCount),
    [pageForm, rowCount]
  );
  const pictureRowLayout = useMemo(
    () => computePictureRowSpans(rowCount, mergeNextFlags),
    [rowCount, mergeNextFlags]
  );
  const pageMarkers = {
    tips: Boolean(pageForm.page_markers?.tips ?? pageForm.marker_tips),
    quality: Boolean(pageForm.page_markers?.quality ?? pageForm.marker_quality),
    safety: Boolean(pageForm.page_markers?.safety ?? pageForm.marker_safety),
    visual: Boolean(pageForm.page_markers?.visual ?? pageForm.marker_visual),
  };

  const setPageField = (key, value) => onPageChange(pageIndex, { [key]: value });

  const applyPageRowPatch = (patch) => onPageChange(pageIndex, patch);

  const setPageMarkers = (nextMarkers) => {
    onPageChange(pageIndex, {
      page_markers: nextMarkers,
      marker_tips: Boolean(nextMarkers.tips),
      marker_quality: Boolean(nextMarkers.quality),
      marker_safety: Boolean(nextMarkers.safety),
      marker_visual: Boolean(nextMarkers.visual),
    });
  };

  const setBlockIcon = (blockNum, slotIndex, iconId) => {
    onPageChange(pageIndex, {
      block_icons: setBlockIconSlot(blockIcons, blockNum, slotIndex, iconId),
    });
  };

  const logoSlotCountForBlock = (blockNum) => {
    const key = String(blockNum);
    const n = Number(blockLogoCounts[key]);
    if (!Number.isFinite(n)) return 1;
    return Math.max(1, Math.min(LOGOS_PER_BLOCK, Math.trunc(n)));
  };

  const setBlockLogoCount = (blockNum, nextCount) => {
    const key = String(blockNum);
    const count = Math.max(1, Math.min(LOGOS_PER_BLOCK, Math.trunc(Number(nextCount) || 1)));
    onPageChange(pageIndex, {
      block_logo_counts: {
        ...blockLogoCounts,
        [key]: count,
      },
    });
  };

  const removeLastLogoSlot = (blockNum) => {
    const key = String(blockNum);
    const current = logoSlotCountForBlock(blockNum);
    if (current <= 1) return;
    const nextCount = current - 1;
    const nextSlots = [...(blockIcons[key] || Array(LOGOS_PER_BLOCK).fill(""))];
    nextSlots[nextCount] = "";
    onPageChange(pageIndex, {
      block_icons: {
        ...blockIcons,
        [key]: nextSlots,
      },
      block_logo_counts: {
        ...blockLogoCounts,
        [key]: nextCount,
      },
    });
  };

  return (
    <article
      id={sectionId}
      className={`doc-scroll-page${moveHint ? " doc-scroll-page--moved" : ""}`}
      aria-label={`Page ${pageForm.page_num}`}
    >
      <header className="doc-scroll-page__head">
        <div className="doc-scroll-page__head-main">
          <h2 className="doc-scroll-page__title">
            Page {pageForm.page_num} of {totalPages}
          </h2>
          {moveHint ? (
            <span className="doc-scroll-page__moved-badge">
              Moved from position {moveHint.from}
            </span>
          ) : null}
        </div>
        {pageForm.page_id ? (
          <span className="swi-muted small">Page ID {pageForm.page_id}</span>
        ) : (
          <span className="swi-muted small">New page — saved on Save document</span>
        )}
        {!formLocked ? (
          <div className="doc-scroll-page__actions">
            <button
              type="button"
              className="swi-btn swi-btn--ghost swi-btn--compact doc-page-duplicate-btn"
              disabled={removingPage}
              onClick={() => onDuplicatePage(pageIndex)}
            >
              Duplicate page
            </button>
            {canRemovePage ? (
              <button
                type="button"
                className="swi-btn swi-btn--danger swi-btn--compact doc-page-remove-btn"
                disabled={removingPage}
                onClick={() => onRemovePage(pageIndex)}
              >
                Remove page
              </button>
            ) : null}
          </div>
        ) : null}
      </header>

      <DocumentDetailsHeader
        form={{
          ...sharedForm,
          page: `${pageForm.page_num}/${totalPages}`,
        }}
        isAdminField={isAdminField}
        onFieldChange={onSharedFieldChange}
      />

      <div className="document-wrap document-wrap--form document-wrap--content doc-a4-sheet">
        {!formLocked ? (
          <div className="doc-content-rows-toolbar">
            <span className="doc-content-rows-toolbar__label">
              Rows: {rowCount}
              {spaceLeftPct > 0 ? (
                <span className="doc-content-rows-toolbar__meta"> · room for more</span>
              ) : null}
            </span>
            <div className="doc-content-rows-toolbar__actions">
              <PageMarkerPanel
                markers={pageMarkers}
                formLocked={formLocked}
                onChange={setPageMarkers}
              />
              {canAddRow ? (
                <button
                  type="button"
                  className="swi-btn swi-btn--ghost swi-btn--compact"
                  onClick={() => applyPageRowPatch(addPageRow(pageForm))}
                >
                  + Add row
                </button>
              ) : (
                <span className="swi-muted small doc-content-rows-toolbar__hint">
                  {rowCount >= MAX_TEXT_BLOCKS
                    ? "Row limit reached."
                    : "Page full — shrink a row or add another page."}
                </span>
              )}
            </div>
          </div>
        ) : null}

        <div
          className="doc-main doc-main--form doc-main--fixed-height"
          style={{
            "--doc-page-content-height": `${pageContentHeightPx}px`,
            "--doc-row-count": rowCount,
          }}
        >
          <div className="doc-materials-side doc-materials-side--form doc-main-materials">
            <label className="form-doc-vertical-label">
              <span className="form-field-tag">Column → prints vertical</span>
              <input
                type="text"
                name="materials_label"
                value={pageForm.materials_label}
                maxLength={32}
                className="form-input-materials"
                onChange={(e) => setPageField("materials_label", e.target.value)}
              />
            </label>
          </div>

          <div className="doc-main-rows">
            {textBlocks.map((block, rowIndex) => {
              const isLastRow = rowIndex === rowCount - 1;
              const blockIndex = block.block_index;
              const rowSpan = pictureRowLayout.spans[rowIndex];
              const rowCellItems = itemsForPictureCell(
                canvasItems,
                rowIndex,
                rowSpan,
                rowHeightsPx
              );
              const gridRow = rowIndex + 1;
              const previewStageHeightPx = previewPictureCellHeightPx(
                rowHeightsPx,
                rowIndex,
                rowSpan
              );

              return (
                <React.Fragment key={`content-row-${block.block_index}`}>
                  <div
                    className={`doc-main-rows__logo${
                      isLastRow ? " doc-main-rows__logo--last" : ""
                    }`}
                    style={{ gridRow, gridColumn: 1, minHeight: `${rowHeightsPx[rowIndex]}px` }}
                  >
                    <div className="doc-logo-cell doc-logo-cell--form doc-content-row__logo">
                      <BlockLogoSlots
                        blockNum={block.block_index}
                        blockIcons={blockIcons}
                        slotCount={logoSlotCountForBlock(block.block_index)}
                        formLocked={formLocked}
                        icons={icons}
                        onIconChange={setBlockIcon}
                        onAddSlot={() =>
                          setBlockLogoCount(
                            block.block_index,
                            logoSlotCountForBlock(block.block_index) + 1
                          )
                        }
                        onRemoveSlot={() => removeLastLogoSlot(block.block_index)}
                      />
                      {!formLocked && rowCount > MIN_TEXT_BLOCKS ? (
                        <button
                          type="button"
                          className="swi-btn swi-btn--ghost swi-btn--compact doc-row-remove-btn"
                          onClick={() => applyPageRowPatch(removePageRow(pageForm, rowIndex))}
                        >
                          Remove row
                        </button>
                      ) : null}
                    </div>
                  </div>

                  <div
                    className={`doc-main-rows__text${
                      isLastRow ? " doc-main-rows__text--last" : ""
                    }`}
                    style={{ gridRow, gridColumn: 2, minHeight: `${rowHeightsPx[rowIndex]}px` }}
                  >
                    <TextBlockField
                      blockIndex={block.block_index}
                      value={block.text}
                      heightPx={block.height_px}
                      formLocked={formLocked}
                      onTextChange={(text) =>
                        applyPageRowPatch(updatePageRowText(pageForm, rowIndex, { text }))
                      }
                      onHeightChange={(height_px) =>
                        applyPageRowPatch(
                          updatePageRowText(pageForm, rowIndex, { height_px })
                        )
                      }
                    />
                  </div>

                  {!pictureRowLayout.skip[rowIndex] ? (
                    <div
                      className={`doc-main-rows__pics${
                        formLocked ? "" : " doc-main-rows__pics--edit"
                      }${rowSpan > 1 ? " doc-main-rows__pics--merged" : ""}${
                        isLastRow || rowIndex + rowSpan === rowCount
                          ? " doc-main-rows__pics--last"
                          : ""
                      }`}
                      style={{
                        gridRow: `${gridRow} / span ${rowSpan}`,
                        gridColumn: 3,
                        minHeight: `${previewStageHeightPx}px`,
                        "--doc-pics-stage-height": `${previewStageHeightPx}px`,
                      }}
                    >
                      <PictureCanvasColumn
                        items={rowCellItems}
                        readOnly={formLocked}
                        rowMode
                        stageHeightPx={previewStageHeightPx}
                        mergeWithNext={Boolean(mergeNextFlags[rowIndex])}
                        canMergeWithNext={!formLocked && !isLastRow}
                        onMergeWithNextChange={(mergeNext) =>
                          onPictureMergeChange(pageIndex, blockIndex, mergeNext)
                        }
                        onChange={(items) =>
                          onCanvasChange(pageIndex, blockIndex, items, rowSpan)
                        }
                        onAddImage={(file) =>
                          onCanvasAddImage(pageIndex, blockIndex, file)
                        }
                        onEditImage={(itemId) =>
                          onCanvasEditImage(pageIndex, blockIndex, itemId)
                        }
                        onClearAll={() => onCanvasClear(pageIndex, blockIndex)}
                      />
                    </div>
                  ) : null}
                </React.Fragment>
              );
            })}
          </div>
        </div>
      </div>
    </article>
  );
}

export default function DocumentFormPage({ documentId, documentNumber, onBack }) {
  const [sharedForm, setSharedForm] = useState({});
  const [pageForms, setPageForms] = useState([]);
  const [adminFields, setAdminFields] = useState([]);
  const [documentMeta, setDocumentMeta] = useState(null);
  const [icons, setIcons] = useState([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState("");
  const [success, setSuccess] = useState("");
  const [editorOpen, setEditorOpen] = useState(false);
  const [editorPageIndex, setEditorPageIndex] = useState(null);
  const [editorBlockIndex, setEditorBlockIndex] = useState(null);
  const [editorItemId, setEditorItemId] = useState(null);
  const [editorMode, setEditorMode] = useState("add");
  const [editorSrc, setEditorSrc] = useState("");
  const editorObjectUrlRef = useRef(null);
  const scrollEndRef = useRef(null);
  const [savedOrderSignature, setSavedOrderSignature] = useState("");
  const [startingWork, setStartingWork] = useState(false);
  const [copyModalOpen, setCopyModalOpen] = useState(false);
  const [copyingPage, setCopyingPage] = useState(false);
  const [removingPage, setRemovingPage] = useState(false);
  const [removeConfirmOpen, setRemoveConfirmOpen] = useState(false);
  const [removePageIndex, setRemovePageIndex] = useState(null);
  const [submitConfirmOpen, setSubmitConfirmOpen] = useState(false);

  const totalPages = pageForms.length || 1;
  const statusId = documentMeta?.document_status_id;
  const statusName = documentMeta?.document_status_name || "Unknown";
  const completedDoc = isCompleted(statusId);
  const showStartWork = canStartWork(statusId);
  const inProgress = canComplete(statusId);
  const showSave = canEditForm(statusId);
  const showComplete = inProgress;
  const formLocked = showStartWork;

  const applyLoadedPages = (pages) => {
    const synced = syncPageNumbers(pages);
    setPageForms(synced);
    setSavedOrderSignature(buildOrderSignature(synced));
    return synced;
  };

  const applyPagesFromApi = (apiPages) => {
    const synced = syncPageNumbers((apiPages || []).map(apiPageRecordToPageForm));
    setPageForms(synced);
    setSavedOrderSignature(buildOrderSignature(synced));
    return synced;
  };

  const loadDocument = async () => {
    const [headerRes, pagesRes] = await Promise.all([
      fetchAssignmentForm(documentId, documentNumber),
      fetchAssignmentDocumentPages(documentId, documentNumber),
    ]);

    setDocumentMeta(headerRes.document);
    setAdminFields(headerRes.adminFields || []);
    setIcons(Array.isArray(headerRes.icons) ? headerRes.icons : []);
    setSharedForm({
      ...pickSharedForm(headerRes.form),
      footer_url:
        headerRes.form?.footer_url || "https://www.v-swi.com/Details.aspx",
    });

    const apiPages = pagesRes?.pages || [];
    if (apiPages.length) {
      applyLoadedPages(apiPages.map(apiPageRecordToPageForm));
    } else {
      applyLoadedPages([formPayloadToPageForm(headerRes.form)]);
    }
  };

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        await loadDocument();
      } catch (e) {
        if (!cancelled) {
          setError(getApiErrorMessage(e, "Could not load form."));
        }
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [documentId, documentNumber]);

  const isAdminField = useMemo(() => new Set(adminFields), [adminFields]);

  const orderDirty = useMemo(
    () => isPageOrderDirty(pageForms, savedOrderSignature),
    [pageForms, savedOrderSignature]
  );

  const originalPositions = useMemo(
    () => buildOriginalPositions(savedOrderSignature ? savedOrderSignature.split("|") : []),
    [savedOrderSignature]
  );

  const setSharedField = (key, value) => {
    setSharedForm((f) => ({ ...f, [key]: value }));
  };

  const updatePage = (pageIndex, patch) => {
    setPageForms((pages) =>
      pages.map((page, i) => (i === pageIndex ? { ...page, ...patch } : page))
    );
  };

  const revokeEditorObjectUrl = () => {
    if (editorObjectUrlRef.current) {
      URL.revokeObjectURL(editorObjectUrlRef.current);
      editorObjectUrlRef.current = null;
    }
  };

  const closeImageEditor = () => {
    setEditorOpen(false);
    setEditorPageIndex(null);
    setEditorBlockIndex(null);
    setEditorItemId(null);
    setEditorMode("add");
    setEditorSrc("");
    revokeEditorObjectUrl();
  };

  const setPageCanvasItems = (pageIndex, items) => {
    updatePage(pageIndex, { picture_canvas: renumberCanvasLabels(items) });
  };

  const onCanvasChange = (pageIndex, blockIndex, items, rowSpan = 1) => {
    setPageForms((pages) =>
      pages.map((page, index) => {
        if (index !== pageIndex) return page;
        const all = normalizeCanvasItems(page);
        return {
          ...page,
          picture_canvas: renumberCanvasLabels(
            replaceRowCanvasItems(all, blockIndex, items, rowSpan)
          ),
        };
      })
    );
  };

  const onCanvasAddImage = (pageIndex, blockIndex, file) => {
    if (!file) return;
    revokeEditorObjectUrl();
    editorObjectUrlRef.current = URL.createObjectURL(file);
    setEditorPageIndex(pageIndex);
    setEditorBlockIndex(blockIndex);
    setEditorItemId(null);
    setEditorMode("add");
    setEditorSrc(editorObjectUrlRef.current);
    setEditorOpen(true);
  };

  const onCanvasEditImage = (pageIndex, blockIndex, itemId) => {
    const items = normalizeCanvasItems(pageForms[pageIndex]);
    const idKey = itemId != null ? String(itemId) : "";
    const item = items.find((it) => String(it.id) === idKey);
    if (!item) return;
    revokeEditorObjectUrl();
    setEditorPageIndex(pageIndex);
    setEditorBlockIndex(blockIndex);
    setEditorItemId(idKey);
    setEditorMode("edit");
    setEditorSrc(item.image_data_url);
    setEditorOpen(true);
  };

  const onCanvasClear = (pageIndex, blockIndex) => {
    onCanvasChange(pageIndex, blockIndex, []);
  };

  const onPictureMergeChange = (pageIndex, blockIndex, mergeNext) => {
    updatePage(
      pageIndex,
      setPictureMergeNext(pageForms[pageIndex], blockIndex, mergeNext)
    );
  };

  const onEditorApply = async (dataUrl) => {
    if (!dataUrl || typeof dataUrl !== "string") return;
    if (editorPageIndex == null || editorBlockIndex == null) return;

    const pageIndex = editorPageIndex;
    const blockIndex = editorBlockIndex;
    const mode = editorMode;
    const itemIdKey = editorItemId != null ? String(editorItemId) : "";
    const imageSize = await measureDataUrlDimensions(dataUrl);

    setPageForms((pages) =>
      pages.map((page, index) => {
        if (index !== pageIndex) return page;

        const textBlocks = normalizeTextBlocks(page);
        const rowHeightsPx = resolvePreviewRowHeightsPx(textBlocks);
        const mergeFlags = pictureMergeNextFlags(page, textBlocks.length);
        const layout = computePictureRowSpans(textBlocks.length, mergeFlags);
        const rowIndex = Math.max(0, blockIndex - 1);
        const rowSpan = layout.spans[rowIndex] || 1;
        const all = normalizeCanvasItems(page);
        const rowItems = itemsForPictureCell(all, rowIndex, rowSpan, rowHeightsPx);

        let nextRowItems;
        if (mode === "edit" && itemIdKey) {
          let foundInRow = false;
          nextRowItems = rowItems.map((item) => {
            if (String(item.id) !== itemIdKey) return item;
            foundInRow = true;
            const fit = fitCanvasItemPercent(imageSize.width, imageSize.height, {
              cellRowSpan: rowSpan,
              rowHeightsPx,
              startRowIndex: rowIndex,
            });
            const curW = Number(item.w_pct) || 0;
            const curH = Number(item.h_pct) || 0;
            const curArea = curW * curH;
            const fitArea = fit.w_pct * fit.h_pct;
            const tooSmall = curW <= 48 || curH <= 34 || curArea < fitArea * 0.72;
            return clampCanvasItem({
              ...item,
              image_data_url: dataUrl,
              ...(tooSmall ? { w_pct: fit.w_pct, h_pct: fit.h_pct } : {}),
            });
          });

          if (!foundInRow) {
            const foundInAll = all.some((item) => String(item.id) === itemIdKey);
            if (foundInAll) {
              return {
                ...page,
                picture_canvas: renumberCanvasLabels(
                  all.map((item) =>
                    String(item.id) === itemIdKey
                      ? { ...item, image_data_url: dataUrl }
                      : item
                  )
                ),
              };
            }
            nextRowItems = [
              ...rowItems,
              createCanvasItem(dataUrl, rowItems, blockIndex, {
                cellRowSpan: rowSpan,
                rowHeightsPx,
                startRowIndex: rowIndex,
                imageWidth: imageSize.width,
                imageHeight: imageSize.height,
              }),
            ];
          }
        } else {
          nextRowItems = [
            ...rowItems,
            createCanvasItem(dataUrl, rowItems, blockIndex, {
              cellRowSpan: rowSpan,
              rowHeightsPx,
              startRowIndex: rowIndex,
              imageWidth: imageSize.width,
              imageHeight: imageSize.height,
            }),
          ];
        }

        return {
          ...page,
          picture_canvas: renumberCanvasLabels(
            replaceRowCanvasItems(all, blockIndex, nextRowItems, rowSpan)
          ),
        };
      })
    );
  };

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

  const addPage = () => {
    setPageForms((pages) => {
      const nextNum = pages.length + 1;
      return syncPageNumbers([
        ...pages,
        createBlankPageForm(nextNum, sharedForm.footer_url),
      ]);
    });
    setSuccess("");
    setError("");
    requestAnimationFrame(() => {
      scrollEndRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
    });
  };

  const duplicatePage = async (pageIndex) => {
    const page = pageForms[pageIndex];
    if (!page || removingPage) return;
    setError("");
    setSuccess("");

    if (page.page_id) {
      setCopyingPage(true);
      try {
        const result = await copyAssignmentPage(
          documentId,
          documentNumber,
          page.page_id
        );
        applyPagesFromApi(result.pages);
        setSuccess(
          `Page duplicated as page ${result.copied_page.page_num} (new page ID ${result.copied_page.page_id}).`
        );
        requestAnimationFrame(() => {
          scrollEndRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
        });
      } catch (e) {
        setError(getApiErrorMessage(e, "Could not duplicate page."));
      } finally {
        setCopyingPage(false);
      }
      return;
    }

    setPageForms((pages) =>
      syncPageNumbers([...pages, clonePageFormFromSource(page)])
    );
    setSuccess("Page duplicated locally. Save document to assign a page ID.");
    requestAnimationFrame(() => {
      scrollEndRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
    });
  };

  const removePage = (pageIndex) => {
    if (pageForms.length <= 1) {
      setError("Each document must keep at least one page.");
      return;
    }

    const page = pageForms[pageIndex];
    if (!page || removingPage) return;

    setError("");
    setSuccess("");
    setRemovePageIndex(pageIndex);
    setRemoveConfirmOpen(true);
  };

  const closeRemoveConfirm = () => {
    if (removingPage) return;
    setRemoveConfirmOpen(false);
    setRemovePageIndex(null);
  };

  const handleRemoveConfirm = async () => {
    if (removePageIndex == null || removingPage) return;

    const pageIndex = removePageIndex;
    const page = pageForms[pageIndex];
    if (!page) {
      closeRemoveConfirm();
      return;
    }

    setError("");
    setSuccess("");

    if (page.page_id) {
      setRemovingPage(true);
      try {
        const result = await deleteAssignmentPage(
          documentId,
          documentNumber,
          page.page_id
        );
        applyPagesFromApi(result.pages);
        setSuccess(`Page ${page.page_num} removed.`);
        setRemoveConfirmOpen(false);
        setRemovePageIndex(null);
      } catch (e) {
        setError(getApiErrorMessage(e, "Could not remove page."));
      } finally {
        setRemovingPage(false);
      }
      return;
    }

    setPageForms((pages) => {
      const next = syncPageNumbers(pages.filter((_, i) => i !== pageIndex));
      setSavedOrderSignature(buildOrderSignature(next));
      return next;
    });
    setSuccess("Page removed.");
    setRemoveConfirmOpen(false);
    setRemovePageIndex(null);
  };

  const pendingRemovePage =
    removePageIndex != null ? pageForms[removePageIndex] : null;

  const handleCopyPageConfirm = async (sourcePageId) => {
    setCopyingPage(true);
    setError("");
    setSuccess("");
    try {
      const result = await copyAssignmentPage(
        documentId,
        documentNumber,
        sourcePageId
      );
      applyPagesFromApi(result.pages);
      setCopyModalOpen(false);
      setSuccess(
        `Copied page ${result.copied_page.page_num} (new page ID ${result.copied_page.page_id}). Content matches the source page.`
      );
      requestAnimationFrame(() => {
        scrollEndRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
      });
    } catch (e) {
      setError(getApiErrorMessage(e, "Could not copy page."));
    } finally {
      setCopyingPage(false);
    }
  };

  const handlePageReorder = (nextPages) => {
    setPageForms(nextPages);
    setSuccess("");
    setError("");
  };

  const handleResetPageOrder = () => {
    setPageForms((pages) => restorePageOrder(pages, savedOrderSignature));
    setSuccess("");
    setError("");
  };

  const scrollToPage = (pageIndex) => {
    const page = pageForms[pageIndex];
    if (!page) return;
    const id =
      page.page_id != null
        ? `doc-page-section-page-${page.page_id}`
        : `doc-page-section-${page._localId || pageIndex}`;
    document.getElementById(id)?.scrollIntoView({ behavior: "smooth", block: "start" });
  };

  const pageStableKey = (pageForm) =>
    pageForm.page_id != null
      ? `page-${pageForm.page_id}`
      : pageForm._localId || `draft-${pageForm.page_num}`;

  const pageSectionId = (pageForm) =>
    pageForm.page_id != null
      ? `doc-page-section-page-${pageForm.page_id}`
      : `doc-page-section-${pageForm._localId || pageForm.page_num}`;

  const saveAll = async ({ markComplete = false } = {}) => {
    setSaving(true);
    setError("");
    setSuccess("");
    const hadOrderChange = orderDirty;
    try {
      if (typeof document !== "undefined" && document.activeElement?.blur) {
        document.activeElement.blur();
      }
      const flushed = applyDomTextBlockHeightsToPageForms(pageForms);
      setPageForms(flushed);
      const synced = syncPageNumbers(flushed);
      const total = synced.length;
      const orderPayload = buildPageOrderPayload(synced);
      if (orderPayload.length > 1 && hadOrderChange) {
        await reorderAssignmentPages(documentId, documentNumber, orderPayload);
      }
      for (let i = 0; i < synced.length; i += 1) {
        const page = synced[i];
        const payload = mergeSavePayload(sharedForm, page, total);
        const isLast = i === synced.length - 1;
        await saveAssignmentForm(documentId, documentNumber, payload, {
          markComplete: markComplete && isLast,
        });
      }
      await loadDocument();
      if (markComplete) {
        setSuccess(`Document completed and saved (${total} page${total === 1 ? "" : "s"}).`);
      } else {
        const orderNote = hadOrderChange ? " Page order saved." : "";
        setSuccess(`Saved ${total} page${total === 1 ? "" : "s"}.${orderNote}`);
      }
      return true;
    } catch (e) {
      setError(getApiErrorMessage(e, "Save failed."));
      return false;
    } finally {
      setSaving(false);
    }
  };

  const handleSubmitConfirm = async () => {
    const ok = await saveAll({ markComplete: true });
    if (ok) setSubmitConfirmOpen(false);
  };

  const startWork = async () => {
    setStartingWork(true);
    setError("");
    setSuccess("");
    try {
      const result = await startAssignmentWork(documentId, documentNumber);
      setDocumentMeta(result.document);
      setSuccess("Status set to In Progress. You can now fill and save the document.");
    } catch (e) {
      setError(getApiErrorMessage(e, "Could not set document to in process."));
    } finally {
      setStartingWork(false);
    }
  };

  if (loading) {
    return <p className="swi-muted">Loading form…</p>;
  }

  return (
    <div className="doc-builder doc-builder--scroll doc-builder--full-width">
      <header className="portal-page-header portal-page-header--solo">
        <div className="portal-page-header__text">
          <h1 className="portal-page-title">
            {documentMeta?.document_name || "Document form"}
          </h1>
          <p className="portal-page-subtitle">
            Document {documentId} · #{documentNumber}
            {totalPages > 1 ? ` · ${totalPages} pages` : ""}
          </p>
          <span className={`swi-status-badge ${statusBadgeClass(statusId)}`}>
            {statusName}
          </span>
        </div>
      </header>

      {showStartWork ? (
        <div className="doc-status-callout doc-status-callout--assigned" role="note">
          This document is <strong>Assigned</strong> to you. Click{" "}
          <strong>Set to in process</strong> before filling the form.
        </div>
      ) : null}
      {completedDoc ? (
        <div className="doc-status-callout doc-status-callout--completed" role="note">
          This document is <strong>Completed</strong>. You can update pages, text, and
          images below, then click <strong>Save changes</strong>.
        </div>
      ) : null}

      {error ? (
        <div className="swi-error" role="alert">
          {error}
        </div>
      ) : null}
      {success ? (
        <div className="swi-success" role="status">
          {success}
        </div>
      ) : null}

      <div className="doc-scroll-workspace">
        <div className={`doc-scroll-pages${showStartWork ? " doc-scroll-pages--locked" : ""}`}>
          {pageForms.map((pageForm, pageIndex) => (
            <DocumentPageSection
              key={pageStableKey(pageForm)}
              sectionId={pageSectionId(pageForm)}
              moveHint={pageMoveHint(pageForm, pageIndex, originalPositions)}
              pageForm={pageForm}
              pageIndex={pageIndex}
              totalPages={totalPages}
              sharedForm={sharedForm}
              icons={icons}
              isAdminField={isAdminField}
              onSharedFieldChange={setSharedField}
              onPageChange={updatePage}
              onCanvasChange={onCanvasChange}
              onCanvasAddImage={onCanvasAddImage}
              onCanvasEditImage={onCanvasEditImage}
              onCanvasClear={onCanvasClear}
              onPictureMergeChange={onPictureMergeChange}
              onDuplicatePage={duplicatePage}
              onRemovePage={removePage}
              canRemovePage={totalPages > 1}
              formLocked={formLocked || copyingPage || removingPage}
              removingPage={removingPage}
            />
          ))}

          <div className="doc-scroll-page-add" ref={scrollEndRef}>
            <p className="swi-muted small doc-scroll-add-hint">
              Use the floating buttons to add or copy pages. Copied pages get a
              new page ID in this document&apos;s sequence.
            </p>
          </div>
        </div>

        <PageOrderBar
          pageForms={pageForms}
          savedOrderSignature={savedOrderSignature}
          disabled={saving || showStartWork || removingPage}
          onReorder={handlePageReorder}
          onResetOrder={handleResetPageOrder}
          onScrollToPage={scrollToPage}
        />
      </div>

      <DocumentFloatingActions
        onBack={onBack}
        showStartWork={showStartWork}
        startingWork={startingWork}
        showSave={showSave}
        showComplete={showComplete}
        saving={saving}
        orderDirty={orderDirty}
        completedDoc={completedDoc}
        onStartWork={startWork}
        onSave={() => saveAll()}
        onComplete={() => setSubmitConfirmOpen(true)}
        onAddPage={addPage}
        onCopyPage={() => setCopyModalOpen(true)}
        pageActionsDisabled={
          saving || showStartWork || copyingPage || removingPage
        }
      />

      <SubmitDocumentConfirmModal
        open={submitConfirmOpen}
        onClose={() => setSubmitConfirmOpen(false)}
        onConfirm={handleSubmitConfirm}
        submitting={saving}
        documentName={documentMeta?.document_name}
        pageCount={totalPages}
      />

      <RemovePageConfirmModal
        open={removeConfirmOpen}
        onClose={closeRemoveConfirm}
        onConfirm={handleRemoveConfirm}
        removing={removingPage}
        pageNum={pendingRemovePage?.page_num}
        pageLabel={
          pendingRemovePage
            ? pendingRemovePage.page_id
              ? `page ${pendingRemovePage.page_num}`
              : `unsaved page ${pendingRemovePage.page_num}`
            : undefined
        }
        isUnsaved={pendingRemovePage != null && !pendingRemovePage.page_id}
      />

      <CopyPageModal
        open={copyModalOpen}
        onClose={() => setCopyModalOpen(false)}
        targetDocumentId={documentId}
        targetDocumentNumber={documentNumber}
        targetDocumentName={documentMeta?.document_name}
        copying={copyingPage}
        onConfirm={handleCopyPageConfirm}
      />

      {editorOpen ? (
        <Suspense fallback={null}>
          <ImageEditorModal
            open={editorOpen}
            imageSrc={editorSrc}
            slotIndex={null}
            onClose={closeImageEditor}
            onApply={onEditorApply}
          />
        </Suspense>
      ) : null}
    </div>
  );
}
