import { useCallback, useEffect, useRef, useState } from "react";
import { usePageModel } from "../client/pageModelBuilder";
import { useDebouncedCallback } from "use-debounce";
import { resolvePageAndSite, } from "../services/contentService";
// Custom event name for device changes - used by ViewportControls to trigger re-renders
export const DEVICE_CHANGE_EVENT = "parhelia:device-change";
function dispatchViewportChange() {
    if (typeof window === "undefined")
        return;
    window.dispatchEvent(new CustomEvent(DEVICE_CHANGE_EVENT));
}
export function useViewportChangeSignal() {
    const [, setViewportChangeCounter] = useState(0);
    useEffect(() => {
        const handler = () => {
            setViewportChangeCounter((x) => x + 1);
        };
        window.addEventListener(DEVICE_CHANGE_EVENT, handler);
        return () => window.removeEventListener(DEVICE_CHANGE_EVENT, handler);
    }, []);
}
export function usePageViewContext({ pageItemDescriptor, itemsRepository, configuration, parheliaSettings, layoutMode = "final", }) {
    const itemsRepositoryRef = useRef(itemsRepository);
    const [latestVersionSignal, setLatestVersionSignal] = useState(0);
    const [device, setDeviceInternal] = useState("desktop");
    const [zoom, setZoomInternal] = useState(1);
    const [scroll, setScroll] = useState(0);
    const [fullscreen, setFullscreen] = useState(false);
    const [editorIframe, setEditorIframe] = useState(null);
    const [deviceWidth, setDeviceWidthInternal] = useState();
    const [deviceHeight, setDeviceHeightInternal] = useState();
    const [lockHeight, setLockHeightInternal] = useState(true);
    const [rotate, setRotateInternal] = useState(false);
    const [viewportRect, setViewportRect] = useState({ width: 0, height: 0 });
    const [site, setSite] = useState();
    const [resolvedPageItemDescriptor, setResolvedPageItemDescriptor] = useState();
    const [editUrl, setEditUrl] = useState();
    const [previewUrl, setPreviewUrl] = useState();
    const [componentsProviderSettings, setComponentsProviderSettings] = useState();
    // The pageViewContext is a stable ref, so viewport-affecting setters must emit
    // an event that dependent components can subscribe to.
    const setDevice = useCallback((value) => {
        setDeviceInternal(value);
        setTimeout(() => {
            dispatchViewportChange();
        }, 0);
    }, []);
    const setZoom = useCallback((value) => {
        setZoomInternal(value);
        setTimeout(() => {
            dispatchViewportChange();
        }, 0);
    }, []);
    const setDeviceWidth = useCallback((value) => {
        setDeviceWidthInternal(value);
        setTimeout(() => {
            dispatchViewportChange();
        }, 0);
    }, []);
    const setDeviceHeight = useCallback((value) => {
        setDeviceHeightInternal(value);
        setTimeout(() => {
            dispatchViewportChange();
        }, 0);
    }, []);
    const setLockHeight = useCallback((value) => {
        setLockHeightInternal(value);
        setTimeout(() => {
            dispatchViewportChange();
        }, 0);
    }, []);
    const setRotate = useCallback((value) => {
        setRotateInternal(value);
        setTimeout(() => {
            dispatchViewportChange();
        }, 0);
    }, []);
    const { page, setPageSkeleton } = usePageModel(itemsRepository, pageItemDescriptor, parheliaSettings, layoutMode);
    useEffect(() => {
        itemsRepositoryRef.current = itemsRepository;
    }, [itemsRepository]);
    // When viewing "Latest" (version 0), we must re-resolve the concrete version
    // when a new version is added. We trigger this via a custom event dispatched from the
    // websocket handler, instead of depending on itemsRepository.revision (which can tick while typing).
    useEffect(() => {
        const handler = (e) => {
            const detail = e?.detail ?? {};
            const itemId = detail?.itemId ?? null;
            const language = detail?.language ?? null;
            const matchesItem = !!pageItemDescriptor?.id && itemId === pageItemDescriptor.id;
            const matchesLang = !language ||
                language === pageItemDescriptor?.language ||
                !pageItemDescriptor?.language;
            const isLatest = pageItemDescriptor?.version === 0;
            if (matchesItem && matchesLang && isLatest) {
                setLatestVersionSignal((x) => x + 1);
            }
        };
        window.addEventListener("parhelia:item-version-added", handler);
        return () => {
            window.removeEventListener("parhelia:item-version-added", handler);
        };
    }, [
        pageItemDescriptor?.id,
        pageItemDescriptor?.language,
        pageItemDescriptor?.version,
    ]);
    useEffect(() => {
        var deviceConfig = configuration?.devices.find((x) => x.name === device);
        let width = deviceConfig?.width;
        let height = deviceConfig?.height;
        if (rotate) {
            [width, height] = [height, width];
        }
        if (device) {
            setDeviceWidth(width);
            setDeviceHeight(lockHeight ? height : undefined);
        }
    }, [rotate, device, lockHeight, configuration]);
    useEffect(() => {
        updateViewportRect();
    }, [fullscreen, device, deviceHeight, deviceWidth]);
    const updateViewportRectDebounced = useDebouncedCallback(() => {
        updateViewportRect();
    }, 100);
    useEffect(() => {
        const resizeObserver = new ResizeObserver(() => {
            updateViewportRectDebounced();
        });
        if (editorIframe) {
            resizeObserver.observe(editorIframe);
        }
        // Cleanup function
        return () => {
            resizeObserver.disconnect();
        };
    }, [editorIframe, updateViewportRectDebounced]);
    useEffect(() => {
        if (!pageItemDescriptor)
            return;
        async function resolvePage() {
            if (!pageItemDescriptor) {
                setSite(undefined);
                setResolvedPageItemDescriptor(undefined);
                setPageSkeleton(undefined);
                setEditUrl(undefined);
                setPreviewUrl(undefined);
                setComponentsProviderSettings(undefined);
                return;
            }
            const repo = itemsRepositoryRef.current;
            if (!repo)
                return;
            const item = await repo.getItem(pageItemDescriptor);
            if (!item?.hasLayout) {
                setSite(undefined);
                setResolvedPageItemDescriptor(undefined);
                setPageSkeleton(undefined);
                setEditUrl(undefined);
                setPreviewUrl(undefined);
                setComponentsProviderSettings(undefined);
                return;
            }
            const result = await resolvePageAndSite(pageItemDescriptor.id, pageItemDescriptor.language, pageItemDescriptor.version);
            if (result) {
                setSite(result.site);
                setResolvedPageItemDescriptor(result.pageItem);
                setEditUrl(result.editUrl);
                setPreviewUrl(result.previewUrl);
                setComponentsProviderSettings(result.componentsProviderSettings);
            }
        }
        resolvePage();
        // IMPORTANT:
        // Do NOT re-resolve page/site on every repository revision bump.
        // Repo revisions can tick frequently during inline typing (field refresh/save acks),
        // and re-resolving updates editUrl/previewUrl which triggers iframe refreshes and caret jumps.
    }, [
        pageItemDescriptor?.id,
        pageItemDescriptor?.language,
        pageItemDescriptor?.version,
        // Run once repository becomes available, but do not re-run when its identity changes.
        // (The repo object is recreated when its internal `revision` state changes.)
        !!itemsRepository,
        // Re-resolve when a new version is added while viewing "Latest".
        latestVersionSignal,
    ]);
    function updateViewportRect() {
        const rect = editorIframe?.getBoundingClientRect();
        if (rect) {
            setViewportRect((prev) => {
                // Only update if dimensions actually changed
                if (prev.width !== rect.width || prev.height !== rect.height) {
                    return { width: rect.width, height: rect.height };
                }
                return prev;
            });
        }
    }
    // Return a stable object identity so consumers can safely store/register it
    // without triggering ref attach/detach loops when internal values change.
    const pageViewContextRef = useRef(null);
    if (!pageViewContextRef.current) {
        pageViewContextRef.current = {
            device,
            setDevice,
            deviceWidth,
            setDeviceWidth,
            deviceHeight,
            setDeviceHeight,
            lockHeight,
            setLockHeight,
            scroll,
            setScroll,
            zoom,
            setZoom,
            fullscreen,
            setFullscreen,
            editorIframe,
            setEditorIframe,
            rotate,
            setRotate,
            page,
            setPageSkeleton,
            viewport: viewportRect,
            pageItemDescriptor: resolvedPageItemDescriptor,
            site,
            editUrl,
            previewUrl,
            componentsProviderSettings,
        };
    }
    else {
        pageViewContextRef.current.device = device;
        pageViewContextRef.current.deviceWidth = deviceWidth;
        pageViewContextRef.current.deviceHeight = deviceHeight;
        pageViewContextRef.current.lockHeight = lockHeight;
        pageViewContextRef.current.scroll = scroll;
        pageViewContextRef.current.zoom = zoom;
        pageViewContextRef.current.fullscreen = fullscreen;
        pageViewContextRef.current.editorIframe = editorIframe;
        pageViewContextRef.current.rotate = rotate;
        pageViewContextRef.current.page = page;
        pageViewContextRef.current.viewport = viewportRect;
        pageViewContextRef.current.pageItemDescriptor = resolvedPageItemDescriptor;
        pageViewContextRef.current.site = site;
        pageViewContextRef.current.editUrl = editUrl;
        pageViewContextRef.current.previewUrl = previewUrl;
        pageViewContextRef.current.componentsProviderSettings =
            componentsProviderSettings;
        // setters are stable; keep initial references
    }
    return pageViewContextRef.current;
}
//# sourceMappingURL=pageViewContext.js.map