import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEditContext, useFieldsEditContext } from "../client/editContext";
import { getComponentById } from "../componentTreeHelper";
import { FieldList } from "../FieldList";
import { ItemInfo } from "../ItemInfo";
import { Insert } from "../sidebar/Insert";
import { useEffect, useRef, useState } from "react";
import { SimpleIconButton } from "../ui/SimpleIconButton";
import { Spinner } from "../ui/Spinner";
import { useDelayedTrue } from "../hooks/useDelayedTrue";
import { SimpleTabs } from "../ui/SimpleTabs";
import { CheckCircle2, ChevronLeft } from "lucide-react";
import { CloseButton } from "../../components/ui/close-button";
import { ComponentTree } from "../sidebar/ComponentTree";
import { useSlotContext } from "../views/editorSlotContext";
import { RenderingParametersSection } from "./RenderingParametersSection";
import { PersonalizationPanel } from "../personalization/PersonalizationPanel";
import { getRulesForComponent } from "../services/personalizationService";
import { Tooltip, TooltipContent, TooltipTrigger, } from "../../components/ui/tooltip";
const LOADING_SPINNER_DELAY_MS = 250;
function normalizePersonalizationRuleId(value) {
    return (value || "").trim().replace(/[{}]/g, "").toLowerCase();
}
/**
 * Keeps the previous non-undefined value during loading transitions.
 * Only updates the stored value when loading is false and value is defined.
 */
function usePreviousWhileLoading(value, isLoading) {
    const prevRef = useRef(value);
    if (!isLoading && value !== undefined) {
        prevRef.current = value;
    }
    return isLoading ? prevRef.current : value;
}
export function EditorForm({ pageViewContext, readonly, compareView, onCollapse, initialActiveTab, }) {
    const editContext = useEditContext();
    const fieldsContext = useFieldsEditContext();
    const slotContext = useSlotContext();
    if (!pageViewContext)
        pageViewContext = slotContext?.primaryPageViewContext;
    if (!pageViewContext)
        return null;
    const insertMode = (editContext.insertMode || editContext.selectedForInsertion) &&
        editContext.mode === "edit" &&
        !compareView &&
        slotContext?.isActive;
    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 [activeTabKey, setActiveTabKey] = useState(initialActiveTab || "content");
    // Switch to the tab requested by the EditContext
    useEffect(() => {
        if (editContext.activeEditorTab) {
            setActiveTabKey(editContext.activeEditorTab);
            // Clear the request after switching
            editContext.setActiveEditorTab(null);
        }
    }, [editContext.activeEditorTab, editContext.setActiveEditorTab]);
    // Set initial active tab if provided
    useEffect(() => {
        if (initialActiveTab) {
            setActiveTabKey(initialActiveTab);
        }
    }, [initialActiveTab]);
    const [item, setItem] = useState();
    const [component, setComponent] = useState();
    const [fullItem, setFullItem] = useState();
    const [componentItemsFullData, setComponentItemsFullData] = useState(new Map());
    const [hasDatasource, setHasDatasource] = useState(true);
    const [personalizationRuleSet, setPersonalizationRuleSet] = useState(undefined);
    const [personalizationLoadKey, setPersonalizationLoadKey] = useState(undefined);
    const [personalizationLoadGen, setPersonalizationLoadGen] = useState(0);
    const [personalizationReloadCounter, setPersonalizationReloadCounter] = useState(0);
    const [personalizationAppliedRuleId, setPersonalizationAppliedRuleId] = useState(undefined);
    // Calculate if this component should be readonly based on layout mode
    const isLayoutComponent = !!component?.layoutId;
    const isLayoutReadonly = isLayoutComponent && !editContext.showLayoutComponents;
    const effectiveReadonly = readonly || isLayoutReadonly;
    const isShared = !!component?.isShared;
    useEffect(() => {
        async function loadFullItem() {
            if (!item)
                return;
            const fullItem = await editContext.itemsRepository.getItem(item);
            setFullItem(fullItem);
        }
        loadFullItem();
    }, [item]);
    useEffect(() => {
        async function loadComponentItemsFullData() {
            if (!component?.items) {
                setComponentItemsFullData(new Map());
                return;
            }
            // Filter out the main item to avoid duplicate loading
            const otherItems = component.items.filter((x) => x.id !== item?.id);
            if (otherItems.length === 0) {
                setComponentItemsFullData(new Map());
                return;
            }
            try {
                const fullItems = await editContext.itemsRepository.getItems(otherItems);
                const fullItemsMap = new Map();
                fullItems.forEach((fullItem) => {
                    const key = `${fullItem.id}-${fullItem.language}-${fullItem.version}`;
                    fullItemsMap.set(key, fullItem);
                });
                setComponentItemsFullData(fullItemsMap);
            }
            catch (error) {
                console.error("Error loading component items full data:", error);
                setComponentItemsFullData(new Map());
            }
        }
        loadComponentItemsFullData();
    }, [component?.items, item?.id]);
    useEffect(() => {
        if (!pageViewContext.page) {
            // Don't clear state if the slot context has the item - the page is likely just temporarily unavailable
            // during a re-render when switching slots. Only clear if we truly don't have any item data.
            if (!slotContext?.item) {
                setComponent(undefined);
                setItem(undefined);
                setHasDatasource(true);
            }
            return;
        }
        let newSelectedItem;
        let selectedComponent;
        if (editContext.selection.length === 1) {
            selectedComponent = getComponentById(editContext.selection[0], pageViewContext.page);
            if (selectedComponent) {
                setComponent(selectedComponent);
                newSelectedItem =
                    selectedComponent?.datasourceItem || selectedComponent?.items?.[0];
            }
            else {
                // The user has a single selection but the freshly-rebuilt page model
                // doesn't contain it yet. This happens during throttled iframe rebuilds
                // where the DOM is briefly mid-update. Falling back to rootComponent
                // here would unmount the Personalize tab and other selection-bound UI,
                // then remount on the next rebuild — causing visible form flicker and
                // refetches. Keep the previous component until the next rebuild.
                return;
            }
        }
        if (!selectedComponent) {
            newSelectedItem = pageViewContext.page.rootComponent.datasourceItem;
            selectedComponent = pageViewContext.page.rootComponent;
            setComponent(selectedComponent);
        }
        // Check if we have a datasource item
        const hasItemDatasource = !!selectedComponent?.datasourceItem;
        const hasSelectedItem = !!newSelectedItem;
        setHasDatasource(hasItemDatasource);
        if (hasSelectedItem) {
            setItem(newSelectedItem);
            // When switching slots, the selected component often falls back to the page root datasource
            // (the page item). That item is already loaded in the surrounding EditorSlotContext,
            // so seed it here to avoid a flicker spinner while the async repository call resolves.
            const slotItem = slotContext?.item;
            const canSeedFromSlot = !!slotItem &&
                !!newSelectedItem &&
                slotItem.id === newSelectedItem.id &&
                slotItem.language === newSelectedItem.language &&
                slotItem.version === newSelectedItem.version;
            if (canSeedFromSlot) {
                setFullItem(slotItem);
            }
            else {
                // Clear previous full item to avoid showing stale content while loading the new item
                const itemChanged = newSelectedItem?.id !== item?.id ||
                    newSelectedItem?.language !== item?.language ||
                    newSelectedItem?.version !== item?.version;
                if (itemChanged) {
                    setFullItem(undefined);
                }
            }
        }
        else {
            // No item to load for this component, clear item state
            setItem(undefined);
            setFullItem(undefined);
        }
    }, [
        editContext.selection,
        pageViewContext.page,
        pageViewContext.pageItemDescriptor,
        slotContext?.item?.id,
        slotContext?.item?.language,
        slotContext?.item?.version,
    ]);
    const supportsPersonalization = !!pageViewContext.componentsProviderSettings?.supportsPersonalization;
    const rootComponentId = pageViewContext.page?.rootComponent?.id;
    const isRootComponent = !!component && component.id === rootComponentId;
    const pageItemDescriptor = pageViewContext.page?.item.descriptor;
    const personalizationComponentId = supportsPersonalization && component && !isRootComponent
        ? component.id
        : undefined;
    const personalizationPageItemId = pageItemDescriptor?.id;
    const personalizationPageItemLanguage = pageItemDescriptor?.language;
    const personalizationPageItemVersion = pageItemDescriptor?.version;
    const targetPersonalizationLoadKey = personalizationComponentId && pageItemDescriptor
        ? `${pageItemDescriptor.id}|${pageItemDescriptor.language || ""}|${pageItemDescriptor.version || ""}|${personalizationComponentId}`
        : undefined;
    useEffect(() => {
        if (!personalizationComponentId || !pageItemDescriptor) {
            setPersonalizationRuleSet(undefined);
            setPersonalizationLoadKey(undefined);
            setPersonalizationAppliedRuleId(undefined);
            return;
        }
        let cancelled = false;
        const controller = new AbortController();
        const targetKey = targetPersonalizationLoadKey;
        void getRulesForComponent(pageItemDescriptor, personalizationComponentId, editContext.sessionId, controller.signal)
            .then((ruleSet) => {
            if (cancelled)
                return;
            const rules = ruleSet.rules || [];
            const normalizedActiveRuleId = normalizePersonalizationRuleId(ruleSet.activeRuleId);
            const activeRuleId = rules.find((rule) => normalizePersonalizationRuleId(rule.uid) === normalizedActiveRuleId)?.uid;
            setPersonalizationRuleSet({ ...ruleSet, rules });
            setPersonalizationLoadKey(targetKey);
            setPersonalizationLoadGen((gen) => gen + 1);
            setPersonalizationAppliedRuleId(activeRuleId);
        })
            .catch(() => {
            if (cancelled)
                return;
            setPersonalizationRuleSet(undefined);
            setPersonalizationLoadKey(undefined);
            setPersonalizationAppliedRuleId(undefined);
        });
        return () => {
            cancelled = true;
            controller.abort();
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [
        personalizationComponentId,
        personalizationPageItemId,
        personalizationPageItemLanguage,
        personalizationPageItemVersion,
        editContext.sessionId,
        personalizationReloadCounter,
    ]);
    // Keep fullItem in sync when slot item properties (like name/path) change for
    // the same descriptor. Rename updates properties without changing id/lang/version.
    useEffect(() => {
        const slotItem = slotContext?.item;
        if (!slotItem || !item)
            return;
        const sameDescriptor = slotItem.id === item.id &&
            slotItem.language === item.language &&
            slotItem.version === item.version;
        if (!sameDescriptor)
            return;
        setFullItem((prev) => {
            if (prev &&
                prev.id === slotItem.id &&
                prev.language === slotItem.language &&
                prev.version === slotItem.version &&
                prev.name === slotItem.name &&
                prev.displayName === slotItem.displayName &&
                prev.path === slotItem.path) {
                return prev;
            }
            return slotItem;
        });
    }, [
        slotContext?.item?.id,
        slotContext?.item?.language,
        slotContext?.item?.version,
        slotContext?.item?.name,
        slotContext?.item?.displayName,
        slotContext?.item?.path,
        item?.id,
        item?.language,
        item?.version,
    ]);
    // Show spinner when we have a datasource but haven't loaded the full item yet,
    // or when we have component items but haven't loaded their full data yet
    const hasComponentItemsToLoad = component?.items &&
        component.items.filter((x) => x.id !== item?.id).length > 0;
    const isLoadingComponentItems = hasComponentItemsToLoad && componentItemsFullData.size === 0;
    const isPanelContextLoading = !!slotContext?.isLoading ||
        !pageViewContext.page ||
        !component ||
        isLoadingComponentItems;
    // Only treat the full item as loaded when it matches the selected item
    const fullItemMatchesSelection = !!fullItem &&
        !!item &&
        fullItem.id === item.id &&
        fullItem.language === item.language &&
        fullItem.version === item.version;
    // Keep showing a spinner only after delay has elapsed
    const isLoadingItem = !!item && !fullItemMatchesSelection;
    const showSpinner = useDelayedTrue(isLoadingItem, LOADING_SPINNER_DELAY_MS);
    // Keep previous item visible during fast loads to avoid flicker
    const displayFullItem = usePreviousWhileLoading(fullItemMatchesSelection ? fullItem : undefined, isLoadingItem && !showSpinner);
    // Show spinner only after delay if we have a selected item to load
    if (item && !displayFullItem) {
        return (_jsx("div", { className: "grid h-full w-full items-center justify-center", children: showSpinner ? _jsx(Spinner, {}) : null }));
    }
    const validators = editContext.validationResult?.find((x) => x.item.id === item?.id &&
        x.item.language === item?.language &&
        x.item.version === item?.version)?.results || [];
    let designFields = Object.values(displayFullItem?.fields || []).filter((x) => x.section === "Design" || x.section === "Rendering");
    const editorFields = component?.editorFields;
    if (editorFields && editorFields["Design"]) {
        editorFields["Design"]?.addFields.forEach((fieldName) => {
            const field = displayFullItem?.fields.find((x) => x.name === fieldName);
            if (field)
                designFields.push(field);
        });
        editorFields["Design"]?.removeFields.forEach((fieldName) => {
            designFields = designFields.filter((x) => x.name !== fieldName);
        });
    }
    const itemFields = [];
    let contentFields = [];
    if (item && displayFullItem) {
        contentFields = Object.values(displayFullItem?.fields || []).filter((x) => ((editorFields &&
            editorFields["Content"]?.addFields.includes(x.name)) ||
            item.renderedFieldIds.indexOf(x.id) >= 0) &&
            !(editorFields &&
                editorFields["Content"]?.removeFields.includes(x.name)));
        if (contentFields.length > 0)
            itemFields.push({ fields: contentFields });
    }
    const hiddenContentFieldCount = displayFullItem?.fields.filter((field) => !field.isStandardValue &&
        !(field.name || "").startsWith("_") &&
        !contentFields.some((contentField) => contentField.id === field.id)).length || 0;
    // Helper function to shorten paths relative to page item
    const getRelativePath = (itemPath) => {
        const pageItemPath = pageViewContext.page?.item.path;
        if (pageItemPath && itemPath.startsWith(pageItemPath)) {
            const relativePath = itemPath.substring(pageItemPath.length);
            return {
                path: `.${relativePath}`,
                fullPath: itemPath, // Keep original for tooltip
            };
        }
        return { path: itemPath, fullPath: undefined };
    };
    // Add fields from other component items (properly loaded)
    component?.items
        .filter((x) => x.id !== item?.id)
        .forEach((componentItem) => {
        const fields = [];
        const itemKey = `${componentItem.id}-${componentItem.language}-${componentItem.version}`;
        const componentItemFullData = componentItemsFullData.get(itemKey);
        if (componentItemFullData) {
            componentItem.renderedFieldIds.forEach((fieldId) => {
                const field = componentItemFullData.fields.find((x) => x.id === fieldId);
                if (field)
                    fields.push(field);
            });
            if (fields.length > 0) {
                const isPageItem = componentItemFullData.id === pageViewContext.page?.item.id;
                let pathInfo = {
                    path: undefined,
                    fullPath: undefined,
                };
                if (!isPageItem) {
                    pathInfo = getRelativePath(componentItemFullData.path);
                }
                itemFields.push({
                    headline: componentItem.name,
                    path: pathInfo.path,
                    fullPath: pathInfo.fullPath,
                    icon: componentItemFullData.icon,
                    fields,
                });
            }
        }
    });
    const getFieldsTab = (fields, headline, id) => {
        return {
            label: headline,
            content: (_jsx("div", { className: "relative h-full", children: _jsx("div", { className: "absolute inset-0 overflow-auto", children: _jsx(FieldList, { fields: fields, validators: validators, simplified: true, readonly: effectiveReadonly }) }) })),
            id,
        };
    };
    const hasRenderingParameters = !!component?.parametersTemplateId;
    const tabPanels = [];
    if (itemFields.length > 0)
        tabPanels.push({
            label: "Content",
            content: (_jsx("div", { className: "relative h-full", children: _jsxs("div", { className: "absolute inset-0 overflow-auto", children: [_jsx(FieldList, { fields: itemFields, validators: validators, simplified: true, readonly: effectiveReadonly }), hiddenContentFieldCount > 0 && (_jsxs("div", { className: "text-2xs border-border-default text-neutral-grey-50 mt-4 px-3 py-2", children: ["There ", hiddenContentFieldCount === 1 ? "is" : "are", " ", hiddenContentFieldCount, " more field", hiddenContentFieldCount === 1 ? "" : "s", " on this item.", " ", _jsx("button", { type: "button", className: "font-medium underline hover:no-underline", onClick: () => setActiveTabKey("advanced"), children: "View all fields in Advanced" }), "."] }))] }) })),
            id: "content",
        });
    //TODO: generalize thisdata-field-id
    if (editorFields) {
        Object.keys(editorFields).forEach((key) => {
            if (key !== "Content" && key !== "Design") {
                const fields = displayFullItem?.fields.filter((x) => editorFields[key]?.addFields.includes(x.name));
                if (fields?.length)
                    tabPanels.push(getFieldsTab([{ fields }], key, key));
            }
        });
    }
    if (designFields?.length || hasRenderingParameters)
        tabPanels.push({
            label: "Design",
            testId: "design-tab-button",
            content: (_jsx("div", { className: "relative h-full", children: _jsxs("div", { className: "absolute inset-0 overflow-auto", children: [designFields?.length > 0 && (_jsx(FieldList, { fields: [{ fields: designFields }], validators: validators, simplified: true, readonly: effectiveReadonly })), _jsx(RenderingParametersSection, { component: component, pageItem: pageViewContext.page?.item.descriptor, readonly: effectiveReadonly })] }) })),
            id: "design",
        });
    if (supportsPersonalization && component && !isRootComponent) {
        {
            const ruleCount = personalizationLoadKey === targetPersonalizationLoadKey
                ? (personalizationRuleSet?.rules.length ?? 0)
                : 0;
            const hasAppliedRule = ruleCount > 0 &&
                !!personalizationAppliedRuleId &&
                !!personalizationRuleSet?.rules.some((rule) => normalizePersonalizationRuleId(rule.uid) ===
                    normalizePersonalizationRuleId(personalizationAppliedRuleId));
            tabPanels.push({
                label: "Personalize",
                testId: "personalize-tab-button",
                badge: ruleCount > 0 || hasAppliedRule ? (_jsxs("span", { className: "inline-flex items-center gap-1", children: [hasAppliedRule && (_jsx("span", { className: "bg-feedback-blue-light text-component-default inline-flex size-4 items-center justify-center rounded-full", "data-testid": "personalize-selected-rule-indicator", title: "Personalization rule selected", "aria-label": "Personalization rule selected", children: _jsx(CheckCircle2, { className: "size-3" }) })), ruleCount > 0 && (_jsx("span", { className: "text-2xs bg-highlight-100 badge-pad-sm inline-flex min-w-4 items-center justify-center rounded-full leading-none text-white", "data-testid": "personalize-rule-count", children: ruleCount }))] })) : undefined,
                content: (_jsx("div", { className: "relative h-full", children: _jsx("div", { className: "absolute inset-0 overflow-auto", children: _jsx(PersonalizationPanel, { component: component, pageItem: pageViewContext.page?.item.descriptor, readonly: effectiveReadonly, ruleSet: personalizationRuleSet, ruleSetForKey: personalizationLoadKey, ruleSetLoadGen: personalizationLoadGen, appliedRuleId: personalizationAppliedRuleId, onAppliedRuleChange: setPersonalizationAppliedRuleId, onRuleSetChange: (update) => setPersonalizationRuleSet((prev) => update(prev ?? { rules: [] })), onExternalReload: () => setPersonalizationReloadCounter((c) => c + 1) }) }) })),
                id: "personalize",
            });
        }
    }
    if (hasDatasource && displayFullItem)
        tabPanels.push({
            label: "Advanced",
            testId: "advanced-tab-button",
            content: (_jsx("div", { className: "relative h-full", children: _jsxs("div", { className: "absolute inset-0 overflow-auto", children: [displayFullItem && _jsx(ItemInfo, { item: displayFullItem }), _jsx(FieldList, { fields: [{ fields: displayFullItem?.fields || [] }], validators: validators, showStandardFieldsEnabled: true, readonly: effectiveReadonly })] }) })),
            id: "advanced",
        });
    if (!item && !isPanelContextLoading) {
        tabPanels.push({
            label: "Content",
            content: (_jsx("div", { className: "text-neutral-grey-50 flex h-full items-center justify-center p-4 text-center text-xs", children: "This rendering has no datasource and no rendered editable fields." })),
            id: "content",
        });
    }
    // Component tree tab
    tabPanels.push({
        label: "Components",
        content: (_jsx("div", { className: "relative h-full", children: _jsx("div", { className: "absolute inset-0 overflow-auto", children: _jsx(ComponentTree, { pageViewContext: pageViewContext, itemDescriptor: treeItemDescriptor, item: treeItem, isLoading: treeIsLoading }) }) })),
        id: "tree",
    });
    return (_jsxs("div", { className: "flex h-full flex-col", "data-testid": "editor-sidepanel", onClickCapture: (e) => {
            const target = e.target;
            const inField = target.closest("[data-field-id]");
            if (!inField) {
                fieldsContext?.setFocusedField(undefined, false);
            }
        }, children: [_jsxs("h1", { className: "border-border-default flex h-12 items-center justify-center border-b p-2", children: [component && !isRootComponent && (_jsx(SimpleIconButton, { icon: _jsx(ChevronLeft, { className: "h-5 w-5", strokeWidth: 1 }), onClick: () => editContext.select([]), label: "Back to page", size: "small", "data-testid": "back-to-page-button" })), editContext.selection.length === 0 && _jsx("div", { className: "w-12" }), _jsxs("div", { className: "text-neutral-grey-100 flex min-w-0 flex-1 items-center justify-center text-center", id: "item-name", "data-testid": "component-name", children: [_jsxs("div", { className: "flex max-w-full min-w-0 flex-col items-center", children: [_jsx("div", { className: "max-w-full truncate text-sm font-medium", title: component?.name, children: insertMode ? "Add Component" : component?.name }), !insertMode &&
                                        component?.type &&
                                        component.type !== component.name && (_jsx("div", { className: "text-2xs text-neutral-grey-50", children: component.type }))] }), !insertMode && isShared && (_jsx("span", { className: "text-2xs bg-component-shared ml-2 rounded-md p-1 text-white", children: "Shared" })), !insertMode &&
                                isLayoutComponent &&
                                (isLayoutReadonly && !readonly ? (_jsxs(Tooltip, { delayDuration: 500, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "text-2xs bg-highlight-100 ml-2 rounded-md p-1 text-white", children: "Layout" }) }), _jsx(TooltipContent, { children: "Layout editing is disabled" })] })) : (_jsx("span", { className: "text-2xs bg-highlight-100 ml-2 rounded-md p-1 text-white", children: "Layout" })))] }), _jsx(CloseButton, { "data-testid": "close-editor-form-button", onClick: (e) => {
                            e.stopPropagation();
                            if (insertMode) {
                                editContext.setInsertMode(false);
                            }
                            else {
                                onCollapse?.();
                            }
                        }, label: "Close", showTooltip: false })] }), insertMode && _jsx(Insert, {}), !insertMode && (_jsx("div", { className: "flex flex-1 flex-col", children: _jsx(SimpleTabs, { tabs: tabPanels, className: "border-border-default flex items-center border-b pt-1", activeTab: Math.max(0, tabPanels.findIndex((x) => x.id === activeTabKey)), setActiveTab: (index) => setActiveTabKey(tabPanels[index]?.id) }, "editor-tabs") }))] }));
}
//# sourceMappingURL=EditorForm.js.map