import React, {
  useState, useEffect, useCallback, useMemo, useRef,
  forwardRef, useImperativeHandle,
} from 'react';
import { supabase } from '@/integrations/supabase/client';
import { combineOwnerNames } from '@/lib/ownerAddressUtils';
import {
  Loader2, Save, AlertTriangle, Settings,
  Check, ChevronsUpDown, Trash2,
} from 'lucide-react';
import { CustomFormPreview } from './CustomFormPreview';
import { CustomFormPdfExport } from './CustomFormPdfExport';
import { CustomFormPdfExportButton } from './CustomFormPdfExportButton';
import { useSavedFormTemplates } from '@/hooks/useSavedFormTemplates';
import SaveToDocumentsDialog from '@/components/documents/SaveToDocumentsDialog';
import { sanitizeRichTextHtml } from '@/lib/customFormHtmlSanitizer';
import { normalizePastedRichHtml, plainTextToRichHtml } from '@/lib/customFormPasteUtils';
import { convertDocxFileToHtml } from '@/lib/customFormDocxImport';

// ─── CONSTANTS ────────────────────────────────────────────────────────────────
const PAGE_W_PX = 816;
const PAGE_H_PX = 1056;
const MARGIN_PX = 72;

const FONT_SIZES = [8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 48, 72];
const EDITOR_FONTS = [
  { label: 'Default',         value: 'inherit' },
  { label: 'Helvetica',       value: 'Helvetica, Arial, sans-serif' },
  { label: 'Times New Roman', value: '"Times New Roman", Times, serif' },
  { label: 'Georgia',         value: 'Georgia, serif' },
  { label: 'Courier New',     value: '"Courier New", monospace' },
  { label: 'Arial',           value: 'Arial, sans-serif' },
  { label: 'Garamond',        value: 'Garamond, serif' },
  { label: 'Verdana',         value: 'Verdana, sans-serif' },
  { label: 'Trebuchet MS',    value: '"Trebuchet MS", sans-serif' },
];
const BLOCK_FORMATS = [
  { label: 'Normal',       value: 'p' },
  { label: 'Heading 1',   value: 'h1' },
  { label: 'Heading 2',   value: 'h2' },
  { label: 'Heading 3',   value: 'h3' },
  { label: 'Heading 4',   value: 'h4' },
  { label: 'Preformatted',value: 'pre' },
];
const FORMAT_TARGET_SELECTOR = 'li,p,div,h1,h2,h3,h4,blockquote,pre,td,th';

// ─── INLINE SVG ICONS ─────────────────────────────────────────────────────────
const Icon = ({ d, size = 14, className = '' }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
    {Array.isArray(d) ? d.map((p, i) => <path key={i} d={p} />) : <path d={d} />}
  </svg>
);
const BoldIcon      = () => <Icon d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z" />;
const ItalicIcon    = () => <Icon d="M19 4h-9M14 20H5M15 4L9 20" />;
const UnderlineIcon = () => <Icon d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3M4 21h16" />;
const StrikeIcon    = () => <Icon d={["M17.3 4.9c-2.3-.6-4.4-1-6.2-.9-2.7 0-5.3.7-5.3 3.6 0 1.5 1.1 2.2 2.4 2.8","M6.8 19.3c1.6.4 3.3.7 5.2.7 2.8 0 5.4-1 5.4-3.7 0-3-2.7-3.6-4.2-4M4 12h16"]} />;
const AlignLIcon    = () => <Icon d={["M21 6H3","M15 12H3","M17 18H3"]} />;
const AlignCIcon    = () => <Icon d={["M21 6H3","M17 12H7","M19 18H5"]} />;
const AlignRIcon    = () => <Icon d={["M21 6H3","M21 12H9","M21 18H7"]} />;
const AlignJIcon    = () => <Icon d={["M21 6H3","M21 12H3","M21 18H3"]} />;
const ListIcon      = () => <Icon d={["M8 6h13","M8 12h13","M8 18h13","M3 6h.01","M3 12h.01","M3 18h.01"]} />;
const OListIcon     = () => <Icon d={["M10 6h11","M10 12h11","M10 18h11","M4 6h1v4","M4 10H3","M3 14h2a2 2 0 1 1-2 2v-.5"]} />;
const IndentIcon    = () => <Icon d={["M3 6h18","M3 12h12","M3 18h18","M8 9l4 3-4 3"]} />;
const OutdentIcon   = () => <Icon d={["M3 6h18","M9 12h12","M3 18h18","M12 9l-4 3 4 3"]} />;
const LinkIcon      = () => <Icon d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71 M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />;
const TableIcon     = () => <Icon d={["M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2V9M9 21H5a2 2 0 0 1-2-2V9m0 0h18"]} />;
const UndoIcon      = () => <Icon d="M3 7v6h6 M3.51 15a9 9 0 1 0 .49-3.96" />;
const RedoIcon      = () => <Icon d="M21 7v6h-6 M20.49 15a9 9 0 1 1-.49-3.96" />;
const PaletteIcon   = () => <Icon d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8z" />;
const HighlightIcon = () => <Icon d={["M12 20h9","M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"]} />;
const RemoveIcon    = () => <Icon d={["M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7","M18.375 2.625a2.121 2.121 0 1 1 3 3L12 15l-4 1 1-4 9.375-9.375z","M10 14L3 21"]} />;
const PrinterIcon   = () => <Icon d={["M6 9V2h12v7","M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2","M6 14h12v8H6z"]} />;
const EyeIcon       = () => <Icon d={["M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z","M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z"]} />;
const EyeOffIcon    = () => <Icon d={["M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24","M1 1l22 22"]} />;
const ChevronDownIcon = () => <Icon d="M6 9l6 6 6-6" />;
const XIcon         = () => <Icon d="M18 6L6 18M6 6l12 12" />;
const PageBreakIcon = () => <Icon d={["M21 15H3","M21 9H3","M3 12h18"]} />;

// ─── UTILITIES ────────────────────────────────────────────────────────────────
function cls(...args) { return args.filter(Boolean).join(' '); }

function getClosestFormatTarget(node, root) {
  if (!node || !root) return null;

  const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
  if (!element) return null;

  const target = element.closest(FORMAT_TARGET_SELECTOR);
  return target && root.contains(target) ? target : root;
}

function getSelectionFormatTargets(root) {
  if (!root) return [];

  const selection = window.getSelection();
  if (!selection || selection.rangeCount === 0) return [];

  const range = selection.getRangeAt(0);
  const targets = new Set();

  const commonAncestor = range.commonAncestorContainer.nodeType === Node.ELEMENT_NODE
    ? range.commonAncestorContainer
    : range.commonAncestorContainer.parentElement;

  if (commonAncestor && root.contains(commonAncestor)) {
    const walker = document.createTreeWalker(commonAncestor, NodeFilter.SHOW_ELEMENT, {
      acceptNode(node) {
        if (!(node instanceof Element) || !root.contains(node)) return NodeFilter.FILTER_REJECT;
        if (!node.matches(FORMAT_TARGET_SELECTOR)) return NodeFilter.FILTER_SKIP;
        return range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
      },
    });

    while (walker.nextNode()) {
      targets.add(walker.currentNode);
    }
  }

  if (targets.size === 0) {
    [selection.anchorNode, selection.focusNode].forEach((node) => {
      const target = getClosestFormatTarget(node, root);
      if (target) targets.add(target);
    });
  }

  return [...targets];
}

function getOrderedListItems(list) {
  return [...(list?.children || [])].filter((child) => child.tagName === 'LI');
}

function getOrderedListStart(list) {
  const start = Number.parseInt(list?.getAttribute?.('start') || '1', 10);
  return Number.isFinite(start) && start > 0 ? start : 1;
}

function getOrderedListNextValue(list) {
  const items = getOrderedListItems(list);
  let value = getOrderedListStart(list) - 1;

  items.forEach((item) => {
    const explicitValue = Number.parseInt(item.getAttribute('value') || '', 10);
    value = Number.isFinite(explicitValue) && explicitValue > 0 ? explicitValue : value + 1;
  });

  return Math.max(1, value + 1);
}

function getPreviousOrderedList(root, referenceNode) {
  if (!root || !referenceNode) return null;

  let previous = null;
  [...root.querySelectorAll('ol')].forEach((list) => {
    if (list === referenceNode || list.contains(referenceNode)) return;

    const relation = list.compareDocumentPosition(referenceNode);
    if (relation & Node.DOCUMENT_POSITION_FOLLOWING) {
      previous = list;
    }
  });

  return previous;
}

function setOrderedListStart(list, start) {
  if (!list) return;
  const normalizedStart = Math.max(1, Number(start) || 1);

  if (normalizedStart === 1) list.removeAttribute('start');
  else list.setAttribute('start', String(normalizedStart));
}

function convertLegacyFontTag(fontElement) {
  if (!fontElement?.parentNode) return;

  const span = document.createElement('span');
  const size = fontElement.getAttribute('size');
  const face = fontElement.getAttribute('face');
  const color = fontElement.getAttribute('color');

  const sizeMap = {
    1: '8pt',
    2: '10pt',
    3: '12pt',
    4: '14pt',
    5: '18pt',
    6: '24pt',
    7: '32pt',
  };

  if (size && sizeMap[size]) span.style.fontSize = sizeMap[size];
  if (face) span.style.fontFamily = face;
  if (color) span.style.color = color;

  while (fontElement.firstChild) {
    span.appendChild(fontElement.firstChild);
  }

  fontElement.replaceWith(span);
}

// ─── Inline Toast ─────────────────────────────────────────────────────────────
function useToast() {
  const [toasts, setToasts] = useState([]);
  const toast = useCallback(({ variant, title, description }) => {
    const id = Date.now();
    setToasts(t => [...t, { id, variant, title, description }]);
    setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 4000);
  }, []);
  const ToastContainer = () => (
    <div className="fixed bottom-6 right-6 z-50 flex flex-col gap-2">
      {toasts.map(t => (
        <div key={t.id} className={`px-5 py-3 rounded-xl shadow-lg text-sm max-w-xs ${t.variant === 'destructive' ? 'bg-destructive text-destructive-foreground' : 'bg-foreground text-background'}`}>
          <p className="font-semibold">{t.title}</p>
          {t.description && <p className="opacity-80 mt-0.5">{t.description}</p>}
        </div>
      ))}
    </div>
  );
  return { toast, ToastContainer };
}

// ─── Simple Homeowner Combobox ────────────────────────────────────────────────
function HomeownerCombobox({ homeowners, selectedId, onSelect }) {
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState('');
  const ref = useRef(null);
  const selected = homeowners.find(h => h.id === selectedId);
  const filtered = homeowners.filter(h =>
    h.owner_name?.toLowerCase().includes(query.toLowerCase()) ||
    h.property_address?.toLowerCase().includes(query.toLowerCase())
  );
  useEffect(() => {
    const h = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, []);
  return (
    <div ref={ref} className="relative">
      <button type="button" onClick={() => setOpen(o => !o)}
        className="w-full flex items-center justify-between h-8 px-3 rounded-md border border-slate-200 bg-white text-xs text-slate-700 hover:border-blue-400">
        <span className="truncate">{selected?.owner_name || 'Select Homeowner (Optional)'}</span>
        <ChevronsUpDown className="w-3 h-3 opacity-50 shrink-0 ml-2" />
      </button>
      {open && (
        <div className="absolute z-50 top-9 left-0 right-0 bg-white border border-slate-200 rounded-lg shadow-lg overflow-hidden">
          <input autoFocus type="text" placeholder="Search homeowner..." value={query}
            onChange={e => setQuery(e.target.value)}
            className="w-full h-8 px-3 text-xs border-b border-slate-200 focus:outline-none bg-white" />
          <div className="max-h-48 overflow-y-auto">
            {filtered.length === 0
              ? <div className="px-3 py-2 text-xs text-slate-400">No homeowner found.</div>
              : filtered.map(owner => (
                <button key={owner.id} type="button"
                  onClick={() => { onSelect(owner.id === selectedId ? '' : owner.id); setOpen(false); setQuery(''); }}
                  className="flex items-center gap-2 w-full px-3 py-2 text-xs text-left hover:bg-blue-50">
                  <Check className={`h-3 w-3 shrink-0 ${selectedId === owner.id ? 'opacity-100 text-blue-600' : 'opacity-0'}`} />
                  <div>
                    <div>{owner.owner_name}</div>
                    <div className="text-[10px] text-slate-400">{owner.property_address}</div>
                  </div>
                </button>
              ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ─── TOOLBAR PRIMITIVES ───────────────────────────────────────────────────────
function ToolBtn({ onClick, active, title, children }) {
  return (
    <button type="button" title={title}
      onMouseDown={e => { e.preventDefault(); onClick(); }}
      className={cls(
        'flex items-center justify-center w-7 h-7 rounded text-sm transition-all select-none cursor-pointer flex-shrink-0',
        active
          ? 'bg-blue-100 text-blue-700 shadow-inner'
          : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'
      )}>
      {children}
    </button>
  );
}

function Sep() {
  return <div className="w-px h-5 bg-slate-200 mx-0.5 flex-shrink-0" />;
}

function Picker({ label, items, onSelect, renderItem, minWidth = 60 }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    const h = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, []);
  return (
    <div ref={ref} className="relative flex-shrink-0">
      <button type="button"
        onMouseDown={e => { e.preventDefault(); setOpen(o => !o); }}
        className="flex items-center gap-1 h-7 px-2 rounded border border-slate-200 bg-white text-xs text-slate-700 hover:border-blue-400 transition-colors"
        style={{ minWidth }}>
        <span className="truncate flex-1">{label}</span>
        <ChevronDownIcon />
      </button>
      {open && (
        <div className="absolute z-50 top-8 left-0 bg-white border border-slate-200 rounded shadow-xl overflow-y-auto max-h-52" style={{ minWidth }}>
          {items.map((item, i) => (
            <button key={i} type="button"
              onMouseDown={e => { e.preventDefault(); onSelect(item); setOpen(false); }}
              className="w-full text-left px-3 py-1.5 text-xs hover:bg-blue-50 text-slate-700 whitespace-nowrap">
              {renderItem ? renderItem(item) : String(item)}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function ColorBtn({ icon: IconComp, title, onPick, currentColor }) {
  const inputRef = useRef(null);
  return (
    <button type="button" title={title}
      className="flex items-center justify-center w-7 h-7 rounded text-slate-600 hover:bg-slate-100 cursor-pointer relative flex-shrink-0"
      onMouseDown={e => { e.preventDefault(); inputRef.current?.click(); }}>
      <IconComp />
      {currentColor && (
        <div className="absolute bottom-0.5 left-1 right-1 h-0.5 rounded-full" style={{ background: currentColor }} />
      )}
      <input ref={inputRef} type="color" className="absolute opacity-0 w-0 h-0 pointer-events-none"
        defaultValue={currentColor || '#000000'} onChange={e => onPick(e.target.value)} />
    </button>
  );
}

// ─── RICH TEXT EDITOR ─────────────────────────────────────────────────────────
const RichTextEditor = forwardRef(function RichTextEditor(
  { value = '', onChange, placeholder = 'Start typing...', minHeight = 400 }, fwdRef
) {
  const divRef = useRef(null);
  const savedRange = useRef(null);
  const skipSync = useRef(false);
  const [fontSize, setFontSize] = useState(11);
  const [fontLabel, setFontLabel] = useState('Default');
  const [blockLabel, setBlockLabel] = useState('Normal');
  const [fgColor, setFgColor] = useState('#111111');
  const [bgColor, setBgColor] = useState('#ffff00');
  const [es, setEs] = useState({
    bold: false, italic: false, underline: false, strike: false,
    alignL: true, alignC: false, alignR: false, alignJ: false,
    ol: false, ul: false,
  });

  useEffect(() => {
    if (skipSync.current || !divRef.current) return;
    if (divRef.current.innerHTML !== value) divRef.current.innerHTML = value || '';
  }, [value]);

  const refresh = useCallback(() => {
    try {
      setEs({
        bold: document.queryCommandState('bold'),
        italic: document.queryCommandState('italic'),
        underline: document.queryCommandState('underline'),
        strike: document.queryCommandState('strikeThrough'),
        alignL: document.queryCommandState('justifyLeft'),
        alignC: document.queryCommandState('justifyCenter'),
        alignR: document.queryCommandState('justifyRight'),
        alignJ: document.queryCommandState('justifyFull'),
        ol: document.queryCommandState('insertOrderedList'),
        ul: document.queryCommandState('insertUnorderedList'),
      });
    } catch (_) {}
  }, []);

  const saveRange = useCallback(() => {
    const sel = window.getSelection();
    if (sel && sel.rangeCount > 0) savedRange.current = sel.getRangeAt(0).cloneRange();
  }, []);

  const restoreRange = useCallback(() => {
    if (!savedRange.current) { divRef.current?.focus(); return; }
    const sel = window.getSelection();
    if (sel) { sel.removeAllRanges(); sel.addRange(savedRange.current); }
  }, []);

  useImperativeHandle(fwdRef, () => ({
    insertAtCursor(html) {
      divRef.current?.focus();
      restoreRange();
      document.execCommand('insertHTML', false, html);
      const sel = window.getSelection();
      if (sel && sel.rangeCount > 0) savedRange.current = sel.getRangeAt(0).cloneRange();
      refresh();
      if (onChange) onChange(divRef.current?.innerHTML || '');
    },
    getContent() { return divRef.current?.innerHTML || ''; },
    focus() { divRef.current?.focus(); },
  }), [restoreRange, refresh, onChange]);

  const exec = useCallback((cmd, val = null) => {
    restoreRange(); divRef.current?.focus();
    document.execCommand(cmd, false, val);
    saveRange(); refresh();
    if (onChange) onChange(divRef.current?.innerHTML || '');
  }, [restoreRange, saveRange, refresh, onChange]);

  const normalizeLegacyFontTags = useCallback(() => {
    if (!divRef.current) return;
    [...divRef.current.querySelectorAll('font')].forEach(convertLegacyFontTag);
  }, []);

  const getActiveOrderedList = useCallback(() => {
    const selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || !divRef.current) return null;

    const node = selection.anchorNode?.nodeType === Node.ELEMENT_NODE
      ? selection.anchorNode
      : selection.anchorNode?.parentElement;
    const list = node?.closest?.('ol');

    return list && divRef.current.contains(list) ? list : null;
  }, []);

  const applyInlineStyle = useCallback((styles) => {
    restoreRange();
    divRef.current?.focus();

    const selection = window.getSelection();
    if (!selection || selection.rangeCount === 0 || !divRef.current) return;

    const range = selection.getRangeAt(0);

    if (range.collapsed) {
      const target = getClosestFormatTarget(selection.anchorNode, divRef.current);
      if (target) Object.assign(target.style, styles);
    } else {
      const wrapper = document.createElement('span');
      Object.assign(wrapper.style, styles);
      wrapper.appendChild(range.extractContents());
      range.insertNode(wrapper);

      const updatedRange = document.createRange();
      updatedRange.selectNodeContents(wrapper);
      selection.removeAllRanges();
      selection.addRange(updatedRange);
    }

    saveRange();
    refresh();
    if (onChange) onChange(divRef.current.innerHTML || '');
  }, [restoreRange, saveRange, refresh, onChange]);

  const applyAlignment = useCallback((alignment) => {
    restoreRange();
    divRef.current?.focus();

    if (!divRef.current) return;

    const targets = getSelectionFormatTargets(divRef.current);
    if (targets.length === 0) return;

    targets.forEach((target) => {
      const effectiveAlignment = target.tagName === 'LI' && alignment === 'justify' ? 'left' : alignment;

      if (effectiveAlignment === 'left') target.style.removeProperty('text-align');
      else target.style.textAlign = effectiveAlignment;

      if (target.tagName === 'LI') {
        target.querySelectorAll('ul, ol').forEach((list) => list.style.textAlign = 'left');
      }
    });

    saveRange();
    refresh();
    if (onChange) onChange(divRef.current.innerHTML || '');
  }, [restoreRange, saveRange, refresh, onChange]);

  const applyOrderedListStart = useCallback((mode) => {
    restoreRange();
    divRef.current?.focus();

    if (!divRef.current) return;

    const selection = window.getSelection();
    const referenceTarget = getClosestFormatTarget(selection?.anchorNode, divRef.current) || divRef.current;
    const previousList = mode === 'continue'
      ? getPreviousOrderedList(divRef.current, referenceTarget)
      : null;

    document.execCommand('insertOrderedList', false, null);

    const activeList = getActiveOrderedList();
    setOrderedListStart(activeList, previousList ? getOrderedListNextValue(previousList) : 1);

    saveRange();
    refresh();
    if (onChange) onChange(divRef.current.innerHTML || '');
  }, [restoreRange, saveRange, refresh, onChange, getActiveOrderedList]);

  const applyFontSize = useCallback(pt => {
    setFontSize(pt);
    restoreRange();
    divRef.current?.focus();
    document.execCommand('styleWithCSS', false, true);
    document.execCommand('fontSize', false, '7');
    normalizeLegacyFontTags();

    const selection = window.getSelection();
    if (selection && selection.rangeCount > 0 && divRef.current?.contains(selection.anchorNode)) {
      const targets = getSelectionFormatTargets(divRef.current);
      targets.forEach((target) => {
        if (target.tagName !== 'LI') target.style.fontSize = `${pt}pt`;
      });
    }

    const legacySized = divRef.current?.querySelectorAll('span[style*="font-size: 32pt"]') || [];
    legacySized.forEach((element) => {
      element.style.fontSize = `${pt}pt`;
    });

    saveRange();
    refresh();
    if (onChange) onChange(divRef.current?.innerHTML || '');
  }, [normalizeLegacyFontTags, restoreRange, saveRange, refresh, onChange]);

  const applyFont = useCallback(f => {
    setFontLabel(f.label);
    restoreRange();
    divRef.current?.focus();
    document.execCommand('styleWithCSS', false, true);
    document.execCommand('fontName', false, f.label);
    normalizeLegacyFontTags();

    const selection = window.getSelection();
    if (selection && selection.rangeCount > 0 && divRef.current?.contains(selection.anchorNode)) {
      const targets = getSelectionFormatTargets(divRef.current);
      targets.forEach((target) => {
        target.style.fontFamily = f.value;
      });
    }

    saveRange();
    refresh();
    if (onChange) onChange(divRef.current?.innerHTML || '');
  }, [normalizeLegacyFontTags, restoreRange, saveRange, refresh, onChange]);
  const applyBlock = useCallback(f => {
    setBlockLabel(f.label); restoreRange(); divRef.current?.focus();
    document.execCommand('formatBlock', false, f.value);
    saveRange(); refresh();
    if (onChange) onChange(divRef.current?.innerHTML || '');
  }, [restoreRange, saveRange, refresh, onChange]);

  const applyFgColor = useCallback(color => { setFgColor(color); exec('foreColor', color); }, [exec]);
  const applyBgColor = useCallback(color => { setBgColor(color); exec('hiliteColor', color); }, [exec]);

  const insertTable = useCallback(() => {
    restoreRange(); divRef.current?.focus();
    document.execCommand('insertHTML', false,
      '<table border="1" style="border-collapse:collapse;width:100%;margin:8px 0">' +
      Array(2).fill('<tr>' + Array(3).fill('<td style="padding:6px 8px;border:1px solid #ccc">&nbsp;</td>').join('') + '</tr>').join('') +
      '</table><p><br></p>');
    saveRange(); refresh();
    if (onChange) onChange(divRef.current?.innerHTML || '');
  }, [restoreRange, saveRange, refresh, onChange]);

  const insertLink = useCallback(() => {
    restoreRange();
    const url = prompt('Enter URL:', 'https://');
    if (url) exec('createLink', url);
  }, [restoreRange, exec]);

  const handlePaste = useCallback(e => {
    e.preventDefault();
    restoreRange();
    divRef.current?.focus();

    const html = e.clipboardData?.getData('text/html');
    const text = e.clipboardData?.getData('text/plain');

    if (html) {
      const clean = normalizePastedRichHtml(html, text || '');
      document.execCommand('insertHTML', false, clean || plainTextToRichHtml(text || ''));
    } else if (text) {
      document.execCommand('insertHTML', false, plainTextToRichHtml(text));
    }

    saveRange(); refresh();
    if (onChange) onChange(divRef.current?.innerHTML || '');
  }, [restoreRange, saveRange, refresh, onChange]);

  const handleInput = useCallback(() => {
    skipSync.current = true; refresh();
    if (onChange) onChange(divRef.current?.innerHTML || '');
    setTimeout(() => { skipSync.current = false; }, 0);
  }, [refresh, onChange]);

  const currentFont = EDITOR_FONTS.find(f => f.label === fontLabel) || EDITOR_FONTS[0];

  return (
    <div className="flex flex-col border border-slate-200 rounded-lg overflow-hidden bg-white">
      {/* Toolbar */}
      <div className="flex flex-wrap items-center gap-0.5 px-2 py-1.5 border-b border-slate-200 bg-gradient-to-b from-slate-50 to-white sticky top-0 z-10">
        <ToolBtn title="Undo" onClick={() => exec('undo')}><UndoIcon /></ToolBtn>
        <ToolBtn title="Redo" onClick={() => exec('redo')}><RedoIcon /></ToolBtn>
        <Sep />
        <Picker label={blockLabel} items={BLOCK_FORMATS} onSelect={applyBlock} minWidth={110}
          renderItem={f => (
            <span style={{ fontSize: f.value==='h1'?17:f.value==='h2'?14:f.value==='h3'?12:11, fontWeight: f.value.startsWith('h')?'bold':'normal' }}>
              {f.label}
            </span>
          )}
        />
        <Sep />
        <Picker label={<span style={{ fontFamily: currentFont.value }}>{fontLabel}</span>} items={EDITOR_FONTS} onSelect={applyFont} minWidth={130}
          renderItem={f => <span style={{ fontFamily: f.value }}>{f.label}</span>}
        />
        <Sep />
        <Picker label={String(fontSize)} items={FONT_SIZES} onSelect={applyFontSize} minWidth={52}
          renderItem={s => String(s)}
        />
        <Sep />
        <ToolBtn title="Bold" active={es.bold} onClick={() => exec('bold')}><BoldIcon /></ToolBtn>
        <ToolBtn title="Italic" active={es.italic} onClick={() => exec('italic')}><ItalicIcon /></ToolBtn>
        <ToolBtn title="Underline" active={es.underline} onClick={() => exec('underline')}><UnderlineIcon /></ToolBtn>
        <ToolBtn title="Strikethrough" active={es.strike} onClick={() => exec('strikeThrough')}><StrikeIcon /></ToolBtn>
        <Sep />
        <ColorBtn icon={PaletteIcon} title="Text Color" onPick={applyFgColor} currentColor={fgColor} />
        <ColorBtn icon={HighlightIcon} title="Highlight Color" onPick={applyBgColor} currentColor={bgColor} />
        <ToolBtn title="Remove Formatting" onClick={() => exec('removeFormat')}><RemoveIcon /></ToolBtn>
        <Sep />
        <ToolBtn title="Align Left"    active={es.alignL} onClick={() => applyAlignment('left')}><AlignLIcon /></ToolBtn>
        <ToolBtn title="Align Center"  active={es.alignC} onClick={() => applyAlignment('center')}><AlignCIcon /></ToolBtn>
        <ToolBtn title="Align Right"   active={es.alignR} onClick={() => applyAlignment('right')}><AlignRIcon /></ToolBtn>
        <ToolBtn title="Justify"       active={es.alignJ} onClick={() => applyAlignment('justify')}><AlignJIcon /></ToolBtn>
        <Sep />
        <ToolBtn title="Bullet List" active={es.ul} onClick={() => exec('insertUnorderedList')}><ListIcon /></ToolBtn>
        <ToolBtn title="Numbered List (Restart at 1)" active={es.ol} onClick={() => applyOrderedListStart('restart')}><OListIcon /></ToolBtn>
        <ToolBtn title="Continue Numbering" onClick={() => applyOrderedListStart('continue')}><span className="text-[10px] font-semibold leading-none">↺</span></ToolBtn>
        <ToolBtn title="Indent"        onClick={() => exec('indent')}><IndentIcon /></ToolBtn>
        <ToolBtn title="Outdent"       onClick={() => exec('outdent')}><OutdentIcon /></ToolBtn>
        <Sep />
        <ToolBtn title="Insert Link"   onClick={insertLink}><LinkIcon /></ToolBtn>
        <ToolBtn title="Insert Table"  onClick={insertTable}><TableIcon /></ToolBtn>
        <Sep />
        <ToolBtn title="Insert Page Break" onClick={() => {
          restoreRange();
          document.execCommand('insertHTML', false, '<div class="page-break-marker" contenteditable="false"><span>\u2014 PAGE BREAK \u2014</span></div><p><br></p>');
          handleInput();
        }}><PageBreakIcon /></ToolBtn>
      </div>

      {/* Editable area */}
      <div
        ref={divRef}
        contentEditable
        suppressContentEditableWarning
        onInput={handleInput}
        onKeyUp={() => { saveRange(); refresh(); }}
        onMouseUp={() => { saveRange(); refresh(); }}
        onPaste={handlePaste}
        data-placeholder={placeholder}
        className="flex-1 outline-none text-slate-800 leading-relaxed"
        style={{
          minHeight,
          padding: '16px 20px',
          fontFamily: 'Helvetica, Arial, sans-serif',
          fontSize: '11pt',
          lineHeight: 1.65,
          wordBreak: 'break-word',
          overflowWrap: 'break-word',
        }}
      />
      <style>{`
        [contenteditable]:empty:before {
          content: attr(data-placeholder);
          color: #94a3b8;
          pointer-events: none;
        }
        [contenteditable] .ql-align-center { text-align: center; }
        [contenteditable] .ql-align-right { text-align: right; }
        [contenteditable] .ql-align-justify { text-align: justify; }
        [contenteditable] .ql-indent-1 { padding-left: 2em; }
        [contenteditable] .ql-indent-2 { padding-left: 4em; }
        [contenteditable] .ql-indent-3 { padding-left: 6em; }
        [contenteditable] .ql-indent-4 { padding-left: 8em; }
        [contenteditable] .ql-indent-5 { padding-left: 10em; }
        [contenteditable] .ql-indent-6 { padding-left: 12em; }
        [contenteditable] p,
        [contenteditable] > div,
        [contenteditable] blockquote,
        [contenteditable] pre {
          margin: 0 0 0.55em;
          line-height: inherit;
        }
        [contenteditable] p:last-child,
        [contenteditable] > div:last-child,
        [contenteditable] blockquote:last-child,
        [contenteditable] pre:last-child {
          margin-bottom: 0;
        }
        [contenteditable] ul { list-style: disc; padding-left: 2em; margin: 0.3em 0; }
        [contenteditable] ol { list-style: decimal; padding-left: 2em; margin: 0.3em 0; }
        [contenteditable] li.ql-indent-1 { margin-left: 2em; }
        [contenteditable] li.ql-indent-2 { margin-left: 4em; }
        [contenteditable] li.ql-indent-3 { margin-left: 6em; }
        [contenteditable] li.ql-indent-4 { margin-left: 8em; }
        [contenteditable] li.ql-indent-5 { margin-left: 10em; }
        [contenteditable] li.ql-indent-6 { margin-left: 12em; }
        [contenteditable] ul ul { list-style: circle; }
        [contenteditable] ul ul ul { list-style: square; }
        [contenteditable] ol ol { list-style: lower-alpha; }
        [contenteditable] ul > li.ql-indent-1,
        [contenteditable] ul > li.ql-indent-4 { list-style-type: circle; }
        [contenteditable] ul > li.ql-indent-2,
        [contenteditable] ul > li.ql-indent-5 { list-style-type: square; }
        [contenteditable] ul > li.ql-indent-3,
        [contenteditable] ul > li.ql-indent-6 { list-style-type: disc; }
        [contenteditable] ol > li.ql-indent-1,
        [contenteditable] ol > li.ql-indent-4 { list-style-type: lower-alpha; }
        [contenteditable] ol > li.ql-indent-2,
        [contenteditable] ol > li.ql-indent-5 { list-style-type: lower-roman; }
        [contenteditable] ol > li.ql-indent-3,
        [contenteditable] ol > li.ql-indent-6 { list-style-type: decimal; }
        [contenteditable] li { margin: 0.15em 0; text-align: left; }
        [contenteditable] li.ql-align-center { text-align: center; }
        [contenteditable] li.ql-align-right { text-align: right; }
        [contenteditable] li.ql-align-justify { text-align: justify; }
        [contenteditable] table p,
        [contenteditable] table div {
          margin: 0;
        }
        [contenteditable] blockquote { border-left: 3px solid #94a3b8; padding-left: 1em; color: #64748b; margin: 0.5em 0; }
        [contenteditable] table { border-collapse: collapse; width: 100%; margin: 8px 0; }
        [contenteditable] td, [contenteditable] th { border: 1px solid #ccc; padding: 5px 7px; }
        [contenteditable] th { background: #f1f5f9; font-weight: bold; }
        [contenteditable] pre { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 4px; padding: 8px 12px; font-family: "Courier New", monospace; font-size: 9pt; white-space: pre-wrap; }
        [contenteditable] a { color: #2563eb; }
        [contenteditable] h1 { font-size: 2em; font-weight: bold; margin: 0.5em 0 0.25em; }
        [contenteditable] h2 { font-size: 1.5em; font-weight: bold; margin: 0.4em 0 0.2em; }
        [contenteditable] h3 { font-size: 1.17em; font-weight: bold; margin: 0.35em 0 0.15em; }
        [contenteditable] h4 { font-size: 1em; font-weight: bold; margin: 0.3em 0 0.1em; }
        [contenteditable] .var-pill { background: #eff6ff; color: #1d4ed8; border: 1px solid #bfdbfe; border-radius: 3px; padding: 0 3px; font-size: .88em; font-family: monospace; white-space: nowrap; }
        [contenteditable] .page-break-marker { display: flex; align-items: center; justify-content: center; margin: 16px 0; padding: 8px 0; border-top: 2px dashed #94a3b8; border-bottom: 2px dashed #94a3b8; background: #f8fafc; color: #64748b; font-size: 11px; font-weight: 600; letter-spacing: 1px; user-select: none; cursor: default; }
        [contenteditable] .page-break-marker span { pointer-events: none; }
      `}</style>
    </div>
  );
});

// ─── PAGE CALCULATOR ─────────────────────────────────────────────────────────
function usePageCount({ richContent, headerConfig, footerConfig }) {
  const [pageCount, setPageCount] = useState(1);
  const timerRef = useRef(null);

  useEffect(() => {
    clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      const iframe = document.createElement('iframe');
      iframe.style.cssText = `position:fixed;left:-9999px;top:0;width:${PAGE_W_PX}px;height:${PAGE_H_PX}px;border:none;visibility:hidden;`;
      document.body.appendChild(iframe);
      const doc = iframe.contentDocument;
      const headerH = headerConfig?.enabled !== false ? (headerConfig?.styles?.logoHeight || 60) + 60 : 0;
      const footerH = footerConfig?.enabled !== false ? 24 : 0;
      const usableH = PAGE_H_PX - MARGIN_PX * 2 - headerH - footerH;

      doc.open();
      doc.write(`<!DOCTYPE html><html><head><style>
        *{box-sizing:border-box;margin:0;padding:0}
        body{font-family:Helvetica,Arial,sans-serif;font-size:11pt;line-height:1.65;color:#111;width:${PAGE_W_PX - MARGIN_PX * 2}px;padding:0;}
        .ql-align-center{text-align:center}.ql-align-right{text-align:right}.ql-align-justify{text-align:justify}
        .ql-indent-1{padding-left:2em}.ql-indent-2{padding-left:4em}.ql-indent-3{padding-left:6em}.ql-indent-4{padding-left:8em}.ql-indent-5{padding-left:10em}.ql-indent-6{padding-left:12em}
        ul{list-style:disc;padding-left:2em;margin:0.3em 0}ol{list-style:decimal;padding-left:2em;margin:0.3em 0}
        li{margin:0.15em 0;text-align:left}
        li.ql-indent-1{margin-left:2em}li.ql-indent-2{margin-left:4em}li.ql-indent-3{margin-left:6em}li.ql-indent-4{margin-left:8em}li.ql-indent-5{margin-left:10em}li.ql-indent-6{margin-left:12em}
        ul>li.ql-indent-1,ul>li.ql-indent-4{list-style-type:circle}ul>li.ql-indent-2,ul>li.ql-indent-5{list-style-type:square}ul>li.ql-indent-3,ul>li.ql-indent-6{list-style-type:disc}
        ol>li.ql-indent-1,ol>li.ql-indent-4{list-style-type:lower-alpha}ol>li.ql-indent-2,ol>li.ql-indent-5{list-style-type:lower-roman}ol>li.ql-indent-3,ol>li.ql-indent-6{list-style-type:decimal}
        li.ql-align-center{text-align:center}li.ql-align-right{text-align:right}li.ql-align-justify{text-align:justify}
        h1{font-size:2em;font-weight:bold;margin:0.5em 0 0.25em}
        h2{font-size:1.5em;font-weight:bold;margin:0.4em 0 0.2em}
        h3{font-size:1.17em;font-weight:bold;margin:0.35em 0 0.15em}
        h4{font-size:1em;font-weight:bold;margin:0.3em 0 0.1em}
        table{border-collapse:collapse;width:100%;margin:8px 0}
        td,th{border:1px solid #ccc;padding:5px 7px}th{background:#f1f5f9;font-weight:bold}
        blockquote{border-left:3px solid #94a3b8;padding-left:1em;color:#64748b;margin:0.5em 0}
        pre{background:#f8fafc;border:1px solid #e2e8f0;border-radius:4px;padding:8px 12px;font-family:'Courier New',monospace;font-size:9pt;white-space:pre-wrap}
      </style></head><body><div>${richContent || ''}</div></body></html>`);
      doc.close();

      const contentH = doc.body.scrollHeight;
      const pages = Math.max(1, Math.ceil(contentH / usableH));
      setPageCount(pages);
      document.body.removeChild(iframe);
    }, 300);
    return () => clearTimeout(timerRef.current);
  }, [richContent, headerConfig, footerConfig]);

  return pageCount;
}

// ─── CANVAS HEADER ────────────────────────────────────────────────────────────
function CanvasHeader({ config, clientLogo }) {
  if (!config?.enabled) return null;
  const st = config.styles || {};
  const align = st.alignment || 'left';
  const justifyMap = { left: 'flex-start', center: 'center', right: 'flex-end' };
  const logo = config.logoUrl || clientLogo;

  return (
    <div style={{ marginBottom: 16 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, justifyContent: justifyMap[align] || 'flex-start', flexWrap: 'wrap', marginBottom: 8 }}>
        {logo && (
          <img src={logo} alt="Logo"
            onError={e => { e.target.style.display = 'none'; }}
            style={{
              height: `${st.logoHeight || 60}px`,
              width: st.logoWidth ? `${st.logoWidth}px` : 'auto',
              objectFit: 'contain',
            }}
          />
        )}
        <div style={{ textAlign: align }}>
          {config.title && (
            <div style={{
              fontSize: `${st.fontSize || 22}px`,
              fontWeight: st.bold ? 'bold' : (st.fontWeight || 'bold'),
              fontStyle: st.italic ? 'italic' : 'normal',
              textDecoration: st.underline ? 'underline' : 'none',
              color: st.textColor || '#000000',
              fontFamily: st.fontFamily ? EDITOR_FONTS.find(f => f.label === st.fontFamily)?.value || 'inherit' : 'inherit',
              lineHeight: 1.2,
            }}>
              {config.title}
            </div>
          )}
          {config.description && (
            <div style={{
              fontSize: `${Math.round((st.fontSize || 22) * 0.45)}px`,
              color: st.textColor || '#555',
              opacity: 0.75,
              marginTop: 3,
              fontFamily: st.fontFamily ? EDITOR_FONTS.find(f => f.label === st.fontFamily)?.value || 'inherit' : 'inherit',
            }}>
              {config.description}
            </div>
          )}
        </div>
      </div>
      <div style={{ borderTop: '1.5px solid #e2e8f0', marginBottom: 14 }} />
    </div>
  );
}

// ─── CANVAS FOOTER ────────────────────────────────────────────────────────────
function CanvasFooter({ config, logoUrl, pageCount, companyName }) {
  if (!config?.enabled) return null;
  const today = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
  const st = config.styles || {};
  const fSize = Math.max(6, st.fontSize || 8);

  const left = config.showLogo !== false
    ? (logoUrl ? <img src={logoUrl} alt="" style={{ height: 12, objectFit: 'contain', verticalAlign: 'middle' }} onError={e => e.target.style.display='none'} />
               : <span style={{ fontWeight: 600, fontSize: `${fSize}pt` }}>{config.companyName || companyName || ''}</span>)
    : (config.companyName || companyName || '');

  const centerParts = [
    config.content ? config.content.replace(/<[^>]+>/g, '') : '',
    config.showDate ? today : '',
  ].filter(Boolean);

  return (
    <div style={{
      borderTop: '0.5pt solid #cbd5e1',
      display: 'flex',
      justifyContent: 'space-between',
      alignItems: 'center',
      padding: '3px 0 1px',
      color: '#64748b',
      fontSize: `${fSize}pt`,
      lineHeight: 1.2,
      marginTop: st.marginTop != null ? st.marginTop : 8,
    }}>
      <span style={{ flex: 1, textAlign: 'left' }}>{left}</span>
      <span style={{ flex: 2, textAlign: 'center' }}>{centerParts.join(' · ')}</span>
      <span style={{ flex: 1, textAlign: 'right' }}>
        {config.showPageNumbers !== false && (
          <span style={{ color: '#94a3b8', fontSize: `${fSize}pt` }}>
            Page 1 of {pageCount}
          </span>
        )}
      </span>
    </div>
  );
}

// ─── HEADER CONFIG PANEL ──────────────────────────────────────────────────────
function HeaderPanel({ config, onChange, clientLogo }) {
  const set = (k, v) => onChange(prev => ({ ...prev, [k]: v }));
  const setSt = (k, v) => onChange(prev => ({ ...prev, styles: { ...prev.styles, [k]: v } }));
  const st = config.styles || {};

  return (
    <div className="space-y-3 text-xs">
      <div className="flex items-center justify-between">
        <span className="font-semibold text-slate-700 text-xs uppercase tracking-wide">Header</span>
        <label className="flex items-center gap-1.5 cursor-pointer">
          <span className="text-slate-500">Show</span>
          <div
            onClick={() => set('enabled', !config.enabled)}
            className={cls('w-8 h-4 rounded-full transition-colors cursor-pointer relative', config.enabled ? 'bg-blue-500' : 'bg-slate-300')}>
            <div className={cls('absolute top-0.5 w-3 h-3 bg-white rounded-full shadow transition-transform', config.enabled ? 'translate-x-4' : 'translate-x-0.5')} />
          </div>
        </label>
      </div>

      {config.enabled && (
        <div className="space-y-2.5">
          <div>
            <label className="text-slate-500 block mb-1">Title</label>
            <input value={config.title || ''} onChange={e => set('title', e.target.value)}
              className="w-full border border-slate-200 rounded px-2 py-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-blue-400"
              placeholder="Document Title" />
          </div>
          <div>
            <label className="text-slate-500 block mb-1">Subtitle</label>
            <textarea value={config.description || ''} onChange={e => set('description', e.target.value)}
              className="w-full border border-slate-200 rounded px-2 py-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-blue-400 resize-none h-14"
              placeholder="Description or subtitle..." />
          </div>
          <div>
            <label className="text-slate-500 block mb-1">Logo URL</label>
            <div className="flex gap-1">
              <input value={config.logoUrl || ''} onChange={e => set('logoUrl', e.target.value)}
                className="flex-1 border border-slate-200 rounded px-2 py-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-blue-400"
                placeholder="https://..." />
              {config.logoUrl && (
                <button onClick={() => set('logoUrl', '')} className="text-slate-400 hover:text-red-500 px-1"><XIcon /></button>
              )}
            </div>
            {(config.logoUrl || clientLogo) && (
              <div className="mt-1.5 p-2 border border-slate-100 rounded bg-slate-50 flex justify-center">
                <img src={config.logoUrl || clientLogo} alt="Preview" style={{ height: `${st.logoHeight || 60}px`, objectFit: 'contain' }} onError={e => e.target.style.display='none'} />
              </div>
            )}
          </div>

          <details className="group">
            <summary className="cursor-pointer text-slate-500 hover:text-blue-600 list-none flex items-center gap-1">
              <ChevronDownIcon /> Styling
            </summary>
            <div className="mt-2 space-y-2.5 pl-1">
              <div className="grid grid-cols-2 gap-2">
                <div>
                  <label className="text-slate-400 block mb-1">Alignment</label>
                  <select value={st.alignment || 'left'} onChange={e => setSt('alignment', e.target.value)}
                    className="w-full border border-slate-200 rounded px-1.5 py-1 text-xs">
                    <option value="left">Left</option>
                    <option value="center">Center</option>
                    <option value="right">Right</option>
                  </select>
                </div>
                <div>
                  <label className="text-slate-400 block mb-1">Font</label>
                  <select value={st.fontFamily || 'Default'} onChange={e => setSt('fontFamily', e.target.value)}
                    className="w-full border border-slate-200 rounded px-1.5 py-1 text-xs">
                    {EDITOR_FONTS.map(f => <option key={f.label} value={f.label}>{f.label}</option>)}
                  </select>
                </div>
              </div>
              <div className="grid grid-cols-2 gap-2">
                <div>
                  <label className="text-slate-400 block mb-1">Title Size: {st.fontSize || 22}px</label>
                  <input type="range" min={12} max={48} value={st.fontSize || 22} onChange={e => setSt('fontSize', +e.target.value)}
                    className="w-full h-1.5 accent-blue-500" />
                </div>
                <div>
                  <label className="text-slate-400 block mb-1">Logo H: {st.logoHeight || 60}px</label>
                  <input type="range" min={20} max={150} step={5} value={st.logoHeight || 60} onChange={e => setSt('logoHeight', +e.target.value)}
                    className="w-full h-1.5 accent-blue-500" />
                </div>
              </div>
              <div>
                <label className="text-slate-400 block mb-1">Text Color</label>
                <div className="flex gap-1.5">
                  <input type="color" value={st.textColor || '#000000'} onChange={e => setSt('textColor', e.target.value)} className="w-7 h-7 rounded border border-slate-200 p-0.5 cursor-pointer" />
                  <input value={st.textColor || '#000000'} onChange={e => setSt('textColor', e.target.value)} className="flex-1 border border-slate-200 rounded px-2 text-xs" />
                </div>
              </div>
            </div>
          </details>
        </div>
      )}
    </div>
  );
}

// ─── FOOTER CONFIG PANEL ──────────────────────────────────────────────────────
function FooterPanel({ config, onChange }) {
  const set = (k, v) => onChange(prev => ({ ...prev, [k]: v }));
  const setSt = (k, v) => onChange(prev => ({ ...prev, styles: { ...prev.styles, [k]: v } }));
  const st = config.styles || {};

  return (
    <div className="space-y-3 text-xs">
      <div className="flex items-center justify-between">
        <span className="font-semibold text-slate-700 text-xs uppercase tracking-wide">Footer</span>
        <label className="flex items-center gap-1.5 cursor-pointer">
          <span className="text-slate-500">Show</span>
          <div onClick={() => set('enabled', !config.enabled)}
            className={cls('w-8 h-4 rounded-full transition-colors cursor-pointer relative', config.enabled ? 'bg-blue-500' : 'bg-slate-300')}>
            <div className={cls('absolute top-0.5 w-3 h-3 bg-white rounded-full shadow transition-transform', config.enabled ? 'translate-x-4' : 'translate-x-0.5')} />
          </div>
        </label>
      </div>

      {config.enabled && (
        <div className="space-y-2.5">
          <div>
            <label className="text-slate-500 block mb-1">Center Text</label>
            <input value={config.content || ''} onChange={e => set('content', e.target.value)}
              className="w-full border border-slate-200 rounded px-2 py-1.5 text-xs focus:outline-none focus:ring-1 focus:ring-blue-400"
              placeholder="Confidential · Internal Use Only" />
          </div>
          <div className="grid grid-cols-3 gap-2">
            {[
              { key: 'showDate',        label: 'Date' },
              { key: 'showLogo',        label: 'Logo' },
              { key: 'showPageNumbers', label: 'Page #s' },
            ].map(({ key, label }) => (
              <label key={key} className="flex items-center gap-1.5 cursor-pointer border border-slate-100 rounded p-1.5 bg-slate-50 hover:bg-slate-100">
                <div onClick={() => set(key, !config[key])}
                  className={cls('w-7 h-3.5 rounded-full transition-colors cursor-pointer relative flex-shrink-0', config[key] !== false ? 'bg-blue-500' : 'bg-slate-300')}>
                  <div className={cls('absolute top-0.5 w-2.5 h-2.5 bg-white rounded-full shadow transition-transform', config[key] !== false ? 'translate-x-3.5' : 'translate-x-0.5')} />
                </div>
                <span className="text-slate-600 truncate">{label}</span>
              </label>
            ))}
          </div>
          <div className="grid grid-cols-2 gap-2">
            <div>
              <label className="text-slate-400 block mb-1">Font Size</label>
              <select value={String(st.fontSize || 9)} onChange={e => setSt('fontSize', +e.target.value)}
                className="w-full border border-slate-200 rounded px-1.5 py-1 text-xs">
                {[8, 9, 10, 11, 12].map(s => <option key={s} value={s}>{s}pt</option>)}
              </select>
            </div>
            <div>
              <label className="text-slate-400 block mb-1">Margin Top</label>
              <select value={String(st.marginTop || 16)} onChange={e => setSt('marginTop', +e.target.value)}
                className="w-full border border-slate-200 rounded px-1.5 py-1 text-xs">
                {[8, 12, 16, 24, 32].map(s => <option key={s} value={s}>{s}px</option>)}
              </select>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── BUILD PRINT HTML ─────────────────────────────────────────────────────────
async function toDataUrl(url) {
  if (!url) return '';
  if (url.startsWith('data:')) return url;
  try {
    const resp = await fetch(url, { mode: 'cors' });
    const blob = await resp.blob();
    return new Promise(resolve => {
      const reader = new FileReader();
      reader.onloadend = () => resolve(reader.result || '');
      reader.onerror = () => resolve('');
      reader.readAsDataURL(blob);
    });
  } catch { return ''; }
}

async function buildPrintHtml({ richContent, headerConfig, footerConfig, sampleData, documentName, borderConfig, clientLogo, pageCount }) {
  let body = (richContent || '')
    .replace(/<span[^>]*>\s*(\{\{[\w.]+\}\})\s*<\/span>/g, '$1')
    .replace(/\{\{([\w.]+)\}\}/g, (match, k) => {
      const v = sampleData?.[k];
      return (v != null && v !== '') ? String(v) : match;
    })
    .replace(/<span\s[^>]*class="var-pill"[^>]*>([\s\S]*?)<\/span>/g, '$1');

  const hst = headerConfig?.styles || {};
  const fst = footerConfig?.styles || {};
  const hOn = headerConfig?.enabled !== false;
  const fOn = footerConfig?.enabled !== false;
  const rawLogo = headerConfig?.logoUrl || clientLogo || '';
  const logo = await toDataUrl(rawLogo);
  const hAlign = hst.alignment || 'left';
  const fSize = Math.max(6, fst.fontSize || 9);
  const today = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
  const centerTxt = footerConfig?.content ? footerConfig.content.replace(/<[^>]+>/g, '') : '';
  const companyName = sampleData?.clientName || footerConfig?.companyName || '';
  const FOOTER_H = fOn ? '0.4in' : '0';

  const headerHtml = hOn ? `
<div id="doc-header">
  <div class="hdr-row" style="justify-content:${hAlign==='center'?'center':hAlign==='right'?'flex-end':'flex-start'};background:${hst.backgroundColor||'#ffffff'}">
    ${logo ? `<img src="${logo}" style="height:${hst.logoHeight||60}px;object-fit:contain;margin-right:14px" onerror="this.style.display='none'">` : ''}
    <div style="text-align:${hAlign}">
      ${headerConfig.title ? `<div style="font-size:${hst.fontSize||22}px;font-weight:${hst.bold?'bold':hst.fontWeight||'bold'};font-style:${hst.italic?'italic':'normal'};text-decoration:${hst.underline?'underline':'none'};color:${hst.textColor||'#000000'};line-height:1.2">${headerConfig.title}</div>` : ''}
      ${headerConfig.description ? `<div style="font-size:${Math.round((hst.fontSize||22)*0.45)}px;color:${hst.textColor||'#555'};opacity:0.75;margin-top:3px">${headerConfig.description}</div>` : ''}
    </div>
  </div>
  <div class="hdr-rule"></div>
</div>` : '';

  const fLeft = footerConfig?.showLogo !== false
    ? (logo ? `<img src="${logo}" style="height:14px;object-fit:contain;vertical-align:middle" onerror="this.style.display='none'">` : `<strong>${companyName}</strong>`)
    : companyName;
  const fCenter = [centerTxt, footerConfig?.showDate ? today : ''].filter(Boolean).join(' · ');
  const fRight = footerConfig?.showPageNumbers !== false ? `<span class="page-counter"></span>` : '';

  const footerHtml = fOn ? `
<div id="print-footer" style="font-size:${fSize}pt">
  <div class="ftr-row">
    <span class="ftr-l">${fLeft}</span>
    <span class="ftr-c">${fCenter}</span>
    <span class="ftr-r">${fRight}</span>
  </div>
</div>` : '';

  return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>${(documentName||'Document').replace(/</g,'&lt;')}</title>
<style>
*,*::before,*::after{box-sizing:border-box}
html,body{margin:0;padding:0}
body{font-family:Helvetica,Arial,sans-serif;font-size:11pt;color:#111;line-height:1.65;padding:0.75in;padding-bottom:calc(0.75in + 50px)}
@page{size:letter;margin:0.75in 0.75in ${fOn?'0.75in':'0.75in'} 0.75in}
@media print{
  body{padding:0;padding-bottom:${FOOTER_H}}
  #print-footer{display:flex!important;position:fixed;bottom:0;left:0;right:0;background:#fff;padding:2pt 0.75in 2pt;border-top:0.5pt solid #cbd5e1;color:#64748b;font-size:${fSize}pt;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .ftr-row{display:flex;width:100%;justify-content:space-between;align-items:center}
  .ftr-l{flex:1;text-align:left}.ftr-c{flex:2;text-align:center}.ftr-r{flex:1;text-align:right}
  .page-counter::before{content:"Page " counter(page) " of " counter(pages)}
  ${borderConfig?.enabled ? `body{border:1px solid ${borderConfig.color||'#000'}}` : ''}
}
@media screen{
  #print-footer{display:flex;justify-content:space-between;align-items:center;margin-top:20pt;border-top:0.5pt solid #cbd5e1;padding-top:6pt;color:#64748b}
  .ftr-row{display:flex;width:100%;justify-content:space-between;align-items:center}
  .ftr-l{flex:1;text-align:left}.ftr-c{flex:2;text-align:center}.ftr-r{flex:1;text-align:right}
  .page-counter::before{content:"Page 1 of ${pageCount || 1}"}
}
#doc-header{margin-bottom:16pt}
.hdr-row{display:flex;align-items:center;gap:12px;margin-bottom:10px;flex-wrap:wrap}
.hdr-rule{border:none;border-top:1.5pt solid #e2e8f0;margin-bottom:14pt}
 p.ql-align-center,div.ql-align-center,li.ql-align-center{text-align:center}
 p.ql-align-right,div.ql-align-right,li.ql-align-right{text-align:right}
 p.ql-align-justify,div.ql-align-justify{text-align:justify}
p{margin:0.15em 0}
h1{font-size:2em;font-weight:bold;margin:0.5em 0 0.3em}
h2{font-size:1.5em;font-weight:bold;margin:0.4em 0 0.25em}
h3{font-size:1.17em;font-weight:bold;margin:0.35em 0 0.2em}
h4{font-size:1em;font-weight:bold;margin:0.3em 0 0.15em}
ul{list-style:disc;padding-left:2em;margin:0.3em 0}
ol{list-style:decimal;padding-left:2em;margin:0.3em 0}
ul ul{list-style:circle}ul ul ul{list-style:square}
ol ol{list-style:lower-alpha}
 li{margin:0.15em 0;text-align:left}
 .ql-indent-1{padding-left:2em}.ql-indent-2{padding-left:4em}.ql-indent-3{padding-left:6em}.ql-indent-4{padding-left:8em}.ql-indent-5{padding-left:10em}.ql-indent-6{padding-left:12em}
 li.ql-indent-1{margin-left:2em}li.ql-indent-2{margin-left:4em}li.ql-indent-3{margin-left:6em}li.ql-indent-4{margin-left:8em}li.ql-indent-5{margin-left:10em}li.ql-indent-6{margin-left:12em}
 ul>li.ql-indent-1,ul>li.ql-indent-4{list-style-type:circle}ul>li.ql-indent-2,ul>li.ql-indent-5{list-style-type:square}ul>li.ql-indent-3,ul>li.ql-indent-6{list-style-type:disc}
 ol>li.ql-indent-1,ol>li.ql-indent-4{list-style-type:lower-alpha}ol>li.ql-indent-2,ol>li.ql-indent-5{list-style-type:lower-roman}ol>li.ql-indent-3,ol>li.ql-indent-6{list-style-type:decimal}
table{border-collapse:collapse;width:100%;margin:8pt 0}
td,th{border:1px solid #ccc;padding:5pt 6pt}
th{background:#f1f5f9;font-weight:bold}
a{color:#2563eb}
pre{background:#f8fafc;border:1px solid #e2e8f0;border-radius:3px;padding:8pt 10pt;font-family:'Courier New',monospace;font-size:9pt;white-space:pre-wrap;word-break:break-all}
code{font-family:'Courier New',monospace;font-size:0.9em}
blockquote{border-left:3px solid #94a3b8;padding-left:1em;color:#64748b;margin:0.5em 0}
hr{border:none;border-top:1px solid #e2e8f0;margin:8pt 0}
.var-pill{background:none!important;color:inherit!important;border-radius:0;padding:0}
.page-break-marker{page-break-before:always;break-before:page;display:block;height:0;margin:0;padding:0;border:none;visibility:hidden}.page-break-marker span{display:none}
</style>
</head>
<body>
${headerHtml}
<div id="content">${body}</div>
${footerHtml}
</body>
</html>`;
}

function triggerPrint(htmlString) {
  const w = window.open('', '_blank', 'width=960,height=720,scrollbars=yes');
  if (!w) { alert('Pop-up blocked — please allow pop-ups and try again.'); return; }
  w.document.open();
  w.document.write(htmlString);
  w.document.close();
  const doPrint = () => { w.focus(); w.print(); };
  w.addEventListener('load', () => setTimeout(doPrint, 350));
  setTimeout(() => { if (w.document.readyState === 'complete') doPrint(); }, 1400);
}

// =============================================================================
//  MAIN EXPORT
// =============================================================================
export function CustomFormBuilder() {
  const { toast, ToastContainer } = useToast();
  const exportRef = useRef(null);
  const editorRef = useRef(null);
  const docxInputRef = useRef(null);

  const [preview, setPreview] = useState(false);
  const [sidePanel, setSidePanel] = useState('content');
  const [isLoading, setIsLoading] = useState(false);
  const [isExporting, setIsExporting] = useState(false);
  const [isImportingDocx, setIsImportingDocx] = useState(false);
  const [showSaveToDocsDialog, setShowSaveToDocsDialog] = useState(false);

  // ── Saved forms ──
  const {
    savedTemplates, loading: templatesLoading, currentTemplateId,
    currentTemplateName, setCurrentTemplateName,
    saveTemplate, loadTemplate, deleteTemplate, newTemplate,
  } = useSavedFormTemplates('custom_form');
  const [clients,         setClients]         = useState([]);
  const [selectedClient,  setSelectedClient]  = useState('');
  const [formMetadata,    setFormMetadata]    = useState({ name: '', description: '' });
  const [homeowners,      setHomeowners]      = useState([]);
  const [selectedHomeownerId, setSelectedHomeownerId] = useState('');
  const [customVariables, setCustomVariables] = useState([]);

  // ── Config state ──
  const [headerConfig, setHeaderConfig] = useState({
    enabled: true, title: 'Official Notice', logoUrl: '', description: '',
    styles: { alignment: 'left', logoHeight: 60, textColor: '#000000', backgroundColor: '#ffffff', fontFamily: 'Default', fontSize: 22, fontWeight: 'bold' },
  });
  const [footerConfig, setFooterConfig] = useState({
    enabled: true, content: '', showPageNumbers: true, showDate: true, showLogo: true, companyName: '',
    styles: { fontSize: 9, marginTop: 16 },
  });
  const [borderConfig, setBorderConfig] = useState({ enabled: false, color: '#000000' });
  const [globalSettings, setGlobalSettings] = useState({ lineWidth: 100, fontFamily: 'Helvetica', attachValidationProof: false });
  const [richContent, setRichContent] = useState('<p>Start typing here...</p>');
  const [docName, setDocName] = useState('My Document');
  const [sampleData, setSampleData] = useState({
    propertyAddress: '123 Palm Tree Lane, Miami FL 33101',
    ownerName: 'John & Jane Doe',
    currentDate: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }),
    pageNumber: '1',
    accountNumber: 'ACC-99823',
    currentBalance: '$450.00',
    clientName: 'Association Name',
  });

  const sanitizedRichContent = useMemo(() => sanitizeRichTextHtml(richContent), [richContent]);
  const pageCount = usePageCount({ richContent: sanitizedRichContent, headerConfig, footerConfig });

  // ── Fetch associations ──
  useEffect(() => {
    const fetchClients = async () => {
      const { data, error } = await supabase.from('associations').select('id, name, logo_url').eq('status', 'active').order('name');
      if (!error && data) {
        setClients(data.map(a => ({ id: a.id, name: a.name, notice_logo: a.logo_url || '' })));
        if (data.length > 0 && !selectedClient) setSelectedClient(data[0].id);
      }
    };
    fetchClients();
  }, [selectedClient]);

  useEffect(() => {
    const fetchClientContext = async () => {
      if (!selectedClient) { setHomeowners([]); setSelectedHomeownerId(''); return; }

      const [{ data: association }, { data: owners }, { data: boardMembers }] = await Promise.all([
        supabase.from('associations').select('id, name').eq('id', selectedClient).single(),
        supabase.from('owners')
          .select('id, first_name, last_name, property_address, mailing_address, alternate_address_1, alternate_address_2, balance, unit_id, units(unit_number, address, account_number)')
          .eq('association_id', selectedClient).eq('status', 'active').order('last_name'),
        supabase.from('board_members').select('member_name, role').eq('association_id', selectedClient).ilike('role', '%president%').limit(1),
      ]);

      const formatCurrency = (value) => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(Number(value || 0));

      const ownersByUnit = {};
      for (const owner of (owners || [])) {
        const key = owner.unit_id || owner.id;
        if (!ownersByUnit[key]) ownersByUnit[key] = [];
        ownersByUnit[key].push(owner);
      }

      const mappedHomeowners = Object.values(ownersByUnit).map(group => {
        const primary = group[0];
        const combinedName = combineOwnerNames(group);
        const propertyAddress = primary.property_address || primary.units?.address || '';
        const allAddresses = [
          primary.mailing_address || propertyAddress,
          ...group.flatMap(o => [o.alternate_address_1, o.alternate_address_2].filter(Boolean)),
        ].filter((v, i, a) => v && a.indexOf(v) === i);

        return {
          id: primary.id,
          owner_name: combinedName,
          property_address: propertyAddress,
          recipient_address: primary.mailing_address || propertyAddress,
          alternate_addresses: allAddresses.slice(1),
          unit_number: primary.units?.unit_number || primary.units?.account_number || '',
          account_number: primary.units?.account_number || '',
          formatted_balance: formatCurrency(primary.balance),
        };
      });

      setHomeowners(mappedHomeowners);
      setSelectedHomeownerId(current => mappedHomeowners.some(owner => owner.id === current) ? current : '');
      setSampleData(prev => ({
        ...prev,
        clientName: association?.name || prev.clientName,
        boardPresident: boardMembers?.[0]?.member_name || prev.boardPresident || '',
      }));
    };
    fetchClientContext();
  }, [selectedClient]);

  // ── Fetch custom variables ──
  useEffect(() => {
    if (!selectedClient) { setCustomVariables([]); return; }
    supabase.from('custom_variables')
      .select('variable_name, display_label, default_value, category')
      .eq('association_id', selectedClient).order('category').order('variable_name')
      .then(({ data }) => {
        setCustomVariables(data || []);
        if (data?.length) {
          setSampleData(prev => {
            const merged = { ...prev };
            data.forEach(v => { if (v.default_value && !merged[v.variable_name]) merged[v.variable_name] = v.default_value; });
            return merged;
          });
        }
      });
  }, [selectedClient]);

  useEffect(() => {
    const selectedClientRecord = clients.find(client => client.id === selectedClient);
    const selectedHomeowner = homeowners.find(owner => owner.id === selectedHomeownerId);
    setSampleData(prev => ({
      ...prev,
      clientName: selectedClientRecord?.name || prev.clientName,
      ownerName: selectedHomeowner?.owner_name || 'John & Jane Doe',
      ownersNames: selectedHomeowner?.owner_name || 'John & Jane Doe',
      propertyAddress: selectedHomeowner?.property_address || '123 Palm Tree Lane, Miami FL 33101',
      recipientAddress: selectedHomeowner?.recipient_address || selectedHomeowner?.property_address || '123 Palm Tree Lane, Miami FL 33101',
      unitId: selectedHomeowner?.unit_number || selectedHomeowner?.account_number || 'ACC-99823',
      accountNumber: selectedHomeowner?.account_number || selectedHomeowner?.unit_number || 'ACC-99823',
      balance: selectedHomeowner?.formatted_balance || '$450.00',
    }));
  }, [clients, homeowners, selectedClient, selectedHomeownerId]);

  const clientLogo = clients.find(c => c.id === selectedClient)?.notice_logo || '';
  const companyName = sampleData?.clientName || '';
  const systemVariables = [
    { key: 'clientName', label: 'Association name' },
    { key: 'ownersNames', label: 'Recipient full name(s)' },
    { key: 'ownerName', label: 'Recipient name' },
    { key: 'propertyAddress', label: 'Property address' },
    { key: 'recipientAddress', label: 'Mailing address' },
    { key: 'currentDate', label: 'Current date' },
    { key: 'unitId', label: 'Unit / account number' },
    { key: 'accountNumber', label: 'Account number' },
    { key: 'balance', label: 'Current balance' },
    { key: 'boardPresident', label: 'Board president' },
  ];
  const variableOptions = [
    ...systemVariables,
    ...customVariables.map(v => ({ key: v.variable_name, label: v.display_label || v.variable_name, isCustom: true })),
  ];

  const previewFormConfig = useMemo(() => ({
    header: { ...headerConfig, logoUrl: headerConfig.logoUrl || clientLogo },
    footer: footerConfig,
    richContent,
    borderSettings: borderConfig,
    globalSettings,
    styles: {},
    blocks: richContent
      ? [{ id: 'rich-content-preview', content: richContent, styles: { fontFamily: globalSettings.fontFamily || 'Helvetica, Arial, sans-serif' } }]
      : [],
  }), [headerConfig, footerConfig, richContent, borderConfig, globalSettings, clientLogo]);

  const resetForm = useCallback(() => {
    newTemplate();
    setFormMetadata({ name: '', description: '' });
    setRichContent('<p>Start typing here...</p>');
    setBorderConfig({ enabled: false, color: '#000000' });
    setGlobalSettings({ lineWidth: 100, fontFamily: 'Helvetica', attachValidationProof: false });
    setDocName('My Document');
  }, [newTemplate]);

  const handleLoadForm = useCallback(async (formId) => {
    if (formId === 'new') { resetForm(); return; }
    const data = await loadTemplate(formId);
    if (!data) return;
    const cfg = data.form_data || {};
    setFormMetadata({ name: data.name, description: '' });
    setDocName(data.name || 'My Document');
    if (cfg.header) setHeaderConfig(cfg.header);
    if (cfg.footer) setFooterConfig(cfg.footer);
    if (cfg.borderSettings) setBorderConfig(cfg.borderSettings);
    if (cfg.globalSettings) setGlobalSettings(cfg.globalSettings);
    setRichContent(cfg.richContent || (cfg.blocks?.length ? cfg.blocks.map(b => `<p>${b.content || ''}</p>`).join('') : '<p></p>'));
  }, [loadTemplate, resetForm]);

  const handleSaveForm = useCallback(async () => {
    const name = formMetadata.name || docName;
    if (!name) { toast({ variant: 'destructive', title: 'Error', description: 'Please enter a template name.' }); return; }
    setIsLoading(true);
    try {
      const formData = { header: headerConfig, footer: footerConfig, richContent, borderSettings: borderConfig, globalSettings, styles: {} };
      const result = await saveTemplate(name, formData, selectedClient || null);
      if (result) toast({ title: 'Saved', description: `Template "${name}" saved successfully.` });
    } finally { setIsLoading(false); }
  }, [formMetadata, docName, headerConfig, footerConfig, richContent, borderConfig, globalSettings, selectedClient, saveTemplate, toast]);

  const insertVariable = useCallback(variableName => {
    const token = `{{${variableName.replace(/[{}]/g, '')}}}`;
    if (editorRef.current?.insertAtCursor) {
      editorRef.current.insertAtCursor(`<span class="var-pill">${token}</span>&#8203;`);
    } else {
      setRichContent(p => p + `<p><span class="var-pill">${token}</span></p>`);
    }
    toast({ title: 'Variable Inserted', description: `${token} inserted at cursor.` });
  }, [toast]);

  const handlePrint = useCallback(async () => {
    setIsExporting(true);
    try {
      if (!exportRef.current) throw new Error('PDF generator unavailable.');

      const blob = await exportRef.current.generatePdf(formMetadata.name || docName || 'Document', true, true);
      if (!blob) throw new Error('Could not generate the PDF preview.');

      const url = URL.createObjectURL(blob);
      const win = window.open(url, '_blank');

      if (win) {
        win.opener = null;
        setTimeout(() => URL.revokeObjectURL(url), 60000);
      } else {
        URL.revokeObjectURL(url);
        throw new Error('Pop-up blocked — please allow pop-ups and try again.');
      }
    } catch (e) {
      toast({ variant: 'destructive', title: 'Print Error', description: e.message || 'Print preview failed.' });
    } finally { setIsExporting(false); }
  }, [formMetadata.name, docName, toast]);

  const handleExportPdf = useCallback(async () => {
    setIsExporting(true);
    try {
      if (exportRef.current) await exportRef.current.generatePdf(formMetadata.name || docName || 'Form');
    } catch (e) {
      toast({ variant: 'destructive', title: 'Export Error', description: e.message || 'PDF generation failed.' });
    } finally { setIsExporting(false); }
  }, [formMetadata.name, docName, toast]);

  const handleImportDocx = useCallback(async (event) => {
    const file = event.target.files?.[0];
    event.target.value = '';
    if (!file) return;

    setIsImportingDocx(true);
    try {
      const { html, messages } = await convertDocxFileToHtml(file);
      setRichContent(html);
      editorRef.current?.focus?.();

      const warningCount = messages.filter((message) => message.type === 'warning').length;
      toast({
        title: 'DOCX imported',
        description: warningCount > 0
          ? `Imported ${file.name} with ${warningCount} formatting note${warningCount === 1 ? '' : 's'}.`
          : `Imported ${file.name}.`,
      });
    } catch (error) {
      toast({
        variant: 'destructive',
        title: 'Import failed',
        description: error?.message || 'Could not import the DOCX file.',
      });
    } finally {
      setIsImportingDocx(false);
    }
  }, [toast]);

  const contentAreaStyle = {
    padding: `${MARGIN_PX}px`,
    paddingBottom: footerConfig?.enabled !== false ? MARGIN_PX + 52 : MARGIN_PX,
    flex: 1,
    minHeight: 0,
  };

  // ── Render ──
  return (
    <div className="flex flex-col h-full min-h-[600px]">
      <ToastContainer />

      {/* Header — matches other form pages */}
      <div className="px-6 pt-6 pb-0">
        <div className="flex items-center justify-between">
          <div>
            <h2 className="text-lg font-semibold text-foreground">Custom Form Builder</h2>
            <p className="text-[13px] text-muted-foreground">Create and manage custom document templates.</p>
          </div>
          <div className="flex items-center gap-2">
            {/* Page count badge */}
            <span className="inline-flex items-center gap-1.5 text-[12px] text-muted-foreground bg-muted px-2.5 py-1 rounded-md border border-border">
              <span className={`w-1.5 h-1.5 rounded-full ${pageCount > 1 ? 'bg-warning' : 'bg-emerald-500'}`} />
              {pageCount} {pageCount === 1 ? 'page' : 'pages'}
            </span>

            <button
              onClick={() => setPreview(p => !p)}
              className={`inline-flex items-center gap-1.5 h-8 px-3 rounded-md border text-[12px] font-medium transition-colors ${
                preview
                  ? 'bg-primary text-primary-foreground border-primary'
                  : 'bg-card text-muted-foreground border-border hover:bg-accent'
              }`}
            >
              {preview ? <EyeOffIcon /> : <EyeIcon />}
              {preview ? 'Edit' : 'Preview'}
            </button>

            <button onClick={handlePrint}
              className="inline-flex items-center gap-1.5 h-8 px-3 rounded-md bg-primary text-primary-foreground text-[12px] font-medium hover:bg-primary/90 transition-colors">
              <PrinterIcon /> Print / PDF
            </button>

            <button
              onClick={() => docxInputRef.current?.click()}
              disabled={isImportingDocx}
              className="inline-flex items-center gap-1.5 h-8 px-3 rounded-md border border-border bg-card text-foreground text-[12px] font-medium hover:bg-accent transition-colors disabled:opacity-50"
            >
              {isImportingDocx ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Save className="w-3.5 h-3.5" />}
              Import DOCX
            </button>

            <input
              ref={docxInputRef}
              type="file"
              accept=".docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
              className="hidden"
              onChange={handleImportDocx}
            />

            <CustomFormPdfExportButton onClick={handleExportPdf} isLoading={isExporting} formName={formMetadata.name || docName}
              className="h-8 text-[12px] min-w-[90px] border border-border rounded-md bg-card text-foreground hover:bg-accent" />

            <button onClick={() => setShowSaveToDocsDialog(true)}
              className="inline-flex items-center gap-1.5 h-8 px-3 rounded-md border border-border bg-card text-foreground text-[12px] font-medium hover:bg-accent transition-colors">
              <Save className="w-3.5 h-3.5" /> Save to Documents
            </button>
          </div>
        </div>

        {/* Template & doc name row */}
        <div className="flex items-center gap-3 mt-4 pb-4 border-b border-border">
          <div className="flex-1">
            <input value={docName} onChange={e => { setDocName(e.target.value); setFormMetadata(p => ({ ...p, name: e.target.value })); }}
              className="w-full h-8 px-3 rounded-md border border-border bg-card text-[13px] text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
              placeholder="Document Name..." />
          </div>

          <select value={currentTemplateId || 'new'} onChange={e => handleLoadForm(e.target.value)}
            className="h-8 px-2 rounded-md border border-border bg-card text-[12px] text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring">
            <option value="new">+ New Template</option>
            {savedTemplates.map(f => <option key={f.id} value={f.id}>{f.name}</option>)}
          </select>

          <button onClick={handleSaveForm} disabled={isLoading || templatesLoading}
            className="inline-flex items-center gap-1.5 h-8 px-3 rounded-md border border-border bg-card text-[12px] text-foreground hover:bg-accent transition-colors disabled:opacity-50">
            {isLoading ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Save className="w-3.5 h-3.5" />}
            Save
          </button>

          {currentTemplateId && (
            <button onClick={async () => { if (!confirm('Delete this template?')) return; await deleteTemplate(currentTemplateId); resetForm(); toast({ title: 'Deleted', description: 'Template removed.' }); }}
              className="inline-flex items-center gap-1.5 h-8 px-2.5 rounded-md border border-border text-destructive hover:bg-destructive/10 transition-colors">
              <Trash2 className="w-3.5 h-3.5" />
            </button>
          )}
        </div>
      </div>

      {/* Main Content */}
      <div className="flex flex-1 overflow-hidden">

        {/* Left Config Panel */}
        {!preview && (
          <div className="w-[280px] shrink-0 bg-card border-r border-border flex flex-col overflow-hidden">
            {/* Panel Tabs */}
            <div className="flex border-b border-border bg-muted/30">
              {['content', 'header', 'footer', 'global'].map(tab => (
                <button key={tab} onClick={() => setSidePanel(tab)}
                  className={`flex-1 py-2.5 px-1 text-[10px] font-medium capitalize tracking-wide border-b-2 transition-colors ${
                    sidePanel === tab
                      ? 'bg-card text-primary border-primary'
                      : 'text-muted-foreground border-transparent hover:text-foreground'
                  }`}>
                  {tab}
                </button>
              ))}
            </div>

            {/* Panel Content */}
            <div className="flex-1 overflow-y-auto p-3.5">
              {sidePanel === 'content' && (
                <div className="space-y-4 text-xs">
                  {/* Association Selector */}
                  <div className="space-y-2">
                    <span className="font-semibold text-muted-foreground text-xs uppercase tracking-wide">Association / Client</span>
                    <select value={selectedClient} onChange={e => setSelectedClient(e.target.value)}
                      className="w-full h-8 text-xs rounded-md border border-border bg-card px-2 text-foreground focus:outline-none focus:ring-1 focus:ring-ring">
                      <option value="">Select Association</option>
                      {clients.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
                    </select>
                    {!selectedClient && (
                      <div className="text-[10px] text-warning flex items-center gap-1 bg-warning/10 p-2 rounded border border-warning/30">
                        <AlertTriangle className="w-3 h-3" /> Select a client to resolve variables
                      </div>
                    )}
                  </div>

                  {/* Homeowner Selector */}
                  {selectedClient && homeowners.length > 0 && (
                    <HomeownerCombobox homeowners={homeowners} selectedId={selectedHomeownerId} onSelect={setSelectedHomeownerId} />
                  )}

                  {/* Variables */}
                  <div className="pt-2 border-t border-border">
                    <span className="font-semibold text-muted-foreground text-xs uppercase tracking-wide block mb-2">Variables</span>
                    <p className="text-[10px] text-muted-foreground mb-2">Click a variable to insert it at your cursor position.</p>
                    <div className="space-y-1.5">
                      {variableOptions.map(variable => (
                        <button key={variable.key} type="button" onClick={() => insertVariable(variable.key)}
                          className="w-full rounded-md border border-border bg-muted/50 px-2.5 py-2 text-left transition-colors hover:border-primary/30 hover:bg-primary/5">
                          <div className="flex items-center gap-1.5">
                            <span className="font-mono text-[10px] font-semibold text-primary">{`{{${variable.key}}}`}</span>
                            {variable.isCustom && <span className="text-[8px] bg-primary/10 text-primary px-1 py-0.5 rounded font-medium">Custom</span>}
                          </div>
                          <div className="mt-0.5 text-[10px] text-muted-foreground">{variable.label}</div>
                        </button>
                      ))}
                    </div>
                  </div>

                  {/* Border toggle */}
                  <div className="pt-2 border-t border-border space-y-2">
                    <div className="flex items-center justify-between">
                      <span className="font-semibold text-muted-foreground text-xs uppercase tracking-wide">Page Border</span>
                      <div onClick={() => setBorderConfig(p => ({ ...p, enabled: !p.enabled }))}
                        className={cls('w-8 h-4 rounded-full transition-colors cursor-pointer relative', borderConfig.enabled ? 'bg-primary' : 'bg-muted-foreground/30')}>
                        <div className={cls('absolute top-0.5 w-3 h-3 bg-white rounded-full shadow transition-transform', borderConfig.enabled ? 'translate-x-4' : 'translate-x-0.5')} />
                      </div>
                    </div>
                    {borderConfig.enabled && (
                      <div className="flex items-center gap-2">
                        <label className="text-muted-foreground text-[10px]">Color</label>
                        <input type="color" value={borderConfig.color} onChange={e => setBorderConfig(p => ({ ...p, color: e.target.value }))} className="w-7 h-7 rounded border border-border p-0.5 cursor-pointer" />
                      </div>
                    )}
                  </div>
                </div>
              )}
              {sidePanel === 'header' && <HeaderPanel config={headerConfig} onChange={setHeaderConfig} clientLogo={clientLogo} />}
              {sidePanel === 'footer' && <FooterPanel config={footerConfig} onChange={setFooterConfig} />}
              {sidePanel === 'global' && (
                <div className="space-y-3 text-xs">
                  <span className="font-semibold text-muted-foreground text-xs uppercase tracking-wide flex items-center gap-1.5">
                    <Settings className="w-3 h-3" /> Security & Validation
                  </span>
                  <div className="flex items-center justify-between p-2 bg-muted/50 rounded-md border border-border">
                    <div>
                      <div className="text-foreground text-xs">Attach Validation Proof</div>
                      <div className="text-[10px] text-muted-foreground max-w-[180px]">Append a certificate to the PDF.</div>
                    </div>
                    <div onClick={() => setGlobalSettings(p => ({ ...p, attachValidationProof: !p.attachValidationProof }))}
                      className={cls('w-8 h-4 rounded-full transition-colors cursor-pointer relative', globalSettings.attachValidationProof ? 'bg-primary' : 'bg-muted-foreground/30')}>
                      <div className={cls('absolute top-0.5 w-3 h-3 bg-white rounded-full shadow transition-transform', globalSettings.attachValidationProof ? 'translate-x-4' : 'translate-x-0.5')} />
                    </div>
                  </div>
                </div>
              )}
            </div>
          </div>
        )}

        {/* Document Canvas Area */}
        <div className="flex-1 overflow-y-auto overflow-x-auto p-8" style={{ background: 'hsl(var(--muted))' }}>
          {preview ? (
            <div className="flex justify-center">
              <CustomFormPreview formConfig={previewFormConfig} clientId={selectedClient} sampleData={sampleData} mode="preview" />
            </div>
          ) : (
            <div className="flex justify-center">
              <div style={{
                width: PAGE_W_PX,
                minHeight: PAGE_H_PX,
                background: '#fff',
                boxShadow: '0 4px 24px rgba(0,0,0,0.12)',
                position: 'relative',
                display: 'flex',
                flexDirection: 'column',
                outline: borderConfig.enabled ? `1.5px solid ${borderConfig.color || '#000'}` : 'none',
                borderRadius: 4,
              }}>
                {/* Content area */}
                <div style={contentAreaStyle}>
                  <CanvasHeader config={headerConfig} clientLogo={clientLogo} />
                  {!selectedClient && (
                    <div className="flex gap-2 p-3 rounded-lg border my-2 bg-warning/10 border-warning/30 text-foreground">
                      <AlertTriangle className="w-4 h-4 mt-0.5 shrink-0 text-warning" />
                      <div>
                        <p className="text-xs font-semibold">No Client Selected</p>
                        <p className="text-xs mt-0.5 text-muted-foreground">Select a client from the sidebar to resolve variables.</p>
                      </div>
                    </div>
                  )}
                  <RichTextEditor
                    ref={editorRef}
                    value={richContent}
                    onChange={setRichContent}
                    placeholder="Start typing your document content here..."
                    minHeight={Math.max(300, PAGE_H_PX - MARGIN_PX * 2 - 120)}
                  />
                </div>

                {/* Sticky footer strip */}
                {footerConfig?.enabled !== false && (
                  <div style={{
                    position: 'sticky', bottom: 0, background: '#fff',
                    paddingLeft: MARGIN_PX, paddingRight: MARGIN_PX, paddingBottom: 8, zIndex: 5,
                  }}>
                    <CanvasFooter config={footerConfig} logoUrl={headerConfig?.logoUrl || clientLogo} pageCount={pageCount} companyName={companyName} />
                  </div>
                )}

                {/* Page-overflow indicators */}
                {Array.from({ length: pageCount - 1 }, (_, i) => (
                  <div key={i} style={{
                    position: 'absolute', top: PAGE_H_PX * (i + 1), left: 0, right: 0, height: 2,
                    background: 'repeating-linear-gradient(90deg, hsl(var(--muted-foreground)) 0, hsl(var(--muted-foreground)) 6px, transparent 6px, transparent 12px)',
                    zIndex: 10, pointerEvents: 'none',
                  }} />
                ))}
                {Array.from({ length: pageCount - 1 }, (_, i) => (
                  <div key={`label-${i}`} style={{
                    position: 'absolute', top: PAGE_H_PX * (i + 1) + 4, right: 12,
                    fontSize: 10, color: 'hsl(var(--muted-foreground))', zIndex: 11, pointerEvents: 'none',
                    background: '#fff', padding: '1px 4px', borderRadius: 3, border: '1px solid hsl(var(--border))',
                  }}>
                    Page {i + 2}
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      </div>

      {/* Hidden PDF engine */}
      <CustomFormPdfExport ref={exportRef} formConfig={previewFormConfig} sampleData={sampleData} borderSettings={borderConfig} />

      <SaveToDocumentsDialog
        open={showSaveToDocsDialog}
        onOpenChange={setShowSaveToDocsDialog}
        generatePdfBlob={async () => {
          if (exportRef.current) return await exportRef.current.generatePdf(formMetadata.name || docName || 'Form', true, true);
          return null;
        }}
        defaultTitle={formMetadata.name || docName || "Custom Form"}
        associationId={selectedClient}
        associations={clients}
      />
    </div>
  );
}

export default CustomFormBuilder;
