'use client';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useCallback, useEffect, useMemo, useState } from 'react';
import { AlertTriangle, Loader2 } from 'lucide-react';
import { toast } from 'sonner';
import { ConfirmDialog } from '../../components/ui/ConfirmDialog.js';
import { ErrorBoundary } from '../../components/ErrorBoundary.js';
import { addSection, createSection, duplicateSection, emptyPostFromTemplate, fetchPostForEditor, fetchTemplateForEditor, publishPost, removeSection, reorderSections, savePostDraft, toggleSectionVisibility, updateSectionContent, updateSectionMeta, updateSectionSettings, validatePost, } from '../../lib/post-editor-service.js';
import { defaultPostHeader } from '@actuate-media/cms-core/page-sections';
import { buildPublicPreviewUrl, createPreviewToken, resolveSiteUrl, } from '../../lib/preview-link.js';
import { DEFAULT_VIEWPORT } from '../page-editor/viewports.js';
import { EditorTopBar } from '../page-editor/EditorTopBar.js';
import { SectionsPanel } from '../page-editor/SectionsPanel.js';
import { SectionInspector } from '../page-editor/SectionInspector.js';
import { AddSectionModal } from '../page-editor/AddSectionModal.js';
import { ValidationSummary } from '../page-editor/ValidationSummary.js';
import { PostEditorCanvas } from './PostEditorCanvas.js';
import { PostFieldsModal } from './PostFieldsModal.js';
function deriveCan(session) {
    const role = session?.user?.role;
    // Default closed: when we can't identify a role, hide the edit/publish
    // affordances rather than exposing them. The server independently enforces
    // access on every write, so this is a UX guard — but it should fail safe.
    if (!role)
        return { canEdit: false, canPublish: false };
    const editors = ['ADMIN', 'EDITOR', 'AUTHOR'];
    const publishers = ['ADMIN', 'EDITOR'];
    return { canEdit: editors.includes(role), canPublish: publishers.includes(role) };
}
export function PostSectionEditor({ postType, documentId, config, session, onNavigate, onOpenPreview, }) {
    const [post, setPost] = useState(null);
    const [header, setHeader] = useState(defaultPostHeader());
    const [loading, setLoading] = useState(true);
    const [loadError, setLoadError] = useState(null);
    const [selectedId, setSelectedId] = useState(null);
    const [viewport, setViewport] = useState(DEFAULT_VIEWPORT);
    const [dirty, setDirty] = useState(false);
    const [saveState, setSaveState] = useState('idle');
    const [publishing, setPublishing] = useState(false);
    const [addOpen, setAddOpen] = useState(false);
    const [fieldsOpen, setFieldsOpen] = useState(false);
    const [deleteTargetId, setDeleteTargetId] = useState(null);
    const [confirmPublish, setConfirmPublish] = useState(false);
    const [leaveTarget, setLeaveTarget] = useState(null);
    const [validation, setValidation] = useState(null);
    const { canEdit, canPublish } = useMemo(() => deriveCan(session), [session]);
    const typeLabel = useMemo(() => {
        const col = config?.collections?.[postType];
        return col?.labels?.singular ?? postType;
    }, [config, postType]);
    // ─── Load ──────────────────────────────────────────────────────────
    useEffect(() => {
        let cancelled = false;
        setLoading(true);
        setLoadError(null);
        // Always load the type template header (the post editor renders it but
        // never edits it — header design lives at the type level).
        fetchTemplateForEditor(postType)
            .then((t) => {
            if (!cancelled)
                setHeader(t.header);
        })
            .catch(() => {
            /* fall back to the default header */
        });
        const load = documentId
            ? fetchPostForEditor(postType, documentId)
            : emptyPostFromTemplate(postType);
        load
            .then((p) => {
            if (cancelled)
                return;
            setPost(p);
            setSelectedId(null);
            if (!documentId)
                setDirty(true);
        })
            .catch((err) => {
            if (!cancelled)
                setLoadError(err instanceof Error ? err.message : 'Failed to load post');
        })
            .finally(() => {
            if (!cancelled)
                setLoading(false);
        });
        return () => {
            cancelled = true;
        };
    }, [postType, documentId]);
    // ─── Unsaved-changes guard ───────────────────────────────────────────
    useEffect(() => {
        if (!dirty)
            return;
        function onBeforeUnload(e) {
            e.preventDefault();
            e.returnValue = '';
        }
        window.addEventListener('beforeunload', onBeforeUnload);
        return () => window.removeEventListener('beforeunload', onBeforeUnload);
    }, [dirty]);
    const context = useMemo(() => ({
        title: post?.title,
        excerpt: post?.excerpt,
        body: post?.body,
        featuredImageUrl: post?.featuredImage,
        publishDate: post?.publishDate ?? post?.updatedAt ?? null,
        category: post?.category,
        author: { name: session?.user?.name || session?.user?.email },
    }), [post, session]);
    // ─── Section mutations ───────────────────────────────────────────────
    const mutateSections = useCallback((fn) => {
        setPost((prev) => (prev ? { ...prev, sections: fn(prev.sections) } : prev));
        setDirty(true);
        setSaveState('idle');
    }, []);
    const handleAdd = useCallback((type) => {
        const section = createSection(type);
        mutateSections((s) => addSection(s, section));
        setSelectedId(section.id);
        setAddOpen(false);
    }, [mutateSections]);
    const handleReorder = useCallback((orderedIds) => mutateSections((s) => reorderSections(s, orderedIds)), [mutateSections]);
    const handleToggleVisibility = useCallback((id) => mutateSections((s) => toggleSectionVisibility(s, id)), [mutateSections]);
    const handleDuplicate = useCallback((id) => mutateSections((s) => duplicateSection(s, id)), [mutateSections]);
    const confirmDelete = useCallback(() => {
        if (!deleteTargetId)
            return;
        mutateSections((s) => removeSection(s, deleteTargetId));
        setSelectedId((cur) => (cur === deleteTargetId ? null : cur));
        setDeleteTargetId(null);
    }, [deleteTargetId, mutateSections]);
    const handleContentChange = useCallback((patch) => {
        if (!selectedId)
            return;
        mutateSections((s) => updateSectionContent(s, selectedId, patch));
    }, [selectedId, mutateSections]);
    const handleSettingsChange = useCallback((patch) => {
        if (!selectedId)
            return;
        mutateSections((s) => updateSectionSettings(s, selectedId, patch));
    }, [selectedId, mutateSections]);
    const handleMetaChange = useCallback((patch) => {
        if (!selectedId)
            return;
        mutateSections((s) => updateSectionMeta(s, selectedId, patch));
    }, [selectedId, mutateSections]);
    const handleFields = useCallback((patch) => {
        setPost((prev) => (prev ? { ...prev, ...patch } : prev));
        setDirty(true);
        setSaveState('idle');
    }, []);
    // ─── Save / publish ──────────────────────────────────────────────────
    const handleSaveDraft = useCallback(async () => {
        if (!post)
            return false;
        const result = validatePost(post, false);
        if (result.errors.length > 0) {
            setValidation(result);
            toast.error(result.errors[0]);
            setFieldsOpen(true);
            return false;
        }
        setSaveState('saving');
        try {
            const saved = await savePostDraft(post);
            setPost(saved);
            setDirty(false);
            setSaveState('saved');
            setValidation(null);
            toast.success('Draft saved');
            if (!documentId && saved.id)
                onNavigate(`/posts/${postType}/${saved.id}/edit`);
            return true;
        }
        catch (err) {
            setSaveState('error');
            toast.error(err instanceof Error ? err.message : 'Failed to save draft');
            return false;
        }
    }, [post, documentId, postType, onNavigate]);
    const requestPublish = useCallback(() => {
        if (!post)
            return;
        const result = validatePost(post, true);
        setValidation(result.errors.length || result.warnings.length ? result : null);
        if (result.errors.length > 0) {
            toast.error('Resolve the listed issues before publishing');
            return;
        }
        setConfirmPublish(true);
    }, [post]);
    const doPublish = useCallback(async () => {
        if (!post)
            return;
        setPublishing(true);
        try {
            const published = await publishPost(post);
            setPost(published);
            setDirty(false);
            setSaveState('saved');
            setValidation(null);
            toast.success('Post published');
            if (!documentId && published.id)
                onNavigate(`/posts/${postType}/${published.id}/edit`);
        }
        catch (err) {
            toast.error(err instanceof Error ? err.message : 'Failed to publish post');
        }
        finally {
            setPublishing(false);
        }
    }, [post, documentId, postType, onNavigate]);
    const guardedNavigate = useCallback((path) => {
        if (dirty)
            setLeaveTarget(path);
        else
            onNavigate(path);
    }, [dirty, onNavigate]);
    const handlePreview = useCallback(() => {
        if (!post?.id) {
            toast.error('Save the post before previewing');
            return;
        }
        const path = `/posts/${postType}/${post.id}/preview`;
        // New-tab preview: save first when there are unsaved edits so the preview
        // (which reads the persisted document) reflects what's on screen.
        if (onOpenPreview) {
            onOpenPreview(path, dirty ? handleSaveDraft : undefined);
        }
        else {
            guardedNavigate(path);
        }
    }, [post, postType, dirty, onOpenPreview, handleSaveDraft, guardedNavigate]);
    // "View on site": render the draft through the consumer's real public route
    // (`/<urlPrefix>/<slug>?preview=<token>`) via a signed preview token.
    const handleViewOnSite = useCallback(() => {
        if (!post?.id || !post.slug) {
            toast.error('Save the post before viewing it on the site');
            return;
        }
        const tab = window.open('about:blank', '_blank');
        if (!tab) {
            toast.error('Enable pop-ups for this site to open the preview in a new tab.');
            return;
        }
        void (async () => {
            try {
                if (dirty) {
                    const ok = await handleSaveDraft();
                    if (!ok) {
                        tab.close();
                        return;
                    }
                }
                const { token } = await createPreviewToken(postType, post.id);
                const urlPrefix = config?.collections?.[postType]?.urlPrefix ?? postType;
                const target = buildPublicPreviewUrl({
                    siteUrl: resolveSiteUrl(config?.seo?.siteUrl),
                    urlPrefix,
                    slug: post.slug,
                    token,
                });
                try {
                    tab.opener = null;
                }
                catch {
                    /* same-origin guard */
                }
                tab.location.replace(target);
            }
            catch (err) {
                tab.close();
                toast.error(err instanceof Error ? err.message : 'Failed to open site preview');
            }
        })();
    }, [post, postType, dirty, config, handleSaveDraft]);
    // ─── Render states ───────────────────────────────────────────────────
    if (loading) {
        return (_jsxs("div", { className: "bg-background flex h-full items-center justify-center", children: [_jsx(Loader2, { className: "text-muted-foreground h-6 w-6 animate-spin", "aria-hidden": true }), _jsx("span", { className: "sr-only", children: "Loading post editor\u2026" })] }));
    }
    if (loadError || !post) {
        return (_jsx("div", { className: "bg-background flex h-full items-center justify-center p-6", children: _jsxs("div", { className: "border-destructive/30 bg-destructive/5 flex max-w-md items-center gap-3 rounded-lg border p-4", children: [_jsx(AlertTriangle, { className: "text-destructive shrink-0", size: 20, "aria-hidden": true }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: "Failed to load post" }), _jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: loadError ?? 'Post not found.' }), _jsx("button", { type: "button", onClick: () => onNavigate('/posts'), className: "text-primary mt-2 text-sm font-medium hover:underline", children: "Back to Posts" })] })] }) }));
    }
    const selected = post.sections.find((s) => s.id === selectedId) ?? null;
    return (_jsxs(ErrorBoundary, { children: [_jsxs("div", { className: "bg-background flex h-full flex-col overflow-hidden", children: [_jsx(EditorTopBar, { title: post.title || `New ${typeLabel}`, status: post.status, dirty: dirty, saveState: saveState, publishing: publishing, canEdit: canEdit, canPublish: canPublish && canEdit, viewport: viewport, backLabel: "Posts", onViewport: setViewport, onBack: () => guardedNavigate('/posts'), onPreview: handlePreview, onViewOnSite: post.id ? handleViewOnSite : undefined, onOpenSettings: () => setFieldsOpen(true), onSaveDraft: handleSaveDraft, onPublish: requestPublish }), validation && (_jsx(ValidationSummary, { errors: validation.errors, warnings: validation.warnings, onDismiss: () => setValidation(null) })), _jsxs("div", { className: "flex flex-1 overflow-hidden", children: [_jsx(SectionsPanel, { sections: post.sections, selectedId: selectedId, canEdit: canEdit, onSelect: setSelectedId, onReorder: handleReorder, onToggleVisibility: handleToggleVisibility, onDuplicate: handleDuplicate, onRequestDelete: (id) => setDeleteTargetId(id), onAdd: () => setAddOpen(true) }), _jsx("div", { className: "flex-1 overflow-hidden", children: _jsx(PostEditorCanvas, { sections: post.sections, header: header, context: context, selectedId: selectedId, viewport: viewport, onSelect: setSelectedId, onAddSection: () => setAddOpen(true) }) }), selected && (_jsx(SectionInspector, { section: selected, canEdit: canEdit, onClose: () => setSelectedId(null), onContentChange: handleContentChange, onSettingsChange: handleSettingsChange, onMetaChange: handleMetaChange, onToggleVisibility: () => handleToggleVisibility(selected.id) }))] })] }), _jsx(AddSectionModal, { open: addOpen, scope: "post", onClose: () => setAddOpen(false), onAdd: handleAdd }), _jsx(PostFieldsModal, { open: fieldsOpen, post: post, canEdit: canEdit, onClose: () => setFieldsOpen(false), onSave: handleFields }), _jsx(ConfirmDialog, { open: deleteTargetId !== null, onClose: () => setDeleteTargetId(null), onConfirm: confirmDelete, title: "Delete section?", description: "This removes the section from the post. You can undo by leaving without saving.", confirmLabel: "Delete", destructive: true }), _jsx(ConfirmDialog, { open: confirmPublish, onClose: () => setConfirmPublish(false), onConfirm: doPublish, title: "Publish this post?", description: "The current content will go live and replace the published version.", confirmLabel: "Publish" }), _jsx(ConfirmDialog, { open: leaveTarget !== null, onClose: () => setLeaveTarget(null), onConfirm: () => {
                    const target = leaveTarget;
                    setLeaveTarget(null);
                    setDirty(false);
                    if (target)
                        onNavigate(target);
                }, title: "Discard unsaved changes?", description: "You have unsaved changes that will be lost if you leave now.", confirmLabel: "Leave", cancelLabel: "Stay", destructive: true })] }));
}
//# sourceMappingURL=PostSectionEditor.js.map