import { useState, useEffect, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import DocPreviewPanel from '../user/DocPreviewPanel';
import { IconEdit, IconCheck, IconPlus, IconDeviceFloppy, IconX, IconArrowsMaximize, IconArrowsMinimize, IconTrash, IconDeviceFloppy as IconSave, IconZoomIn, IconZoomOut, IconZoomReset, IconEye, IconEyeOff, IconGripVertical, IconSeparator, IconChevronDown, IconCircleX, IconRefresh, IconWand } from '@tabler/icons-react';
import {
    DndContext, closestCenter, PointerSensor, useSensor, useSensors,
} from '@dnd-kit/core';
import {
    SortableContext, verticalListSortingStrategy, useSortable, arrayMove,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '../ui/sheet';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../ui/dialog';
import ConfirmDialog from '../common/ConfirmDialog';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';

// Bengali character → romanized phonetic map (mirrors backend transliterateBengali)
const BN_CONSONANTS = {
    'ক':'k','খ':'kh','গ':'g','ঘ':'gh','ঙ':'ng',
    'চ':'ch','ছ':'chh','জ':'j','ঝ':'jh','ঞ':'n',
    'ট':'t','ঠ':'th','ড':'d','ঢ':'dh','ণ':'n',
    'ত':'t','থ':'th','দ':'d','ধ':'dh','ন':'n',
    'প':'p','ফ':'ph','ব':'b','ভ':'bh','ম':'m',
    'য':'y','র':'r','ল':'l','শ':'sh','ষ':'sh',
    'স':'s','হ':'h','ড়':'r','ঢ়':'rh','য়':'y','ৎ':'t',
};
const BN_INDEPENDENT_VOWELS = {
    'অ':'o','আ':'a','ই':'i','ঈ':'i','উ':'u','ঊ':'u',
    'ঋ':'ri','এ':'e','ঐ':'oi','ও':'o','ঔ':'ou',
};
const BN_VOWEL_SIGNS = {
    '\u09BE':'a','\u09BF':'i','\u09C0':'i','\u09C1':'u','\u09C2':'u',
    '\u09C3':'ri','\u09C7':'e','\u09C8':'oi','\u09CB':'o','\u09CC':'ou',
};
const BN_MISC = { 'ং':'ng','ঃ':'h','ঁ':'n' };
const BN_DIGITS = { '০':'0','১':'1','২':'2','৩':'3','৪':'4','৫':'5','৬':'6','৭':'7','৮':'8','৯':'9' };
const HASANTA = '\u09CD';
const NUKTA   = '\u09BC';

const transliterateBengali = (text) => {
    const src = text.toString()
        .replace(/\u09A1\u09BC/g, 'ড়')
        .replace(/\u09A2\u09BC/g, 'ঢ়')
        .replace(/\u09AF\u09BC/g, 'য়');
    const chars = [...src];
    let out = '';
    for (let i = 0; i < chars.length; i++) {
        const ch = chars[i];
        const next = chars[i + 1];
        if (ch === HASANTA || ch === NUKTA) continue;
        if (BN_CONSONANTS[ch] !== undefined) {
            out += BN_CONSONANTS[ch];
            if (next && next !== HASANTA && !BN_VOWEL_SIGNS[next] && next !== ' ') out += 'a';
        } else if (BN_INDEPENDENT_VOWELS[ch] !== undefined) {
            out += BN_INDEPENDENT_VOWELS[ch];
        } else if (BN_VOWEL_SIGNS[ch] !== undefined) {
            out += BN_VOWEL_SIGNS[ch];
        } else if (BN_MISC[ch] !== undefined) {
            out += BN_MISC[ch];
        } else if (BN_DIGITS[ch] !== undefined) {
            out += BN_DIGITS[ch];
        } else {
            out += ch;
        }
    }
    return out;
};

const slugifyEn = (text) => {
    const input = text.toString().trim();
    const hasBengali = /[\u0980-\u09FF]/.test(input);
    const processed  = hasBengali ? transliterateBengali(input) : input;
    return processed
        .toLowerCase()
        .trim()
        .replace(/\s+/g, '-')
        .replace(/[^a-z0-9\-_]+/g, '')
        .replace(/\-\-+/g, '-')
        .replace(/^-+/, '')
        .replace(/-+$/, '');
};

const PLACEHOLDER_TYPES = [
    { value: 'Text',        label: 'Text' },
    { value: 'Textarea',    label: 'Textarea (Multiline)' },
    { value: 'Number',      label: 'Number' },
    { value: 'Email',       label: 'Email' },
    { value: 'Phone',       label: 'Phone' },
    { value: 'Date',        label: 'Date' },
    { value: 'Select',      label: 'Select (Dropdown)' },
    { value: 'YesNo',       label: 'Yes / No' },
    { value: 'CustomToggle',label: 'Custom Toggle' },
    { value: 'TableBuilder',label: 'Table Builder' },
    { value: 'BulletList',  label: 'Bullet List' },
];

const TYPES_NEEDING_OPTIONS = new Set(['Select', 'CustomToggle']);

// Human-readable "Saved · Xs ago" helper — shared with DocumentEditorPage conceptually.
const formatSavedAgo = (savedAt) => {
    if (!savedAt) return '';
    const sec = Math.max(0, Math.round((Date.now() - savedAt) / 1000));
    if (sec < 3) return 'just now';
    if (sec < 60) return `${sec}s ago`;
    const min = Math.round(sec / 60);
    if (min < 60) return `${min}m ago`;
    const hr = Math.round(min / 60);
    return `${hr}h ago`;
};
import LexicalDocEditor from '../common/LexicalDocEditor';
import Form, { Field, useForm } from 'rc-field-form';
import { notifications } from '../../lib/notifications';
import { useTranslation } from '../../i18n/hooks/useTranslation';
import templateSetService from '../../services/templateSetService';
import tagService from '../../services/tagService';
import categoryService from '../../services/categoryService';
import TooltipButton from '../common/TooltipButton';
import SearchableSelect from '../common/SearchableSelect';
import MultiSelect from '../common/MultiSelect';
import { Switch } from '../ui/switch';
import styles from './TemplateSetFormDrawer.module.scss';

// LocalStorage utility functions for all form data (Step 1 + Step 2)
const FORM_DRAFT_KEY = 'templateSetFormDraft';
const EDIT_DRAFT_KEY_PREFIX = 'templateSetEditDraft_';

// Get the appropriate storage key based on whether editing or creating
const getDraftKey = (templateSetId) => {
    return templateSetId ? `${EDIT_DRAFT_KEY_PREFIX}${templateSetId}` : FORM_DRAFT_KEY;
};

const saveFormDraft = (formData, templateSetId = null) => {
    try {
        const draftData = {
            ...formData,
            timestamp: new Date().toISOString(),
        };
        const key = getDraftKey(templateSetId);
        localStorage.setItem(key, JSON.stringify(draftData));
        return true;
    } catch (error) {
        console.error('Failed to save form draft:', error);
        return false;
    }
};

const loadFormDraft = (templateSetId = null) => {
    try {
        const key = getDraftKey(templateSetId);
        const saved = localStorage.getItem(key);
        if (!saved) return null;

        const data = JSON.parse(saved);
        // Validate data structure
        if (data && typeof data === 'object') {
            return data;
        }
        return null;
    } catch (error) {
        console.error('Failed to load form draft:', error);
        return null;
    }
};

const clearFormDraft = (templateSetId = null) => {
    try {
        const key = getDraftKey(templateSetId);
        localStorage.removeItem(key);
        return true;
    } catch (error) {
        console.error('Failed to clear form draft:', error);
        return false;
    }
};

const R2_URL = import.meta.env.VITE_R2_PUBLIC_URL || '';

// Returns true when the Lexical JSON represents a blank document (no real content).
function isLexicalContentEmpty(json) {
    if (!json) return true;
    try {
        const parsed = JSON.parse(json);
        const children = parsed?.root?.children;
        if (!Array.isArray(children) || children.length === 0) return true;
        return children.every(node => !Array.isArray(node.children) || node.children.length === 0);
    } catch {
        return true;
    }
}

// ── Sortable placeholder row (used inside DnD context in step 3) ──────────────
function SortablePlaceholderItem({ item, saving, onUpdate, onSave, onDelete }) {
    const [expanded, setExpanded] = useState(false);

    const { attributes, listeners, setNodeRef, transform, transition, isDragging } =
        useSortable({ id: item.slug });

    const wrapStyle = {
        transform: CSS.Transform.toString(transform),
        transition,
        opacity: isDragging ? 0.4 : 1,
        position: 'relative',
        zIndex: isDragging ? 999 : 'auto',
    };

    // Drag handle props — stop all event propagation so the handle doesn't
    // interfere with expand/collapse and vice-versa.
    const gripProps = {
        ...attributes,
        ...listeners,
        onPointerDown: (e) => {
            e.stopPropagation();
            listeners?.onPointerDown?.(e);
        },
        onClick: (e) => e.stopPropagation(),
        style: { cursor: isDragging ? 'grabbing' : 'grab', touchAction: 'none', flexShrink: 0, display: 'flex', alignItems: 'center', padding: '2px 4px', borderRadius: 4 },
    };

    // ── Step Break row ────────────────────────────────────────────────────────
    if (item.type === 'Steps') {
        return (
            <div ref={setNodeRef} style={wrapStyle}>
                <div className="rounded-xl border border-dashed border-purple-400 bg-purple-50 dark:bg-purple-950/20 p-2">
                    <div className="flex items-center gap-2 flex-nowrap">
                        <div {...gripProps}>
                            <IconGripVertical size={14} className="text-purple-500" />
                        </div>
                        <IconSeparator size={13} className="text-purple-500 shrink-0" />
                        <input
                            type="text"
                            placeholder="Step title shown to user"
                            className="flex-1 min-w-0 px-2 py-1 text-xs rounded border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300"
                            value={item.title}
                            onChange={e => onUpdate('title', e.target.value)}
                        />
                        <button
                            title="Save step title"
                            disabled={saving}
                            onClick={onSave}
                            className="p-1 rounded text-purple-600 hover:bg-purple-100 dark:hover:bg-purple-900/30 disabled:opacity-50"
                        >
                            {saving
                                ? <div className="w-3 h-3 border-2 border-purple-500 border-t-transparent rounded-full animate-spin" />
                                : <IconDeviceFloppy size={13} />
                            }
                        </button>
                        <button
                            title="Delete step break"
                            onClick={onDelete}
                            className="p-1 rounded text-red-500 hover:bg-red-50 dark:hover:bg-red-950/20"
                        >
                            <IconCircleX size={14} />
                        </button>
                    </div>
                </div>
            </div>
        );
    }

    // ── Regular placeholder row (custom collapsible) ────────────────────────
    return (
        <div ref={setNodeRef} style={wrapStyle}>
            <div className="rounded-xl border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 overflow-hidden">
                {/* Header — clicking anywhere except the grip toggles expand */}
                <div
                    className="flex items-center gap-2 px-2 py-1.5 cursor-pointer select-none flex-nowrap"
                    onClick={() => setExpanded(v => !v)}
                >
                    <div {...gripProps}>
                        <IconGripVertical size={14} className="text-zinc-400" />
                    </div>
                    <code className="font-mono text-xs bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 rounded shrink-0">{`{{${item.slug}}}`}</code>
                    {item.title && (
                        <span className="text-xs text-zinc-400 flex-1 overflow-hidden text-ellipsis whitespace-nowrap min-w-0">
                            {item.title}
                        </span>
                    )}
                    <IconChevronDown
                        size={14}
                        className="text-zinc-400 shrink-0 ml-auto transition-transform duration-150"
                        style={{ transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)' }}
                    />
                </div>

                {/* Collapsible field panel */}
                {expanded && (
                    <div className="flex flex-col gap-2 px-3 pb-3 pt-1 border-t border-zinc-200 dark:border-zinc-700">
                        <div className="flex flex-col gap-1">
                            <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">Label</label>
                            <input
                                type="text"
                                placeholder={item.slug}
                                className="w-full px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300"
                                value={item.title}
                                onChange={e => onUpdate('title', e.target.value)}
                            />
                        </div>
                        <div className="flex flex-col gap-1">
                            <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">Type</label>
                            <SearchableSelect
                                value={item.type || 'Text'}
                                onChange={val => onUpdate('type', val)}
                                options={PLACEHOLDER_TYPES}
                                placeholder="Select type"
                                searchPlaceholder="Search type…"
                            />
                        </div>
                        {TYPES_NEEDING_OPTIONS.has(item.type) && (
                            <div className="flex flex-col gap-1">
                                <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">Options</label>
                                <input
                                    type="text"
                                    placeholder="Option 1, Option 2, Option 3"
                                    className="w-full px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300"
                                    value={item.options}
                                    onChange={e => onUpdate('options', e.target.value)}
                                />
                                <p className="text-xs text-zinc-400">
                                    {item.type === 'CustomToggle'
                                        ? 'Comma-separated toggle values. User picks one; the chosen label is inserted into the document.'
                                        : 'Comma-separated list of dropdown options'}
                                </p>
                            </div>
                        )}
                        <div className="flex flex-col gap-1">
                            <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">Description</label>
                            <textarea
                                placeholder="Hint shown to the user filling this field"
                                rows={2}
                                className="w-full px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300 resize-none"
                                value={item.description}
                                onChange={e => onUpdate('description', e.target.value)}
                            />
                        </div>
                        <div className="flex items-center justify-between">
                            <button
                                title="Delete placeholder"
                                onClick={onDelete}
                                className="p-1.5 rounded text-red-500 hover:bg-red-50 dark:hover:bg-red-950/20"
                            >
                                <IconTrash size={14} />
                            </button>
                            <button
                                title="Save"
                                disabled={saving}
                                onClick={onSave}
                                className="p-1.5 rounded text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-950/20 disabled:opacity-50"
                            >
                                {saving
                                    ? <div className="w-3.5 h-3.5 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
                                    : <IconDeviceFloppy size={14} />
                                }
                            </button>
                        </div>
                    </div>
                )}
            </div>
        </div>
    );
}

export default function TemplateSetFormDrawer({ opened, onClose, onEditorOpen, templateSet = null, onSuccess }) {
    const { t } = useTranslation();
    const navigate = useNavigate();
    const [activeStep, setActiveStep] = useState(0);
    const [loading, setLoading] = useState(false);
    const [availableTags, setAvailableTags] = useState([]);
    const [availableCategories, setAvailableCategories] = useState([]);
    const [categoriesLoaded, setCategoriesLoaded] = useState(false);
    const [savedTemplateSet, setSavedTemplateSet] = useState(null); // Store saved template for step 4
    const [docs, setDocs] = useState([]);
    const [draftSaved, setDraftSaved] = useState(false); // Track if draft is saved
    const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); // Track if form has unsaved changes
    const [form] = useForm();
    const saveTimeoutRef = useRef(null);
    const initialFormValuesRef = useRef(null);
    const lexicalJsonRef = useRef(null);
    const docAddHandlerRef = useRef(null);
    const contentKeyCacheRef = useRef({});
    const [editorInitialState, setEditorInitialState] = useState(null);
    const [editorLoading, setEditorLoading] = useState(false);
    const [editorExpanded, setEditorExpanded] = useState(false);
    const [activeEditorDoc, setActiveEditorDoc] = useState(null);
    const [editorKey, setEditorKey] = useState(0);
    const [editorSaving, setEditorSaving] = useState(false);
    // Doc-content dirty tracking + auto-save status (mirrors DocumentEditorPage)
    const [editorContentDirty, setEditorContentDirty] = useState(false);
    const editorContentDirtyRef = useRef(false);
    // First OnChangePlugin fire after mount is Lexical echoing the initial state
    // back — that's not a user edit, so we skip it.
    const editorInitialChangeConsumedRef = useRef(false);
    const [editorLastSavedAt, setEditorLastSavedAt] = useState(null);
    const [, setEditorNowTick] = useState(0); // tick for the "Saved · Xs ago" label
    const [editorPreviewOpen, setEditorPreviewOpen] = useState(false);
    const [editorPreviewData, setEditorPreviewData] = useState(null);
    const [editorPreviewLoading, setEditorPreviewLoading] = useState(false);
    // Page settings go into TemplateDoc.metadata alongside the Lexical JSON so
    // PDF export / preview render at the correct size + margins.
    const [editorPageSettings, setEditorPageSettings] = useState(null);
    const editorPageSettingsRef = useRef(null);
    // Ordered list: each item is { slug, title, type, description, options }
    const [placeholderList, setPlaceholderList] = useState([]);
    const [placeholderSaving, setPlaceholderSaving] = useState({});
    const [reordering, setReordering] = useState(false);
    const [editorZoom, setEditorZoom] = useState(1);
    const [editorShowHighlight, setEditorShowHighlight] = useState(true);
    // Tag input state
    const [tagInputValue, setTagInputValue] = useState('');

    // Confirmation dialogs (replaces window.confirm)
    const [confirmDialog, setConfirmDialog] = useState({ open: false, title: '', message: '', onConfirm: null, onCancel: null, variant: 'primary', confirmLabel: '' });

    const openConfirm = ({ title, message, onConfirm, onCancel, variant = 'primary', confirmLabel = '' }) =>
        setConfirmDialog({ open: true, title, message, onConfirm, onCancel, variant, confirmLabel });
    const closeConfirm = () => setConfirmDialog(d => ({ ...d, open: false }));

    const dndSensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));

    const steps = [
        { label: 'Template Info' },
        { label: 'Documents' },
        { label: 'Placeholders' },
    ];

    // Load available tags and categories
    useEffect(() => {
        if (opened) {
            // Load tags from API
            loadTags();
            // Load categories from API
            loadCategories();
        }
    }, [opened]);

    const loadTags = async () => {
        try {
            // /api/tags is paginated — fetch a high limit so the searchable
            // multi-select sees every tag (search is client-side).
            const response = await tagService.getAllTags({ limit: 1000 });
            setAvailableTags((response.data || []).map(tag => ({
                value: tag.id.toString(),
                label: tag.name
            })));
        } catch (error) {
            console.error('Failed to load tags:', error);
            // Fallback to empty array
            setAvailableTags([]);
        }
    };

    const loadCategories = async () => {
        setCategoriesLoaded(false);
        try {
            // /api/categories is paginated — fetch a high limit so the
            // searchable category select sees every category.
            const response = await categoryService.getAllCategories({ limit: 1000 });
            const cats = (response.data || []).map(cat => ({
                value: cat.id.toString(),
                label: cat.name
            }));
            setAvailableCategories(cats);
            // Auto-select first category for new template sets
            if (!templateSet && cats.length > 0) {
                const currentCat = form.getFieldValue('categoryId');
                if (!currentCat) {
                    form.setFieldsValue({ categoryId: cats[0].value });
                }
            }
        } catch (error) {
            console.error('Failed to load categories:', error);
        } finally {
            setCategoriesLoaded(true);
        }
    };

    // Reset form when drawer opens/closes and check for draft
    useEffect(() => {
        if (opened) {
            // Reset unsaved changes flag when drawer opens
            setHasUnsavedChanges(false);

            // Use setTimeout to ensure Form component is mounted before setting values
            const timer = setTimeout(() => {
                // Check for draft (both for creating new and editing)
                const draft = loadFormDraft(templateSet?.id);

                if (draft && draft.timestamp) {
                    const draftDate = new Date(draft.timestamp).toLocaleString();
                    openConfirm({
                        title: templateSet ? t('common.restoreChangesTitle') : t('common.restoreDataTitle'),
                        message: templateSet
                            ? t('common.restoreChangesDescription').replace('{date}', draftDate)
                            : t('common.restoreDataDescription').replace('{date}', draftDate),
                        variant: 'primary',
                        confirmLabel: t('common.restore'),
                        onConfirm: () => {
                            form.setFieldsValue({
                                name: draft.name || '',
                                slug: draft.slug || '',
                                description: draft.description || '',
                                categoryId: draft.categoryId || '',
                                language: draft.language || 'bangla',
                                tagIds: draft.tagIds || [],
                                status: draft.status || 'published',
                                isFree: draft.isFree === true,
                                searchKeys: Array.isArray(draft.searchKeys) ? draft.searchKeys : []
                            });
                            setDocs(draft.docs || []);
                            initialFormValuesRef.current = JSON.stringify({
                                ...form.getFieldsValue(true),
                                docs: draft.docs || []
                            });
                            if (templateSet) setSavedTemplateSet(templateSet);
                            notifications.show({
                                title: t('common.draftRestored') || 'Draft Restored',
                                message: t('common.draftRestoredMessage') || 'Your previous form data has been restored',
                                color: 'green'
                            });
                        },
                        onCancel: () => clearFormDraft(templateSet?.id),
                    });
                    return; // wait for user choice via dialog
                }

                if (templateSet) {
                    form.setFieldsValue({
                        name: templateSet.name || '',
                        slug: templateSet.slug || '',
                        description: templateSet.description || '',
                        categoryId: (templateSet.category?.id ?? templateSet.categoryId ?? templateSet.category_id)?.toString() || '',
                        language: templateSet.language || 'english',
                        tagIds: templateSet.tags?.map(t => t.id.toString()) || [],
                        status: templateSet.status === true ? 'published' : 'draft',
                        isFree: templateSet.isFree === true,
                        searchKeys: Array.isArray(templateSet.searchKeys) ? templateSet.searchKeys : []
                    });
                    setDocs(templateSet.docs || []);
                    setSavedTemplateSet(templateSet);
                    setTimeout(() => {
                        initialFormValuesRef.current = JSON.stringify({
                            ...form.getFieldsValue(true),
                            docs: templateSet.docs || []
                        });
                    }, 50);
                } else {
                    form.setFieldsValue({
                        name: '',
                        description: '',
                        categoryId: '',
                        language: 'bangla',
                        tagIds: [],
                        status: 'published',
                        isFree: false,
                        searchKeys: []
                    });
                    setDocs([]);
                    setActiveStep(0);
                    setSavedTemplateSet(null);
                    // Store initial values for comparison
                    setTimeout(() => {
                        initialFormValuesRef.current = JSON.stringify({
                            ...form.getFieldsValue(true),
                            docs: []
                        });
                    }, 50);
                }
            }, 0);

            return () => clearTimeout(timer);
        } else {
            // Drawer closed - reset state
            setActiveStep(0);
            setSavedTemplateSet(null);
            setDocs([]);
            setEditorInitialState(null);
            setEditorLoading(false);
            setActiveEditorDoc(null);
            setEditorExpanded(false);
            lexicalJsonRef.current = null;
            contentKeyCacheRef.current = {};
        }
    }, [opened, templateSet, form]);

    // Autofocus first field (template set name) when drawer opens on step 0
    useEffect(() => {
        if (!opened || activeStep !== 0) return;

        const focusName = () => {
            const field = document.querySelector('input[name="templateSetName"]');
            if (field) {
                field.focus();
                if (field.select) field.select();
                return true;
            }
            return false;
        };

        const id1 = setTimeout(() => { focusName(); }, 120);
        const id2 = setTimeout(() => { focusName(); }, 400);

        return () => {
            clearTimeout(id1);
            clearTimeout(id2);
        };
    }, [opened, activeStep]);


    // Auto-save function with debouncing - saves all form data (both create and edit modes)
    const handleFormAutoSave = () => {
        // Only auto-save when drawer is opened
        if (!opened) return;

        // Clear existing timeout
        if (saveTimeoutRef.current) {
            clearTimeout(saveTimeoutRef.current);
        }

        // Set new timeout to save after 1 second of inactivity
        saveTimeoutRef.current = setTimeout(() => {
            try {
                // Get all form values
                const formValues = form.getFieldsValue(true);
                const currentFormValues = {
                    ...formValues,
                    docs
                };

                // Check if values have changed from initial
                const currentValues = JSON.stringify(currentFormValues);
                const hasChanges = initialFormValuesRef.current && currentValues !== initialFormValuesRef.current;

                // Update unsaved changes flag
                setHasUnsavedChanges(hasChanges);

                const hasContent = formValues.name && formValues.name.trim();

                if (hasContent && hasChanges) {
                    const saved = saveFormDraft(currentFormValues, templateSet?.id);
                    if (saved) {
                        setDraftSaved(true);
                        // Hide the indicator after 2 seconds
                        setTimeout(() => setDraftSaved(false), 2000);
                    }
                }
            } catch (error) {
                console.debug('Auto-save error:', error);
            }
        }, 1000);
    };

    // Cleanup timeout on unmount
    useEffect(() => {
        return () => {
            if (saveTimeoutRef.current) {
                clearTimeout(saveTimeoutRef.current);
            }
        };
    }, []);

    // Load editor content when activeEditorDoc changes
    useEffect(() => {
        if (!activeEditorDoc) return;
        const load = async () => {
            setEditorLoading(true);
            setEditorInitialState(null);
            lexicalJsonRef.current = null;
            // Reset dirty + initial-change tracking so the new doc starts clean.
            editorContentDirtyRef.current = false;
            editorInitialChangeConsumedRef.current = false;
            setEditorContentDirty(false);
            setEditorLastSavedAt(null);
            // Seed page settings from the doc's metadata (or defaults).
            const md = activeEditorDoc.metadata || {};
            const seeded = {
                size: md.pageSize || 'A4',
                marginTop:    typeof md.marginTop    === 'number' ? md.marginTop    : 96,
                marginBottom: typeof md.marginBottom === 'number' ? md.marginBottom : 96,
                marginLeft:   typeof md.marginLeft   === 'number' ? md.marginLeft   : 72,
                marginRight:  typeof md.marginRight  === 'number' ? md.marginRight  : 72,
            };
            editorPageSettingsRef.current = seeded;
            setEditorPageSettings(seeded);
            try {
                // Use cached contentKey if available
                const doc = activeEditorDoc;
                const cachedKey = contentKeyCacheRef.current[doc.id];
                const contentKey = cachedKey || doc.contentKey;
                if (contentKey) {
                    try {
                        const r2Res = await fetch(`${R2_URL}/${contentKey}?t=${Date.now()}`);
                        if (r2Res.ok) {
                            const text = await r2Res.text();
                            try {
                                JSON.parse(text); // validate it's real JSON
                                setEditorInitialState(text);
                                setEditorKey(k => k + 1);
                                return;
                            } catch {
                                console.warn('R2 response is not valid JSON, ignoring');
                            }
                        }
                    } catch (e) {
                        console.warn('R2 fetch failed, trying legacy', e);
                    }
                }
                // Legacy fallback — only use if it's valid Lexical JSON
                const legacy = doc.content || doc.body;
                if (legacy) {
                    try {
                        const parsed = typeof legacy === 'string' ? JSON.parse(legacy) : legacy;
                        if (parsed?.root) {
                            setEditorInitialState(JSON.stringify(parsed));
                        }
                    } catch {
                        // plain text description, not a Lexical state — start with empty editor
                    }
                }
                setEditorKey(k => k + 1);
            } finally {
                setEditorLoading(false);
            }
        };
        load();
    }, [activeEditorDoc]);

    // Shared payload builder
    const buildPayload = (values, docList) => {
        const docsPayload = docList.filter(d => d.title && d.title.trim().length > 0).map((d, i) => ({
            id: d.id || undefined,
            title: d.title,
            content: d.content || '',
            fileUrl: d.fileUrl || null,
            metadata: d.metadata || null,
            contentKey: d.contentKey || null,
            order: i + 1,
        }));
        return {
            name: values.name,
            slug: values.slug || undefined,
            description: values.description || '',
            categoryId: parseInt(values.categoryId),
            language: values.language,
            tagIds: (values.tagIds || []).map(id => parseInt(id)),
            status: values.status === 'published',
            isFree: values.isFree === true,
            docs: docsPayload,
            searchKeys: Array.isArray(values.searchKeys) ? values.searchKeys : [],
        };
    };

    // Save template set to API and sync state — used by both Step 0 Next and Step 1 Next
    const saveTemplateSetToApi = async () => {
        const values = form.getFieldsValue(true);
        const payload = buildPayload(values, docs);
        const existingId = savedTemplateSet?.id || templateSet?.id;
        let saved;
        if (existingId) {
            saved = await templateSetService.updateTemplateSet(existingId, payload);
        } else {
            payload.placeholders = [];
            saved = await templateSetService.createTemplateSet(payload);
        }
        setSavedTemplateSet(saved.data);
        setDocs(saved.data?.docs || []);
        clearFormDraft(templateSet?.id);
        setHasUnsavedChanges(false);
        return saved.data;
    };

    // Ensure all docs have backend IDs (save if any are missing). Returns saved docs array.
    const ensureDocsSaved = async () => {
        const needsSave = docs.some(d => !d.id);
        if (!needsSave && savedTemplateSet?.docs?.length === docs.length) {
            return savedTemplateSet.docs;
        }
        const saved = await saveTemplateSetToApi();
        return saved.docs || [];
    };

    // Extract placeholder slugs from a lexical JSON tree
    const extractPlaceholderSlugs = (lexicalJson) => {
        const slugs = [];
        const walk = (node) => {
            if (node?.type === 'placeholder' && node.text) {
                const slug = node.text.replace(/^\{\{|\}\}$/g, '').trim();
                if (slug && !slugs.includes(slug)) slugs.push(slug);
            }
            if (node?.children) node.children.forEach(walk);
        };
        if (lexicalJson?.root) walk(lexicalJson.root);
        return slugs;
    };

    // Load/refresh placeholders into ordered list — returns the loaded list
    const loadPlaceholders = async () => {
        if (!savedTemplateSet?.id) return [];
        try {
            const res = await templateSetService.getTemplateSetById(savedTemplateSet.id);
            const fresh = res.data;
            setSavedTemplateSet(fresh);
            const list = (fresh.placeholders || [])
                .sort((a, b) => a.order - b.order)
                .map(p => ({
                    slug: p.slug,
                    title: p.title && p.title !== p.slug ? p.title : '',
                    type: p.type || 'Text',
                    description: p.description || '',
                    options: p.options || '',
                }));
            setPlaceholderList(list);
            return list;
        } catch (err) {
            console.error('loadPlaceholders error', err);
            return [];
        }
    };

    // When entering step 3: load placeholders
    useEffect(() => {
        if (activeStep === 2 && opened) {
            loadPlaceholders();
        }
    }, [activeStep, opened]);

    const handlePlaceholderSave = async (slug) => {
        const item = placeholderList.find(p => p.slug === slug);
        if (!item) return;
        setPlaceholderSaving(prev => ({ ...prev, [slug]: true }));
        try {
            await templateSetService.updatePlaceholderMetadata(savedTemplateSet.id, slug, {
                title: item.title || slug,
                type: item.type || 'Text',
                description: item.description || '',
                options: item.options || '',
            });
            notifications.show({ title: 'Saved', color: 'green', icon: <IconCheck size={14} />, autoClose: 1500 });
        } catch (err) {
            notifications.show({ title: 'Save failed', message: err.message, color: 'red' });
        } finally {
            setPlaceholderSaving(prev => ({ ...prev, [slug]: false }));
        }
    };

    const handleAddStepBreak = async () => {
        const stepCount = placeholderList.filter(p => p.type === 'Steps').length + 1;
        const slug = `step-${stepCount}-${Date.now()}`;
        const newItem = { slug, title: `Step ${stepCount}`, type: 'Steps', description: '', options: '' };
        const newOrder = placeholderList.length + 1;
        setPlaceholderSaving(prev => ({ ...prev, [slug]: true }));
        try {
            await templateSetService.createPlaceholder(savedTemplateSet.id, {
                slug, title: newItem.title, type: 'Steps', description: '', options: '', order: newOrder,
            });
            setPlaceholderList(prev => [...prev, newItem]);
            notifications.show({ title: 'Step break added', color: 'blue', autoClose: 1500 });
        } catch (err) {
            notifications.show({ title: 'Failed to add step', message: err.message, color: 'red' });
        } finally {
            setPlaceholderSaving(prev => ({ ...prev, [slug]: false }));
        }
    };

    const handleDeletePlaceholder = async (slug) => {
        setPlaceholderSaving(prev => ({ ...prev, [slug]: true }));
        try {
            await templateSetService.deletePlaceholder(savedTemplateSet.id, slug);
            setPlaceholderList(prev => prev.filter(p => p.slug !== slug));
            notifications.show({ title: 'Placeholder removed', color: 'orange', autoClose: 1500 });
        } catch (err) {
            notifications.show({ title: 'Failed to delete', message: err.message, color: 'red' });
        } finally {
            setPlaceholderSaving(prev => ({ ...prev, [slug]: false }));
        }
    };

    const handleDragEnd = async (event) => {
        const { active, over } = event;
        if (!over || active.id === over.id) return;

        setPlaceholderList(prev => {
            const oldIndex = prev.findIndex(p => p.slug === active.id);
            const newIndex = prev.findIndex(p => p.slug === over.id);
            return arrayMove(prev, oldIndex, newIndex);
        });
    };

    // Persist order to backend after drag
    const handleDragEndPersist = async (newList) => {
        if (!savedTemplateSet?.id) return;
        setReordering(true);
        try {
            await templateSetService.reorderPlaceholders(
                savedTemplateSet.id,
                newList.map((p, i) => ({ slug: p.slug, order: i + 1 }))
            );
        } catch (err) {
            notifications.show({ title: 'Reorder failed', message: err.message, color: 'red' });
        } finally {
            setReordering(false);
        }
    };

    const doEditorSave = async ({ silent = true } = {}) => {
        // Nothing to persist? Skip silently so auto-save ticks stay cheap.
        if (!activeEditorDoc?.id || !lexicalJsonRef.current || !editorContentDirtyRef.current) {
            return { ok: true, skipped: true };
        }
        // Safety guard: never overwrite R2 with a blank/unloaded Lexical state.
        if (isLexicalContentEmpty(lexicalJsonRef.current)) {
            console.warn('doEditorSave: skipping R2 write — Lexical content not loaded or blank');
            editorContentDirtyRef.current = false;
            setEditorContentDirty(false);
            return { ok: true, skipped: true };
        }
        try {
            setEditorSaving(true);
            const ps = editorPageSettingsRef.current;
            const metadata = ps ? {
                pageSize:     ps.size,
                marginTop:    ps.marginTop,
                marginBottom: ps.marginBottom,
                marginLeft:   ps.marginLeft,
                marginRight:  ps.marginRight,
            } : undefined;
            const res = await templateSetService.updateTemplateDocContent(activeEditorDoc.id, {
                lexicalJson: lexicalJsonRef.current,
                ...(metadata ? { metadata } : {}),
            });
            editorContentDirtyRef.current = false;
            setEditorContentDirty(false);
            setEditorLastSavedAt(Date.now());
            if (res?.data?.contentKey) {
                contentKeyCacheRef.current[activeEditorDoc.id] = res.data.contentKey;
                setSavedTemplateSet(prev => {
                    if (!prev) return prev;
                    return {
                        ...prev,
                        docs: (prev.docs || []).map(d =>
                            d.id === activeEditorDoc.id ? { ...d, contentKey: res.data.contentKey } : d
                        )
                    };
                });
            }
            if (!silent) {
                notifications.show({ title: 'Saved', color: 'green', icon: <IconCheck size={16} />, autoClose: 2000 });
            }
            // Keep the placeholder sidebar in sync with the doc's current slugs.
            const slugs = extractPlaceholderSlugs(lexicalJsonRef.current);
            const freshList = await loadPlaceholders();
            const freshSlugs = new Set(freshList.map(p => p.slug));
            const newItems = slugs
                .filter(slug => !freshSlugs.has(slug))
                .map(slug => ({ slug, title: '', type: 'Text', description: '', options: '' }));
            if (newItems.length > 0) {
                setPlaceholderList(prev => [...prev, ...newItems]);
            }
            return { ok: true };
        } catch (err) {
            notifications.show({ title: 'Save failed', message: err.message, color: 'red' });
            return { ok: false };
        } finally {
            setEditorSaving(false);
        }
    };

    const handleEditorSave = () => doEditorSave({ silent: false });

    // 5s silent auto-save — only fires while the floating editor is open and dirty.
    useEffect(() => {
        if (!activeEditorDoc) return undefined;
        const t = setInterval(() => {
            if (editorContentDirtyRef.current) {
                doEditorSave({ silent: true });
            }
        }, 5_000);
        return () => clearInterval(t);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [activeEditorDoc?.id]);

    // Refresh the "Saved · Xs ago" badge every 5s so it stays accurate without edits.
    useEffect(() => {
        if (!activeEditorDoc) return undefined;
        const t = setInterval(() => setEditorNowTick(x => x + 1), 5_000);
        return () => clearInterval(t);
    }, [activeEditorDoc?.id]);

    // Warn on tab close when there are unsaved editor changes.
    useEffect(() => {
        const beforeUnload = (e) => {
            if (editorContentDirtyRef.current) {
                e.preventDefault();
                e.returnValue = '';
            }
        };
        window.addEventListener('beforeunload', beforeUnload);
        return () => window.removeEventListener('beforeunload', beforeUnload);
    }, []);

    // Open a fullscreen preview using the same DocPreviewPanel the user sees.
    // Flushes any dirty content first so the preview reflects the latest saved copy.
    const handleOpenEditorPreview = async () => {
        if (!activeEditorDoc?.id) return;
        setEditorPreviewLoading(true);
        try {
            if (editorContentDirtyRef.current) {
                const res = await doEditorSave({ silent: true });
                if (res && res.ok === false) return;
            }
            const tsId = savedTemplateSet?.id || templateSet?.id;
            if (!tsId) return;
            const setRes = await templateSetService.getTemplateSetById(tsId);
            const freshSet = setRes.data;
            const freshDoc = (freshSet.docs || []).find(d => d.id === activeEditorDoc.id) || activeEditorDoc;
            setEditorPreviewData({
                doc: freshDoc,
                placeholders: Object.fromEntries((freshSet.placeholders || []).map(p => [p.slug, p])),
            });
            setEditorPreviewOpen(true);
        } catch (err) {
            notifications.show({ title: 'Preview failed', message: err.message, color: 'red' });
        } finally {
            setEditorPreviewLoading(false);
        }
    };

    // Handle close with unsaved changes confirmation
    const handleClose = () => {
        if (hasUnsavedChanges) {
            openConfirm({
                title: t('common.discardChangesTitle'),
                message: t('common.discardChangesDescription'),
                onConfirm: () => doClose(),
                onCancel: null,
                variant: 'danger',
                confirmLabel: t('common.discard'),
            });
            return;
        }
        doClose();
    };

    const doClose = () => {

        // Reset states
        setHasUnsavedChanges(false);
        initialFormValuesRef.current = null;
        setEditorInitialState(null);
        setActiveEditorDoc(null);
        lexicalJsonRef.current = null;
        contentKeyCacheRef.current = {};
        onClose();
    };

    const validateStep = async () => {
        const values = form.getFieldsValue();

        if (activeStep === 0) {
            if (!values.name || values.name.length < 3) {
                notifications.show({
                    title: t('common.validationError'),
                    message: t('validation.templateNameMin'),
                    color: 'red'
                });
                return false;
            }
            if (!values.categoryId) {
                notifications.show({
                    title: t('common.validationError'),
                    message: t('validation.categoryRequired'),
                    color: 'red'
                });
                return false;
            }
            if (!values.language) {
                notifications.show({
                    title: t('common.validationError'),
                    message: t('validation.languageRequired'),
                    color: 'red'
                });
                return false;
            }
        } else if (activeStep === 1) {
            if (!docs || docs.length === 0) {
                notifications.show({
                    title: t('common.validationError'),
                    message: t('validation.documentRequired'),
                    color: 'red'
                });
                return false;
            }

            const invalidDoc = docs.some((doc) => !doc.title || doc.title.trim().length < 2);
            if (invalidDoc) {
                notifications.show({
                    title: t('common.validationError'),
                    message: t('validation.documentTitleRequired') || 'Each document needs a title.',
                    color: 'red'
                });
                return false;
            }
        }
        return true;
    };

    // Step 0 → 1: save template info + docs titles then advance
    const handleNext = async () => {
        const isValid = await validateStep();
        if (!isValid) return;
        setLoading(true);
        try {
            await saveTemplateSetToApi();
            setActiveStep(1);
        } catch (err) {
            notifications.show({ title: t('common.error'), message: err.message, color: 'red' });
        } finally {
            setLoading(false);
        }
    };

    const handleBack = () => {
        setActiveStep(prev => prev - 1);
    };

    const handleSubmit = async () => {
        const isValid = await validateStep();
        if (!isValid) return;

        setLoading(true);
        try {
            await saveTemplateSetToApi();
            setActiveStep(2);
        } catch (err) {
            notifications.show({ title: t('common.error'), message: err.message, color: 'red' });
        } finally {
            setLoading(false);
        }
    };

    const languageOptions = [
        { value: 'bangla', label: t('templateSets.bangla') },
        { value: 'english', label: t('templateSets.english') },
        { value: 'mixed', label: t('templateSets.mixed') }
    ];


    // Handle finishing — bulk-save any unsaved placeholder forms then close
    const handleFinish = async () => {
        if (placeholderList.length > 0 && savedTemplateSet?.id) {
            const results = await Promise.allSettled(
                placeholderList.map((item, i) => templateSetService.updatePlaceholderMetadata(
                    savedTemplateSet.id, item.slug, {
                        title: item.title || item.slug,
                        type: item.type || 'Text',
                        description: item.description || '',
                        options: item.options || '',
                        order: i + 1,
                    }
                ))
            );
            // Surface any failures instead of silently swallowing them — previously
            // one 404 slug would block the save from persisting without any feedback.
            const failed = results
                .map((r, i) => ({ r, slug: placeholderList[i]?.slug }))
                .filter(x => x.r.status === 'rejected');
            if (failed.length > 0) {
                notifications.show({
                    title: 'Some placeholders failed to save',
                    message: failed.map(f => `${f.slug}: ${f.r.reason?.message || 'error'}`).join('; '),
                    color: 'red',
                });
                return; // don't close; let admin retry
            }
        }
        clearFormDraft(templateSet?.id || savedTemplateSet?.id);
        setHasUnsavedChanges(false);
        initialFormValuesRef.current = null;
        onSuccess();
        onClose();
    };

    // Keyboard shortcut inside drawer
    useEffect(() => {
        const handleKeyDown = (e) => {
            if (!opened) return;

            // Step navigation / submit / finish
            if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
                e.preventDefault();

                if (activeStep < 1) {
                    handleNext();
                } else if (activeStep === 1) {
                    handleSubmit();
                } else if (activeStep === 2) {
                    handleFinish();
                }
                return;
            }

            // Add new document in step 1 (documents step)
            if (
                opened &&
                activeStep === 1 &&
                docAddHandlerRef.current &&
                (e.ctrlKey || e.metaKey) &&
                e.shiftKey &&
                (e.key === 'n' || e.key === 'N')
            ) {
                e.preventDefault();
                docAddHandlerRef.current();
            }
        };

        window.addEventListener('keydown', handleKeyDown);
        return () => window.removeEventListener('keydown', handleKeyDown);
    }, [opened, activeStep, handleNext, handleSubmit, handleFinish, savedTemplateSet]);

    // Editor status label/color helpers
    const editorStatusLabel = editorSaving
        ? 'Saving…'
        : editorContentDirty
            ? 'Unsaved'
            : editorLastSavedAt
                ? `Saved · ${formatSavedAgo(editorLastSavedAt)}`
                : '';
    const editorStatusColor = editorSaving
        ? 'text-blue-600 bg-blue-50 dark:bg-blue-950/30 dark:text-blue-400'
        : editorContentDirty
            ? 'text-orange-600 bg-orange-50 dark:bg-orange-950/30 dark:text-orange-400'
            : 'text-green-600 bg-green-50 dark:bg-green-950/30 dark:text-green-400';

    return (
        <>
        <Sheet open={opened} onOpenChange={(v) => !v && handleClose()}>
            <SheetContent side="right" className="w-full sm:max-w-2xl flex flex-col p-0 overflow-hidden">
                <SheetHeader className="px-6 py-4 border-b border-zinc-200 dark:border-zinc-700">
                    <SheetTitle>
                        {templateSet ? t('templateSets.editTemplateSet') : t('templateSets.addNewTemplateSet')}
                    </SheetTitle>
                </SheetHeader>

                <div className="flex-1 overflow-y-auto px-6 py-4">
                    {/* Step Indicator */}
                    <div className="flex items-center gap-0 mb-6">
                        {steps.map((step, i) => (
                            <div key={i} className="contents">
                                <div className="flex flex-col items-center">
                                    <div className={`w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium transition-colors ${
                                        activeStep > i
                                            ? 'bg-green-500 text-white'
                                            : activeStep === i
                                                ? 'bg-orange-500 text-white'
                                                : 'bg-zinc-200 dark:bg-zinc-700 text-zinc-500'
                                    }`}>
                                        {activeStep > i ? <IconCheck size={14} /> : i + 1}
                                    </div>
                                    <span className="text-xs mt-1 text-zinc-500">{step.label}</span>
                                </div>
                                {i < steps.length - 1 && (
                                    <div className={`flex-1 h-px mx-2 mt-[-16px] ${activeStep > i ? 'bg-green-500' : 'bg-zinc-300 dark:bg-zinc-600'}`} />
                                )}
                            </div>
                        ))}
                    </div>

                    {/* Step Content */}
                    <Form form={form}>
                        <div className={styles.stepContent}>
                            {/* Step 1: Template Info */}
                            {activeStep === 0 && (
                                <div className="flex flex-col gap-4">
                                    <Field name="name" initialValue="">
                                        {(control, meta) => (
                                            <div className="flex flex-col gap-1">
                                                <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                    {t('templateSets.templateSetName')} <span className="text-red-500">*</span>
                                                </label>
                                                <input
                                                    type="text"
                                                    name="templateSetName"
                                                    data-autofocus
                                                    placeholder={t('templateSets.templateSetNamePlaceholder')}
                                                    className="w-full px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300"
                                                    value={control.value || ''}
                                                    onChange={(e) => {
                                                        control.onChange(e.target.value);
                                                        // Auto-fill slug from name only if slug is still empty or auto-generated
                                                        const currentSlug = form.getFieldValue('slug') || '';
                                                        const prevAutoSlug = slugifyEn(control.value || '');
                                                        if (!currentSlug || currentSlug === prevAutoSlug) {
                                                            form.setFieldsValue({ slug: slugifyEn(e.target.value) });
                                                        }
                                                        handleFormAutoSave();
                                                    }}
                                                />
                                                {meta.errors?.[0] && (
                                                    <p className="text-xs text-red-500">{meta.errors[0]}</p>
                                                )}
                                            </div>
                                        )}
                                    </Field>

                                    <Field name="slug" initialValue="">
                                        {(control, meta) => (
                                            <div className="flex flex-col gap-1">
                                                <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                    Slug <span className="text-xs font-normal text-zinc-400 ml-1">(URL identifier, English only)</span>
                                                </label>
                                                <div className="flex items-center gap-2">
                                                <input
                                                    type="text"
                                                    placeholder="e.g. invoice-template"
                                                    className="flex-1 px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300 font-mono"
                                                    value={control.value || ''}
                                                    onChange={(e) => {
                                                        const raw = e.target.value.toLowerCase().replace(/[^a-z0-9\-_]/g, '');
                                                        control.onChange(raw);
                                                        handleFormAutoSave();
                                                    }}
                                                />
                                                <button
                                                    type="button"
                                                    title="Generate slug from title"
                                                    onClick={() => {
                                                        const name = form.getFieldValue('name') || '';
                                                        const generated = slugifyEn(name);
                                                        if (generated) {
                                                            control.onChange(generated);
                                                            handleFormAutoSave();
                                                        }
                                                    }}
                                                    className="shrink-0 px-3 py-2 text-xs font-medium rounded-md border border-zinc-300 dark:border-zinc-600 bg-zinc-50 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-orange-50 hover:border-orange-300 hover:text-orange-600 dark:hover:bg-orange-900/20 dark:hover:text-orange-400 transition-colors"
                                                >
                                                    <IconWand size={14} />
                                                </button>
                                                </div>
                                                {meta.errors?.[0] && (
                                                    <p className="text-xs text-red-500">{meta.errors[0]}</p>
                                                )}
                                            </div>
                                        )}
                                    </Field>

                                    <Field name="categoryId" initialValue={null}>
                                        {(control, meta) => (
                                            <div className="flex flex-col gap-1">
                                                <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                    {t('templateSets.category')} <span className="text-red-500">*</span>
                                                </label>
                                                <SearchableSelect
                                                    value={control.value || ''}
                                                    onChange={(val) => {
                                                        control.onChange(val || null);
                                                        handleFormAutoSave();
                                                    }}
                                                    options={availableCategories}
                                                    placeholder={t('templateSets.selectCategory')}
                                                    searchPlaceholder={t('common.search')}
                                                    emptyText={t('common.noResults')}
                                                />
                                                {meta.errors?.[0] && (
                                                    <p className="text-xs text-red-500">{meta.errors[0]}</p>
                                                )}
                                            </div>
                                        )}
                                    </Field>

                                    <Field name="language" initialValue="bangla">
                                        {(control, meta) => (
                                            <div className="flex flex-col gap-1">
                                                <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                    {t('templateSets.language')} <span className="text-red-500">*</span>
                                                </label>
                                                <Select
                                                    value={control.value || 'bangla'}
                                                    onValueChange={(val) => {
                                                        control.onChange(val);
                                                        handleFormAutoSave();
                                                    }}
                                                >
                                                    <SelectTrigger>
                                                        <SelectValue />
                                                    </SelectTrigger>
                                                    <SelectContent>
                                                        {languageOptions.map(opt => (
                                                            <SelectItem key={opt.value} value={opt.value}>{opt.label}</SelectItem>
                                                        ))}
                                                    </SelectContent>
                                                </Select>
                                                {meta.errors?.[0] && (
                                                    <p className="text-xs text-red-500">{meta.errors[0]}</p>
                                                )}
                                            </div>
                                        )}
                                    </Field>

                                    <Field name="tagIds" initialValue={[]}>
                                        {(control) => (
                                            <div className="flex flex-col gap-1">
                                                <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                    {t('templateSets.tags')}
                                                </label>
                                                <MultiSelect
                                                    value={control.value || []}
                                                    onChange={(next) => {
                                                        control.onChange(next);
                                                        handleFormAutoSave();
                                                    }}
                                                    options={availableTags}
                                                    placeholder={t('templateSets.selectTags')}
                                                    searchPlaceholder={t('common.search')}
                                                    emptyText={t('common.noResults')}
                                                />
                                            </div>
                                        )}
                                    </Field>

                                    <Field name="status" initialValue="published">
                                        {(control, meta) => (
                                            <div className="flex flex-col gap-1">
                                                <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                    {t('templateSets.status') || 'Status'} <span className="text-red-500">*</span>
                                                </label>
                                                {(() => {
                                                    const isPublished = (control.value || 'published') === 'published';
                                                    return (
                                                        <div className="flex items-center gap-3 px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800">
                                                            <Switch
                                                                checked={isPublished}
                                                                onCheckedChange={(checked) => {
                                                                    control.onChange(checked ? 'published' : 'draft');
                                                                    handleFormAutoSave();
                                                                }}
                                                            />
                                                            <span className={`text-sm font-medium ${isPublished ? 'text-emerald-600 dark:text-emerald-400' : 'text-zinc-500 dark:text-zinc-400'}`}>
                                                                {isPublished
                                                                    ? (t('templateSets.published') || 'Published')
                                                                    : (t('templateSets.draft') || 'Draft')}
                                                            </span>
                                                        </div>
                                                    );
                                                })()}
                                                {meta.errors?.[0] && (
                                                    <p className="text-xs text-red-500">{meta.errors[0]}</p>
                                                )}
                                            </div>
                                        )}
                                    </Field>

                                    {/* Free vs paid — free sets skip credit deduction on document generation */}
                                    <Field name="isFree" initialValue={false}>
                                        {(control) => {
                                            const free = control.value === true;
                                            return (
                                                <div className="flex flex-col gap-1">
                                                    <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                        {t('templateSets.freeLabel') || 'Free set'}
                                                    </label>
                                                    <div className="flex items-center gap-3 px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800">
                                                        <Switch
                                                            checked={free}
                                                            onCheckedChange={(checked) => {
                                                                control.onChange(checked === true);
                                                                handleFormAutoSave();
                                                            }}
                                                        />
                                                        <span className={`text-sm font-medium ${free ? 'text-emerald-600 dark:text-emerald-400' : 'text-zinc-500 dark:text-zinc-400'}`}>
                                                            {free
                                                                ? (t('templateSets.free') || 'Free')
                                                                : (t('templateSets.paid') || 'Paid')}
                                                        </span>
                                                    </div>
                                                    <p className="text-xs text-zinc-400">{t('templateSets.freeHint') || 'No credit is charged when generating from this set.'}</p>
                                                </div>
                                            );
                                        }}
                                    </Field>

                                    <Field name="description" initialValue="">
                                        {(control) => (
                                            <div className="flex flex-col gap-1">
                                                <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                    {t('templateSets.description')}
                                                </label>
                                                <textarea
                                                    placeholder={t('templateSets.fieldDescriptionPlaceholder')}
                                                    rows={4}
                                                    maxLength={500}
                                                    className="w-full px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300 resize-none"
                                                    value={control.value || ''}
                                                    onChange={(e) => {
                                                        control.onChange(e.target.value);
                                                        handleFormAutoSave();
                                                    }}
                                                />
                                                {control.value && (
                                                    <p className="text-xs text-zinc-400 text-right">
                                                        {control.value.length}/500
                                                    </p>
                                                )}
                                            </div>
                                        )}
                                    </Field>
                                </div>
                            )}

                            {/* Step 2: Documents */}
                            {activeStep === 1 && (
                                <div className="flex flex-col gap-3">
                                    {/* Search keys — free-form tags users type to find this template set. */}
                                    <Field name="searchKeys">
                                        {(control) => {
                                            const tags = Array.isArray(control.value) ? control.value : [];
                                            // Accepts one value or a comma/newline-separated blob (e.g. pasted text)
                                            // and adds every unique, non-empty keyword in one go.
                                            const addTag = (val) => {
                                                const incoming = String(val)
                                                    .split(/[,\n\r\t]+/)
                                                    .map(s => s.trim())
                                                    .filter(Boolean);
                                                if (incoming.length === 0) return;
                                                const next = [...tags];
                                                for (const kw of incoming) {
                                                    if (!next.includes(kw)) next.push(kw);
                                                }
                                                if (next.length !== tags.length) {
                                                    control.onChange(next);
                                                    handleFormAutoSave();
                                                }
                                            };
                                            const removeTag = (tag) => {
                                                control.onChange(tags.filter(t => t !== tag));
                                                handleFormAutoSave();
                                            };
                                            return (
                                                <div className="flex flex-col gap-1">
                                                    <label className="text-sm font-medium text-zinc-700 dark:text-zinc-300">
                                                        {t('templateSets.searchKeysLabel') || 'Search keys'}
                                                    </label>
                                                    <p className="text-xs text-zinc-400">
                                                        {t('templateSets.searchKeysDescription') || 'Press comma or Enter to add a keyword. Users can search any of these to find this template.'}
                                                    </p>
                                                    <div className="flex flex-wrap gap-1 p-2 border border-zinc-300 dark:border-zinc-600 rounded-md min-h-[38px] bg-white dark:bg-zinc-800">
                                                        {tags.map(tag => (
                                                            <span key={tag} className="inline-flex items-center gap-1 px-2 py-0.5 rounded bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400 text-xs">
                                                                {tag}
                                                                <button onClick={() => removeTag(tag)} className="text-orange-500 hover:text-orange-700">
                                                                    <IconX size={10} />
                                                                </button>
                                                            </span>
                                                        ))}
                                                        <input
                                                            type="text"
                                                            value={tagInputValue}
                                                            onChange={e => setTagInputValue(e.target.value)}
                                                            onKeyDown={(e) => {
                                                                if (e.key === 'Enter' || e.key === ',') {
                                                                    e.preventDefault();
                                                                    addTag(tagInputValue);
                                                                    setTagInputValue('');
                                                                }
                                                            }}
                                                            onPaste={(e) => {
                                                                const text = e.clipboardData.getData('text');
                                                                // If the pasted text contains separators, split it into tags.
                                                                if (/[,\n\r\t]/.test(text)) {
                                                                    e.preventDefault();
                                                                    addTag(`${tagInputValue}${text}`);
                                                                    setTagInputValue('');
                                                                }
                                                            }}
                                                            onBlur={() => {
                                                                if (tagInputValue.trim()) {
                                                                    addTag(tagInputValue);
                                                                    setTagInputValue('');
                                                                }
                                                            }}
                                                            className="flex-1 min-w-[80px] bg-transparent text-sm focus:outline-none text-zinc-700 dark:text-zinc-300"
                                                            placeholder={t('templateSets.searchKeysPlaceholder') || 'e.g. rent agreement, lease, tenancy'}
                                                        />
                                                    </div>
                                                </div>
                                            );
                                        }}
                                    </Field>

                                    {docs.length === 0 ? (
                                        <div className="rounded-xl border border-dashed border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-900 p-8 text-center">
                                            <p className="text-sm text-zinc-400">
                                                {t('templateSets.clickAddDocument') || 'No documents yet. Click "+ Add Document" to get started.'}
                                            </p>
                                        </div>
                                    ) : (
                                        <div className="flex flex-col gap-2">
                                            {docs.map((doc, index) => (
                                                <div key={index} className="rounded-xl border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 p-3">
                                                    <div className="flex items-start gap-2 flex-nowrap">
                                                        <span className="px-2 py-0.5 text-xs rounded-full bg-zinc-100 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300 shrink-0 mt-1.5">
                                                            {index + 1}
                                                        </span>
                                                        <div className="flex flex-col gap-1.5 flex-1 min-w-0">
                                                            <input
                                                                type="text"
                                                                placeholder={t('templateSets.documentTitlePlaceholder') || 'Document title…'}
                                                                required
                                                                className="w-full px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-sm focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300"
                                                                value={doc.title || ''}
                                                                onChange={(e) => {
                                                                    const value = e.target.value;
                                                                    setDocs(prev => prev.map((item, i) => i === index ? { ...item, title: value } : item));
                                                                    handleFormAutoSave();
                                                                }}
                                                            />
                                                            <textarea
                                                                placeholder={t('templateSets.fieldDescriptionPlaceholder') || 'Short description (optional)'}
                                                                rows={2}
                                                                className="w-full px-3 py-2 rounded-md border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 text-xs focus:outline-none focus:ring-2 focus:ring-orange-500 text-zinc-700 dark:text-zinc-300 resize-none"
                                                                value={doc.content || ''}
                                                                onChange={(e) => {
                                                                    const value = e.target.value;
                                                                    setDocs(prev => prev.map((item, i) => i === index ? { ...item, content: value } : item));
                                                                    handleFormAutoSave();
                                                                }}
                                                            />
                                                        </div>
                                                        <button
                                                            title={t('common.delete')}
                                                            className="p-1.5 rounded text-red-500 hover:bg-red-50 dark:hover:bg-red-950/20 shrink-0 mt-1"
                                                            onClick={() => {
                                                                setDocs(prev => prev.filter((_, i) => i !== index));
                                                                handleFormAutoSave();
                                                            }}
                                                        >
                                                            <IconTrash size={14} />
                                                        </button>
                                                    </div>
                                                </div>
                                            ))}
                                        </div>
                                    )}

                                    <button
                                        type="button"
                                        className="flex items-center gap-1.5 text-sm text-orange-600 hover:text-orange-700 dark:text-orange-400 px-2 py-1 rounded hover:bg-orange-50 dark:hover:bg-orange-950/20 self-start"
                                        onClick={() => {
                                            setDocs(prev => [...prev, { title: '', content: '' }]);
                                            handleFormAutoSave();
                                        }}
                                    >
                                        <IconPlus size={13} />
                                        {t('templateSets.addDocument')}
                                    </button>
                                </div>
                            )}

                            {/* Step 3: Placeholders */}
                            {activeStep === 2 && (
                                <div className="flex flex-col gap-4">
                                    {/* Document tabs — click to open in inline editor */}
                                    {(savedTemplateSet?.docs || []).length > 0 && (
                                        <div className="flex gap-1.5 flex-wrap p-3 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-zinc-200 dark:border-zinc-700">
                                            <p className="w-full text-xs font-medium tracking-wide uppercase text-zinc-400 mb-1">
                                                {t('templateSets.editDocumentContent')}
                                            </p>
                                            {(savedTemplateSet?.docs || []).map(doc => {
                                                const isActive = activeEditorDoc?.id === doc.id;
                                                const hasSaved = contentKeyCacheRef.current[doc.id] || doc.contentKey;
                                                const fullTitle = doc.title || '';
                                                const displayTitle = fullTitle.length > 30 ? `${fullTitle.slice(0, 30)}…` : fullTitle;
                                                return (
                                                    <button
                                                        key={doc.id}
                                                        title={fullTitle.length > 30 ? fullTitle : undefined}
                                                        onClick={() => navigate(`/admin/lexical-editor/${savedTemplateSet.id}/${doc.id}`)}
                                                        className={`flex items-center gap-1.5 px-3.5 py-1.5 text-sm rounded-md border-none transition-all duration-150 max-w-[280px] ${
                                                            isActive
                                                                ? 'bg-blue-500 text-white font-semibold shadow-md shadow-blue-500/30'
                                                                : 'bg-white dark:bg-zinc-800 text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300 shadow-sm'
                                                        }`}
                                                    >
                                                        <IconEdit size={13} className="shrink-0" />
                                                        <span className="overflow-hidden text-ellipsis whitespace-nowrap">{displayTitle}</span>
                                                        {hasSaved && (
                                                            <span className={`w-1.5 h-1.5 rounded-full shrink-0 ${isActive ? 'bg-white/80' : 'bg-green-500'}`} />
                                                        )}
                                                    </button>
                                                );
                                            })}
                                        </div>
                                    )}

                                    {placeholderList.length === 0 ? (
                                        <div className="rounded-xl border border-dashed border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-900 p-8 text-center">
                                            <p className="text-sm text-zinc-400">
                                                {t('templateSets.noPlaceholdersFound')}
                                            </p>
                                        </div>
                                    ) : (
                                        <DndContext
                                            sensors={dndSensors}
                                            collisionDetection={closestCenter}
                                            onDragEnd={(event) => {
                                                const { active, over } = event;
                                                if (!over || active.id === over.id) return;
                                                setPlaceholderList(prev => {
                                                    const oldIndex = prev.findIndex(p => p.slug === active.id);
                                                    const newIndex = prev.findIndex(p => p.slug === over.id);
                                                    const next = arrayMove(prev, oldIndex, newIndex);
                                                    handleDragEndPersist(next);
                                                    return next;
                                                });
                                            }}
                                        >
                                            <SortableContext
                                                items={placeholderList.map(p => p.slug)}
                                                strategy={verticalListSortingStrategy}
                                            >
                                                <div className="flex flex-col gap-1">
                                                    {placeholderList.map((item) => (
                                                        <SortablePlaceholderItem
                                                            key={item.slug}
                                                            item={item}
                                                            saving={!!placeholderSaving[item.slug]}
                                                            onUpdate={(field, value) =>
                                                                setPlaceholderList(prev =>
                                                                    prev.map(p => p.slug === item.slug ? { ...p, [field]: value } : p)
                                                                )
                                                            }
                                                            onSave={() => handlePlaceholderSave(item.slug)}
                                                            onDelete={() => handleDeletePlaceholder(item.slug)}
                                                        />
                                                    ))}
                                                </div>
                                            </SortableContext>
                                        </DndContext>
                                    )}

                                    <div className="flex items-center gap-2">
                                        <button
                                            type="button"
                                            className="flex items-center gap-1.5 text-sm text-violet-600 dark:text-violet-400 px-3 py-1.5 rounded-md bg-violet-50 dark:bg-violet-950/20 hover:bg-violet-100 dark:hover:bg-violet-950/40"
                                            onClick={handleAddStepBreak}
                                        >
                                            <IconSeparator size={13} />
                                            Add Step Break
                                        </button>
                                        <button
                                            type="button"
                                            title="Refresh placeholder list"
                                            className="p-1.5 rounded text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-700"
                                            onClick={loadPlaceholders}
                                        >
                                            <IconRefresh size={14} />
                                        </button>
                                    </div>
                                </div>
                            )}
                        </div>
                    </Form>
                </div>

                {/* Footer */}
                <div className="px-6 py-4 border-t border-zinc-200 dark:border-zinc-700">
                    <div className="flex items-center justify-between">
                        <button
                            type="button"
                            className="px-4 py-2 text-sm text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700 rounded-md"
                            onClick={handleClose}
                        >
                            {t('common.cancel')}
                        </button>

                        <div className="flex items-center gap-2">
                            {activeStep > 0 && (
                                <TooltipButton
                                    hotkeyAction="PREV_PAGE"
                                    variant="default"
                                    onClick={handleBack}
                                >
                                    {t('common.back')}
                                </TooltipButton>
                            )}

                            {draftSaved && (
                                <span className="px-2 py-0.5 text-xs rounded-full bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400">
                                    Draft Saved
                                </span>
                            )}

                            {activeStep === 0 ? (
                                <TooltipButton
                                    hotkeyAction="SUBMIT_FORM"
                                    onClick={handleNext}
                                    loading={loading}
                                >
                                    {t('common.next')}
                                </TooltipButton>
                            ) : activeStep === 1 ? (
                                <TooltipButton
                                    hotkeyAction="SUBMIT_FORM"
                                    onClick={handleSubmit}
                                    loading={loading}
                                >
                                    {t('templateSets.nextStep')}
                                </TooltipButton>
                            ) : (
                                <TooltipButton
                                    hotkeyAction="SUBMIT_FORM"
                                    onClick={handleFinish}
                                >
                                    {t('common.finish')}
                                </TooltipButton>
                            )}
                        </div>
                    </div>
                </div>
            </SheetContent>
        </Sheet>

        {/* Inline editor panel — renders fixed to the left of the drawer */}
        {activeEditorDoc && (
            <div
                style={{
                    position: 'fixed',
                    top: 0,
                    bottom: 0,
                    right: editorExpanded ? 0 : '30vw',
                    width: editorExpanded ? '100vw' : '70vw',
                    display: 'flex',
                    flexDirection: 'column',
                    borderRight: editorExpanded ? 'none' : '1px solid',
                    boxShadow: '-6px 0 24px rgba(0,0,0,0.12)',
                    zIndex: 401,
                    overflow: 'hidden',
                    transition: 'width 0.2s ease, right 0.2s ease',
                }}
                className="bg-white dark:bg-zinc-900 border-zinc-200 dark:border-zinc-700"
            >
                {/* Editor header */}
                <div className="flex items-center justify-between px-4 py-2 border-b border-zinc-200 dark:border-zinc-700 shrink-0 bg-zinc-50 dark:bg-zinc-800">
                    <div className="flex items-center gap-2">
                        <img
                            src="/images/logo.svg"
                            alt="Sohozkaj"
                            style={{ height: 22, width: 'auto', display: 'block', marginRight: 4 }}
                        />
                        <span className="font-semibold text-sm text-zinc-800 dark:text-zinc-200 line-clamp-1">{activeEditorDoc.title}</span>
                        {(contentKeyCacheRef.current[activeEditorDoc.id] || activeEditorDoc.contentKey) && (
                            <span className="px-2 py-0.5 text-xs rounded-full bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400">Saved</span>
                        )}
                    </div>
                    <div className="flex items-center gap-1.5">
                        <button
                            title="Zoom out"
                            className="p-1.5 rounded text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-700"
                            onClick={() => setEditorZoom(z => Math.max(+(z - 0.1).toFixed(1), 0.3))}
                        >
                            <IconZoomOut size={14} />
                        </button>
                        <span className="text-xs text-zinc-400 min-w-[36px] text-center">
                            {Math.round(editorZoom * 100)}%
                        </span>
                        <button
                            title="Zoom in"
                            className="p-1.5 rounded text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-700"
                            onClick={() => setEditorZoom(z => Math.min(+(z + 0.1).toFixed(1), 2))}
                        >
                            <IconZoomIn size={14} />
                        </button>
                        <button
                            title="Reset zoom"
                            className="p-1.5 rounded text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-700"
                            onClick={() => setEditorZoom(1)}
                        >
                            <IconZoomReset size={14} />
                        </button>
                        <button
                            title={editorShowHighlight ? 'Hide placeholder colors' : 'Show placeholder colors'}
                            className={`p-1.5 rounded ${editorShowHighlight ? 'text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-700' : 'bg-yellow-400 text-white'}`}
                            onClick={() => setEditorShowHighlight(v => !v)}
                        >
                            {editorShowHighlight ? <IconEye size={14} /> : <IconEyeOff size={14} />}
                        </button>
                        <button
                            type="button"
                            className="flex items-center gap-1 text-xs px-2.5 py-1.5 rounded text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700"
                            onClick={() => setEditorExpanded(v => !v)}
                        >
                            {editorExpanded ? <IconArrowsMinimize size={14} /> : <IconArrowsMaximize size={14} />}
                            {editorExpanded ? 'Collapse' : 'Expand'}
                        </button>
                        {editorStatusLabel && (
                            <span className={`px-2 py-0.5 text-xs rounded-full ${editorStatusColor}`}>
                                {editorStatusLabel}
                            </span>
                        )}
                        <button
                            type="button"
                            className="flex items-center gap-1 text-xs px-2.5 py-1.5 rounded bg-zinc-100 dark:bg-zinc-700 text-zinc-600 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-600 disabled:opacity-50"
                            disabled={editorPreviewLoading}
                            onClick={handleOpenEditorPreview}
                        >
                            {editorPreviewLoading
                                ? <div className="w-3.5 h-3.5 border-2 border-zinc-500 border-t-transparent rounded-full animate-spin" />
                                : <IconEye size={14} />
                            }
                            Preview
                        </button>
                        <button
                            type="button"
                            className="flex items-center gap-1 text-xs px-2.5 py-1.5 rounded bg-orange-500 text-white hover:bg-orange-600 disabled:opacity-50"
                            disabled={(!editorContentDirty && !editorSaving) || editorSaving}
                            onClick={handleEditorSave}
                        >
                            {editorSaving
                                ? <div className="w-3.5 h-3.5 border-2 border-white border-t-transparent rounded-full animate-spin" />
                                : <IconDeviceFloppy size={14} />
                            }
                            Save
                        </button>
                        <button
                            type="button"
                            className="flex items-center gap-1 text-xs px-2.5 py-1.5 rounded text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700"
                            onClick={async () => {
                                if (editorContentDirtyRef.current) await doEditorSave({ silent: true });
                                setActiveEditorDoc(null);
                            }}
                        >
                            <IconX size={14} />
                            Close
                        </button>
                    </div>
                </div>

                {/* Editor body */}
                <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
                    {editorLoading ? (
                        <div className="flex items-center justify-center h-full">
                            <div className="w-8 h-8 border-2 border-orange-500 border-t-transparent rounded-full animate-spin" />
                        </div>
                    ) : (
                        <LexicalDocEditor
                            key={editorKey}
                            initialState={editorInitialState}
                            onChange={(json) => {
                                lexicalJsonRef.current = json;
                                // First fire after mount is Lexical echoing the initial state.
                                // Skip it so the Save button stays disabled until the admin edits.
                                if (!editorInitialChangeConsumedRef.current) {
                                    editorInitialChangeConsumedRef.current = true;
                                    return;
                                }
                                if (!editorContentDirtyRef.current) {
                                    editorContentDirtyRef.current = true;
                                    setEditorContentDirty(true);
                                }
                            }}
                            zoom={editorZoom}
                            showHighlight={editorShowHighlight}
                            initialPageSettings={editorPageSettings}
                            onPageSettingsChange={(next) => {
                                editorPageSettingsRef.current = next;
                                setEditorPageSettings(next);
                                if (!editorContentDirtyRef.current) {
                                    editorContentDirtyRef.current = true;
                                    setEditorContentDirty(true);
                                }
                            }}
                        />
                    )}
                </div>
            </div>
        )}

        {/* Preview Modal */}
        <Dialog open={editorPreviewOpen} onOpenChange={setEditorPreviewOpen}>
            <DialogContent className="max-w-full w-screen h-screen p-0 flex flex-col" style={{ zIndex: 500 }}>
                <DialogHeader className="px-4 py-3 border-b border-zinc-200 dark:border-zinc-700 shrink-0">
                    <DialogTitle>
                        <div className="flex items-center gap-2">
                            <IconEye size={16} />
                            <span className="font-semibold text-sm">Preview · {editorPreviewData?.doc?.title || 'Document'}</span>
                            <span className="px-2 py-0.5 text-xs rounded-full bg-zinc-100 text-zinc-500 dark:bg-zinc-700 dark:text-zinc-400">Dummy data</span>
                        </div>
                    </DialogTitle>
                </DialogHeader>
                <div className="flex-1 overflow-hidden">
                    {editorPreviewData?.doc ? (
                        <DocPreviewPanel
                            templateDoc={editorPreviewData.doc}
                            formValues={editorPreviewData.doc.previewValues || {}}
                            placeholders={editorPreviewData.placeholders || {}}
                        />
                    ) : (
                        <div className="flex items-center justify-center py-16">
                            <div className="w-5 h-5 border-2 border-orange-500 border-t-transparent rounded-full animate-spin" />
                        </div>
                    )}
                </div>
            </DialogContent>
        </Dialog>

        {/* Reusable confirm dialog — replaces all window.confirm calls */}
        <ConfirmDialog
            open={confirmDialog.open}
            onClose={() => { closeConfirm(); confirmDialog.onCancel?.(); }}
            onConfirm={() => { closeConfirm(); confirmDialog.onConfirm?.(); }}
            title={confirmDialog.title}
            description={confirmDialog.message}
            confirmLabel={confirmDialog.confirmLabel || t('common.confirm')}
            cancelLabel={t('common.cancel')}
            variant={confirmDialog.variant}
        />
        </>
    );
}
