import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { EditorForm } from "./EditorForm";
import { PageViewerFrame } from "./PageViewerFrame";
import { PerfProfiler } from "../PerfProfiler";
import { ComponentTree } from "../sidebar/ComponentTree";
import { useCallback, useEffect, useRef, useState, } from "react";
import { SimpleIconButton } from "../ui/SimpleIconButton";
import { CloseButton } from "../../components/ui/close-button";
import { useEditContext, useFieldsEditContext } from "../client/editContext";
import { Layers, MessageCircleMore, PanelLeftOpen, StepForward, } from "lucide-react";
import { cn } from "../../lib/utils";
import { Splitter } from "../ui/Splitter";
import { useSlotContext } from "../views/editorSlotContext";
import { localStorageService } from "../services/localStorageService";
import { Comments } from "../reviews/Comments";
import { closeCommentsFeedback } from "../reviews/openCommentsFeedback";
export const SLOT_COMPONENT_NAVIGATOR_DEFAULT_WIDTH = 320;
const SLOT_COMPONENT_NAVIGATOR_MIN_WIDTH = 180;
const COMMENTS_PANEL_DEFAULT_WIDTH = 360;
const COMMENTS_PANEL_MIN_WIDTH = 180;
// Fallback only: the component navigator is normally unmounted when its
// collapse transition ends (event-driven, see handlePanelTransitionEnd). This
// guardrail ensures cleanup even if a transitionend is ever missed.
const COMPONENT_NAVIGATOR_COLLAPSE_SAFETY_MS = 400;
const COMMENTS_PANEL_COLLAPSE_SAFETY_MS = 400;
const EDITOR_FORM_DEFAULT_WIDTH = 320;
const EDITOR_FORM_MIN_WIDTH = 240;
const EDITOR_FORM_MIN_VIEWER_WIDTH = 320;
const EDITOR_FORM_WIDTH_STORAGE_KEY = "editor.pageViewer.formWidth.v1";
function normalizeBridgeId(value) {
    return (value || "").replace(/[{}-]/g, "").toLowerCase();
}
function scrollBridgeFieldIntoView(pageViewContext, field) {
    const geometry = pageViewContext.bridgeGeometry;
    if (!geometry)
        return;
    const target = geometry.targets.find((candidate) => {
        if (candidate.kind !== "field")
            return false;
        return (normalizeBridgeId(candidate.fieldId) ===
            normalizeBridgeId(field.fieldId) &&
            normalizeBridgeId(candidate.item?.id) ===
                normalizeBridgeId(field.item.id) &&
            (!candidate.item?.language ||
                candidate.item.language === field.item.language) &&
            (candidate.item?.version === undefined ||
                candidate.item.version === field.item.version));
    });
    if (!target?.rect)
        return;
    const rectScale = geometry.rectScale ?? 1;
    const scrollScale = typeof geometry.scrollScale === "number" &&
        Number.isFinite(geometry.scrollScale) &&
        geometry.scrollScale > 0
        ? geometry.scrollScale
        : 1;
    const currentScrollY = geometry.scroll.y * scrollScale;
    const documentY = target.rect.top * rectScale + currentScrollY;
    const targetScrollY = documentY -
        geometry.viewport.height / 2 +
        (target.rect.height * rectScale) / 2;
    pageViewContext.requestBridgeScrollBy?.({
        y: targetScrollY - currentScrollY,
        behavior: "smooth",
    });
}
export function PageViewer(props) {
    if (!props.pageViewContext)
        return null;
    return (_jsx(PageViewerContent, { ...props, pageViewContext: props.pageViewContext }));
}
function PageViewerContent({ pageViewContext, showFormEditor, compareView, name, followEditsDefault, className, noMargins, slotCloseButton, }) {
    const editContext = useEditContext();
    const modifiedFieldsContext = useFieldsEditContext();
    const slotContext = useSlotContext();
    const [followEdits, setFollowEdits] = useState(followEditsDefault);
    const [formPanelDefaultWidth] = useState(() => {
        const storedWidth = localStorageService.getItem(EDITOR_FORM_WIDTH_STORAGE_KEY);
        return typeof storedWidth === "number" &&
            Number.isFinite(storedWidth) &&
            storedWidth >= EDITOR_FORM_MIN_WIDTH
            ? storedWidth
            : EDITOR_FORM_DEFAULT_WIDTH;
    });
    const formHidden = editContext?.isEditorFormHiddenForSlot(slotContext?.slotId ?? null) ??
        false;
    // Insert mode always needs the form open so the user can pick a component or
    // configure the new item, even when the user has hidden the panel. The
    // preference itself is preserved, so the form returns to its hidden state as
    // soon as insert mode ends.
    const formEffectivelyHidden = editContext?.mode !== "edit" || (formHidden && !editContext?.insertMode);
    const collapseFormEditor = useCallback(() => {
        if (!editContext)
            return;
        editContext.setEditorFormHiddenForSlot(slotContext?.slotId ?? null, true);
        if (!editContext.editorFormHintSeen) {
            editContext.showEditorFormHint();
        }
    }, [editContext, slotContext?.slotId]);
    useEffect(() => {
        // Clean up legacy per-slot collapse state from older PageViewer implementation.
        localStorageService.removeAllMatchingKeys("editor.pageViewer.slot-");
    }, []);
    useEffect(() => {
        if (followEdits &&
            modifiedFieldsContext?.recentEdits &&
            modifiedFieldsContext.recentEdits.length > 0) {
            const lastEdit = modifiedFieldsContext.recentEdits[modifiedFieldsContext.recentEdits.length - 1];
            if (!lastEdit)
                return;
            if (!lastEdit.user.agentId ||
                lastEdit.user.name !== editContext?.user?.name)
                return;
            // Event-time read: this effect follows recent edits, not selection.
            if (editContext?.getSelection().indexOf(lastEdit.item.id) === -1) {
                editContext.select([lastEdit.item.id]);
            }
            scrollBridgeFieldIntoView(pageViewContext, {
                fieldId: lastEdit.fieldId,
                item: lastEdit.item,
            });
        }
    }, [
        modifiedFieldsContext?.recentEdits,
        editContext,
        followEdits,
        pageViewContext,
    ]);
    const treeItemDescriptor = compareView
        ? (slotContext?.compareTo ?? pageViewContext.page?.item)
        : (slotContext?.itemDescriptor ?? pageViewContext.page?.item);
    const treeItem = compareView ? slotContext?.compareItem : slotContext?.item;
    const treeIsLoading = compareView
        ? !!slotContext?.compareTo && !slotContext?.compareItem
        : (slotContext?.isLoading ?? false);
    const hasLayout = pageViewContext.page?.item?.hasLayout ?? treeItem?.hasLayout ?? false;
    const isPrimaryActiveSlot = (slotContext?.isActive ?? true) && !compareView;
    const showComponentNavigator = !!editContext?.isComponentNavigatorOpenForSlot?.(slotContext?.slotId ?? null) &&
        !editContext?.isMobile &&
        hasLayout;
    const showCommentsPanel = !!editContext?.showComments &&
        !editContext?.isMobile &&
        hasLayout &&
        isPrimaryActiveSlot;
    const [renderComponentNavigator, setRenderComponentNavigator] = useState(showComponentNavigator);
    const [componentNavigatorOpening, setComponentNavigatorOpening] = useState(false);
    const [renderCommentsPanel, setRenderCommentsPanel] = useState(showCommentsPanel);
    const [commentsPanelOpening, setCommentsPanelOpening] = useState(false);
    const componentNavigatorWasOpenRef = useRef(showComponentNavigator);
    const showComponentNavigatorRef = useRef(showComponentNavigator);
    showComponentNavigatorRef.current = showComponentNavigator;
    const commentsPanelWasOpenRef = useRef(showCommentsPanel);
    const showCommentsPanelRef = useRef(showCommentsPanel);
    showCommentsPanelRef.current = showCommentsPanel;
    const handlePanelTransitionEnd = useCallback((panelName) => {
        if (panelName === "form") {
            editContext?.notifySurfaceSettled?.("editor-form");
            return;
        }
        // Event-driven unmount once the navigator finishes collapsing.
        if (panelName === "componentNavigator" &&
            !showComponentNavigatorRef.current) {
            setRenderComponentNavigator(false);
        }
        if (panelName === "commentsFeedback" && !showCommentsPanelRef.current) {
            setRenderCommentsPanel(false);
        }
    }, [editContext]);
    useEffect(() => {
        const wasOpen = componentNavigatorWasOpenRef.current;
        componentNavigatorWasOpenRef.current = showComponentNavigator;
        if (showComponentNavigator) {
            setRenderComponentNavigator(true);
            if (wasOpen) {
                setComponentNavigatorOpening(false);
                return;
            }
            setComponentNavigatorOpening(true);
            const frame = window.requestAnimationFrame(() => {
                setComponentNavigatorOpening(false);
            });
            return () => {
                window.cancelAnimationFrame(frame);
            };
        }
        setComponentNavigatorOpening(false);
        if (!wasOpen) {
            setRenderComponentNavigator(false);
            return;
        }
        // Primary unmount is event-driven (handlePanelTransitionEnd); this timeout is
        // only a guardrail in case the collapse transitionend never arrives.
        const timeout = window.setTimeout(() => {
            setRenderComponentNavigator(false);
        }, COMPONENT_NAVIGATOR_COLLAPSE_SAFETY_MS);
        return () => {
            window.clearTimeout(timeout);
        };
    }, [showComponentNavigator]);
    useEffect(() => {
        const wasOpen = commentsPanelWasOpenRef.current;
        commentsPanelWasOpenRef.current = showCommentsPanel;
        if (showCommentsPanel) {
            setRenderCommentsPanel(true);
            if (wasOpen) {
                setCommentsPanelOpening(false);
                return;
            }
            setCommentsPanelOpening(true);
            const frame = window.requestAnimationFrame(() => {
                setCommentsPanelOpening(false);
            });
            return () => {
                window.cancelAnimationFrame(frame);
            };
        }
        setCommentsPanelOpening(false);
        if (!wasOpen) {
            setRenderCommentsPanel(false);
            return;
        }
        const timeout = window.setTimeout(() => {
            setRenderCommentsPanel(false);
        }, COMMENTS_PANEL_COLLAPSE_SAFETY_MS);
        return () => {
            window.clearTimeout(timeout);
        };
    }, [showCommentsPanel]);
    const hasOuterRightPanel = !!editContext?.showAgentsPanel ||
        !!(editContext?.showHelpTerminal &&
            !editContext?.isMobile &&
            editContext?.currentItemDescriptor);
    const hasRightPanelBeyondComponentNavigator = renderCommentsPanel || hasOuterRightPanel;
    const isVertical = !!editContext?.isMobile;
    const formPanelOpen = showFormEditor && !formEffectivelyHidden;
    const showEditorFormExpandButton = showFormEditor &&
        !formPanelOpen &&
        !editContext?.isMobile &&
        editContext?.mode === "edit" &&
        formHidden &&
        !editContext?.insertMode &&
        isPrimaryActiveSlot;
    const expandEditorForm = useCallback((event) => {
        event.preventDefault();
        event.stopPropagation();
        if (!editContext)
            return;
        editContext.setEditorFormHiddenForSlot(slotContext?.slotId ?? null, false);
        if (editContext.editorFormHintVisible) {
            editContext.dismissEditorFormHint();
        }
    }, [editContext, slotContext?.slotId]);
    const viewerPanels = [];
    if (showFormEditor) {
        viewerPanels.push({
            name: "form",
            defaultSize: formPanelDefaultWidth,
            collapsed: !formPanelOpen,
            animatedCollapse: true,
            preserveVisibilityOnCollapse: true,
            minSize: EDITOR_FORM_MIN_WIDTH,
            content: (_jsx("div", { "data-testid": "editor-form-slider", "aria-hidden": !formPanelOpen, className: cn("h-full w-full", noMargins ? "py-2" : "py-2 pl-2"), children: _jsxs("div", { className: "border-border-default flex h-full w-full flex-col rounded-md border bg-white", children: [_jsx("div", { className: "min-h-0 flex-1", children: _jsx(PerfProfiler, { id: "editor-form", children: _jsx(EditorForm, { pageViewContext: pageViewContext, readonly: editContext?.mode !== "edit", compareView: compareView || false, onCollapse: collapseFormEditor }) }) }), _jsxs("div", { className: "flex items-center justify-between gap-1 p-1", children: [_jsx("div", { className: "text-neutral-grey-50 p-1 text-xs", "aria-live": "polite", role: "status", children: editContext?.statusMessage }), _jsx("div", { className: "flex gap-1", children: _jsx(SimpleIconButton, { "data-testid": "followEditsButton", icon: _jsx(StepForward, { className: "h-4 w-4", strokeWidth: 1 }), selected: followEdits, label: "Follow", onClick: () => setFollowEdits((f) => !f) }) })] })] }) })),
        });
    }
    viewerPanels.push({
        name: "frame",
        defaultSize: "auto",
        minSize: EDITOR_FORM_MIN_VIEWER_WIDTH,
        content: (_jsx(PerfProfiler, { id: "page-viewer-frame", children: _jsx(PageViewerFrame, { compareView: compareView || false, pageViewContext: pageViewContext, slotCloseButton: slotCloseButton, className: cn("py-2", noMargins
                    ? "pr-0"
                    : renderComponentNavigator || renderCommentsPanel
                        ? "pr-0 pl-2"
                        : "px-2", noMargins && formPanelOpen && "ml-2") }) })),
    });
    if (renderComponentNavigator) {
        viewerPanels.push({
            name: "componentNavigator",
            defaultSize: SLOT_COMPONENT_NAVIGATOR_DEFAULT_WIDTH,
            collapsed: !showComponentNavigator || componentNavigatorOpening,
            animatedCollapse: true,
            preserveVisibilityOnCollapse: true,
            minSize: SLOT_COMPONENT_NAVIGATOR_MIN_WIDTH,
            content: (_jsx("div", { "aria-hidden": !showComponentNavigator, className: cn("h-full w-full py-2 pl-2 transition-[opacity,transform] duration-200 ease-in-out motion-reduce:transition-none", showComponentNavigator && !componentNavigatorOpening
                    ? "translate-x-0 opacity-100"
                    : "translate-x-4 opacity-0", !hasRightPanelBeyondComponentNavigator && "pr-2"), children: _jsxs("div", { className: "border-border-default flex h-full w-full flex-col rounded-md border bg-white", "data-testid": "slot-component-navigator", children: [_jsxs("div", { className: "border-border-default text-neutral-grey-100 flex items-center border-b py-2 pr-2 pl-3 text-sm font-medium", children: [_jsx("div", { className: "mr-2 shrink-0", children: _jsx(Layers, { className: "h-4 w-4", strokeWidth: 1 }) }), _jsx("div", { className: "min-w-0 flex-1 truncate", children: "Component Navigator" }), _jsx(CloseButton, { onClick: () => editContext?.setComponentNavigatorOpenForSlot?.(slotContext?.slotId ?? null, false), label: "Close component navigator", className: "ml-2" })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: _jsx(ComponentTree, { pageViewContext: pageViewContext, itemDescriptor: treeItemDescriptor, item: treeItem, isLoading: treeIsLoading }) })] }) })),
        });
    }
    if (renderCommentsPanel) {
        viewerPanels.push({
            name: "commentsFeedback",
            defaultSize: COMMENTS_PANEL_DEFAULT_WIDTH,
            collapsed: !showCommentsPanel || commentsPanelOpening,
            animatedCollapse: true,
            preserveVisibilityOnCollapse: true,
            minSize: COMMENTS_PANEL_MIN_WIDTH,
            content: (_jsx("div", { "aria-hidden": !showCommentsPanel, className: cn("h-full w-full py-2 pl-2 transition-[opacity,transform] duration-200 ease-in-out motion-reduce:transition-none", showCommentsPanel && !commentsPanelOpening
                    ? "translate-x-0 opacity-100"
                    : "translate-x-4 opacity-0", !hasOuterRightPanel && "pr-2"), children: _jsxs("div", { className: "border-border-default flex h-full w-full flex-col rounded-md border bg-white", "data-testid": "comments-suggestions-panel", children: [_jsxs("div", { className: "border-border-default text-neutral-grey-100 flex items-center border-b py-2 pr-2 pl-3 text-sm font-medium", children: [_jsx("div", { className: "mr-2 shrink-0", children: _jsx(MessageCircleMore, { className: "h-4 w-4", strokeWidth: 1 }) }), _jsx("div", { className: "min-w-0 flex-1 truncate", children: "Comments & Suggestions" }), _jsx(CloseButton, { onClick: () => closeCommentsFeedback(editContext), label: "Close comments", className: "ml-2" })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: _jsx(Comments, {}) })] }) })),
        });
    }
    return (_jsxs("div", { className: cn("relative h-full w-full", className), children: [_jsx(Splitter, { panels: viewerPanels, splitterClassName: "bg-transparent -mr-2 md:w-[6px]", direction: isVertical ? "vertical" : "horizontal", localStorageKey: "editor.pageViewer.panels.v2", onPanelTransitionEnd: handlePanelTransitionEnd }), showEditorFormExpandButton && (_jsx(SimpleIconButton, { ref: editContext.editorFormToggleButtonRef, "data-testid": "editor-form-toggle-button", icon: _jsx(PanelLeftOpen, { className: "h-5 w-5", strokeWidth: 1 }), label: "Editor form", selected: false, size: "small", className: cn("border-border-default absolute top-4 left-4 z-50 h-8 w-8 bg-white shadow-sm backdrop-blur-sm hover:bg-white", editContext.editorFormHintVisible &&
                    "ring-highlight-100 animate-pulse ring-2 ring-offset-2"), onMouseDown: (event) => event.stopPropagation(), onClick: expandEditorForm }))] }));
}
//# sourceMappingURL=PageViewer.js.map