import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
/* eslint-disable fp/no-let */
import { Box, Center, Flex, Text, Image, VStack, FormControl, FormLabel, Input, Spinner, Link, Button, useDisclosure, Icon, Modal, ModalHeader, ModalBody, ModalFooter, ModalContent, ModalOverlay, Stack, Skeleton, } from "@chakra-ui/react";
import { css } from "@emotion/react";
import { ChevronCheck, Close } from "@feedloop/icon";
import produce from "immer";
import React from "react";
import { buildComponent, getComponent, isComponent, } from "@qorebase/app-runtime/src/interfaces";
import _ from "lodash";
import { RuntimeContext } from "@qorebase/app-runtime/src/RuntimeContext";
import EditorContext from "@qorebase/app-runtime/src/EditorContext";
import PageMenu from "./PageMenuItem";
import { useForm } from "react-hook-form";
import { importCustomComponent } from "@qorebase/app-runtime/src/utils/importComponent";
import PageMenuTree from "./PageMenuTree";
import { usePageParam } from "@qorebase/app-runtime/src/hooks/usePageParam";
const ModalLayout = (props) => (_jsxs(Modal, Object.assign({ isOpen: props.isOpen, onClose: props.onClose, isCentered: true }, { children: [_jsx(ModalOverlay, {}), _jsx(ModalContent, Object.assign({ w: "408px", h: "448px", p: "40px" }, { children: _jsxs(ModalBody, { children: [_jsx(Center, { children: _jsx(Image, { src: "/assets/ilustration/dekstop.svg", alt: "Desktop" }) }), _jsx(Center, { children: _jsx(Text, Object.assign({ css: css `
              font-weight: 600;
              font-size: 24px;
              color: #001e4d;
            ` }, { children: "Only For Dekstop" })) }), _jsx(Center, Object.assign({ pt: 2 }, { children: _jsx(Text, Object.assign({ css: css `
              font-weight: normal;
              font-size: 12px;
              color: #6f809a;
              text-align: center;
            ` }, { children: "This component only for dekstop, change to dekstop mode to use this component" })) })), _jsx(Center, Object.assign({ pt: 4 }, { children: _jsx(Button, Object.assign({ w: "328px", css: css `
              background: #0065ff;
              color: #fff;
            ` }, { children: "Go to Desktop Mode" })) })), _jsx(Center, Object.assign({ pt: 2 }, { children: _jsx(Button, Object.assign({ variant: "ghost", w: "328px", onClick: props.onClose }, { children: "Cancel" })) }))] }) }))] })));
export const bannedComponents = new Set([
    "ColumnLayout2",
    "ColumnLayout3",
    "Gallery",
    "Menu Grid",
]);
const rootData = {
    id: "ROOT",
    name: "ROOT",
    isOpen: true,
    children: [],
};
export default React.memo(function ComponentsManager() {
    const ready = EditorContext.useSelectState((state) => state.ready);
    const pageID = usePageParam();
    const maps = RuntimeContext.useSelectState(({ maps }) => maps);
    const runtimeActions = RuntimeContext.useActions();
    const runtimeBatchActions = RuntimeContext.useBatchActions();
    const editorActions = EditorContext.useActions();
    const form = useForm({
        defaultValues: {
            name: "",
            url: "",
        },
    });
    const [modalCustomComponent, setModalCustomComponent] = React.useState(null);
    const activeComponent = EditorContext.useSelectState(({ activeComponent }) => activeComponent);
    const [state, setState] = React.useState({
        componentBeingEdit: undefined,
        addComponentMenu: {
            isOpen: false,
            isOpenByComponent: false,
            layoutID: null,
            layoutIdx: null,
        },
    });
    const page = React.useMemo(() => maps.page[pageID], [maps.page, pageID]);
    const components = React.useMemo(() => Iterator.from((page === null || page === void 0 ? void 0 : page.components) || [])
        .map((ref) => maps.component[ref.component])
        .filter((x) => !!x)
        .toArray(), [page === null || page === void 0 ? void 0 : page.components, maps.component]);
    const parentComponents = React.useMemo(() => {
        var _a;
        return (_a = page === null || page === void 0 ? void 0 : page.components) === null || _a === void 0 ? void 0 : _a.filter((component) => {
            const comp = components.find((cmp) => cmp.id === component.component);
            return (comp &&
                (!comp.metadata ||
                    (!comp.metadata.layout && !comp.metadata.container)));
        });
    }, [page === null || page === void 0 ? void 0 : page.components, components]);
    const childrenMap = React.useMemo(() => _.groupBy(components, "metadata.container"), [components]);
    const groupingComponents = React.useMemo(() => {
        const groupedComponents = {
            layer0: [],
            layer1: {},
        };
        components.forEach((component) => {
            if (!!component.metadata &&
                !!component.metadata.layout &&
                !!component.metadata.layout.id) {
                groupedComponents.layer1[component.metadata.layout.id] = [
                    ...(groupedComponents.layer1[component.metadata.layout.id] || []),
                    component,
                ];
            }
            else {
                groupedComponents.layer0.push(component);
            }
        });
        return groupedComponents;
    }, [components]);
    const childComponent = React.useCallback((childrenComp, compId) => {
        var _a;
        const parent = components.find((cmp) => cmp.id === compId);
        const childrenList = childrenComp[compId];
        return Object.assign(Object.assign(Object.assign({}, parent), { isOpen: childrenList ? true : false, name: (_a = parent.name) !== null && _a !== void 0 ? _a : parent.componentID }), ((parent.kind === "layout" || parent.kind === "container") && {
            children: _.sortBy(childrenList, ["metadata.idx"]).map((child) => childComponent(childrenComp, child.id)) || [],
        }));
    }, [components]);
    const groupedComponents = React.useMemo(() => {
        if (!components.length || !page.components || !parentComponents)
            return;
        const groupedComps = parentComponents.map((comp) => {
            var _a, _b;
            const childrenList = childrenMap[comp.component];
            const parent = components.find((cmp) => cmp.id === comp.component);
            return Object.assign(Object.assign(Object.assign({}, parent), { isOpen: childrenList ? true : false, name: (_b = (_a = getComponent(comp.component)) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : comp.component }), ((parent.kind === "layout" || parent.kind === "container") && {
                children: _.sortBy(childrenList, ["metadata.idx"]).map((child) => childComponent(childrenMap, child.id)) || [],
            }));
        });
        return Object.assign(Object.assign({}, rootData), { children: _.sortBy(groupedComps, ["metadata.idx"]) });
    }, [components, page === null || page === void 0 ? void 0 : page.components, parentComponents, childrenMap]);
    React.useEffect(() => {
        var _a, _b, _c;
        if (((_a = page === null || page === void 0 ? void 0 : page.components) === null || _a === void 0 ? void 0 : _a.find((ref) => ref.component === activeComponent)) ===
            undefined) {
            editorActions.setActiveComponent((_c = (_b = page === null || page === void 0 ? void 0 : page.components) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.component);
        }
    }, [page === null || page === void 0 ? void 0 : page.components, activeComponent]);
    const getNewDestination = React.useCallback((destinationIndex, draggableId) => {
        const newDestinationFilter = groupingComponents.layer0.filter((comp, idx) => idx < destinationIndex);
        const newDestination = (newDestinationFilter || []).reduce((newDestination, current) => (isComponent(current, "ColumnLayout2") ||
            isComponent(current, "ColumnLayout3")) &&
            !!groupingComponents.layer1[current.id] &&
            draggableId !== current.id
            ? newDestination + groupingComponents.layer1[current.id].length + 1
            : newDestination + 1, 0);
        return newDestination;
    }, [groupingComponents]);
    const handleAddComponent = React.useCallback((presetID, name, componentURL) => {
        var _a;
        if (presetID === "Custom Component") {
            setModalCustomComponent("register");
            return;
        }
        const compInstance = buildComponent(presetID, {
            metadata: {
                name,
                layout: state.addComponentMenu.isOpenByComponent &&
                    !!state.addComponentMenu.layoutID
                    ? { id: state.addComponentMenu.layoutID, column: 1 }
                    : undefined,
                componentURL,
            },
        });
        if (state.addComponentMenu.isOpenByComponent) {
            const layoutIdx = getNewDestination(state.addComponentMenu.layoutIdx, state.addComponentMenu.layoutID);
            const idx = layoutIdx +
                1 +
                (((_a = groupingComponents.layer1[state.addComponentMenu.layoutID]) === null || _a === void 0 ? void 0 : _a.length) ||
                    0);
            runtimeActions.addComponent(compInstance, { page: pageID }, { idx: idx });
        }
        else {
            runtimeActions.addComponent(compInstance, { page: pageID });
        }
    }, [pageID, state.addComponentMenu, getNewDestination, groupingComponents]);
    const handleDragEnd = React.useCallback((result) => {
        var _a;
        if (!result.destination || !page)
            return;
        const newDestination = getNewDestination(result.destination.index, result.draggableId);
        const newSourceIndex = components.findIndex((comp) => comp.id === result.draggableId);
        const deletionLength = (((_a = groupingComponents.layer1[result.draggableId]) === null || _a === void 0 ? void 0 : _a.length) || 0) + 1;
        runtimeActions.swapComponent({ page: page.id }, newSourceIndex, newDestination, deletionLength);
    }, [runtimeActions, page, groupingComponents, getNewDestination, components]);
    const handleOnDrop = React.useCallback((srcId, dstParentId, dstIndex) => {
        const parentId = dstParentId === "ROOT" ? undefined : dstParentId;
        const listComponents = _.sortBy(childrenMap[parentId], "metadata.idx");
        const clonedListComponents = _.cloneDeep(listComponents);
        const sourceIndex = listComponents.findIndex((comp) => comp.id === srcId);
        if ((sourceIndex === dstIndex || sourceIndex === dstIndex - 1) &&
            sourceIndex >= 0 &&
            parentId === listComponents[sourceIndex].metadata.container)
            return;
        let moveComponents = [];
        if (sourceIndex >= 0) {
            const removed = clonedListComponents.splice(sourceIndex, 1);
            removed &&
                clonedListComponents.splice(sourceIndex > dstIndex || dstIndex === 0 ? dstIndex : dstIndex - 1, 0, ...removed);
        }
        else {
            const component = _.cloneDeep(components.find((comp) => comp.id === srcId));
            component.metadata = Object.assign(Object.assign({}, component.metadata), { container: parentId });
            clonedListComponents.splice(dstIndex, 0, component);
        }
        moveComponents = clonedListComponents.map((comp, idx) => (Object.assign(Object.assign({}, comp), { metadata: Object.assign(Object.assign({}, comp.metadata), { idx }) })));
        runtimeActions.moveComponents(moveComponents);
    }, [runtimeActions, page, components, parentComponents, childrenMap]);
    const handleLayer1DragEnd = React.useCallback((result, offset = 0, parentId) => {
        if (!result.destination || !page)
            return;
        const parentPageIndex = getNewDestination(offset, parentId);
        const newDestinationIndex = parentPageIndex + result.destination.index + 1;
        const newSourceIndex = parentPageIndex + result.source.index + 1;
        runtimeActions.swapComponent({ page: page.id }, newSourceIndex, newDestinationIndex);
    }, [runtimeActions, page, getNewDestination]);
    const handleMoveToLayout = React.useCallback((sourceId, destinationLayoutId) => {
        var _a;
        const destinationLayer0Idx = groupingComponents.layer0.findIndex((comp) => comp.id === destinationLayoutId);
        const layoutIndex = getNewDestination(destinationLayer0Idx, sourceId);
        const sourceIndex = components.findIndex((comp) => comp.id === sourceId);
        const destinationIndex = layoutIndex +
            (((_a = groupingComponents.layer1[destinationLayoutId]) === null || _a === void 0 ? void 0 : _a.length) || 0) +
            (layoutIndex < sourceIndex ? 1 : 0);
        runtimeBatchActions(() => {
            runtimeActions.setComponentMetadata({ component: sourceId }, "layout", {
                id: destinationLayoutId,
                column: 1,
            });
            runtimeActions.swapComponent({ page: page.id }, sourceIndex, destinationIndex);
        });
    }, [groupingComponents, components, getNewDestination, page]);
    const handleRemoveFromLayout = React.useCallback((compId) => {
        const sourceIndex = components.findIndex((comp) => comp.id === compId);
        const destinationIndex = components.length;
        runtimeBatchActions(() => {
            runtimeActions.removeComponentLayoutMetadata({ component: compId });
            runtimeActions.swapComponent({ page: page.id }, sourceIndex, destinationIndex);
        });
    }, [components, page]);
    const handleRenameOnBlur = React.useCallback((e, compId) => {
        runtimeActions.setComponentMetadata({ component: compId }, "name", typeof e === "string" ? e : e.currentTarget.value);
        setState((state) => produce(state, (draft) => {
            draft.componentBeingEdit = undefined;
        }));
    }, []);
    const { isOpen, onClose } = useDisclosure();
    const ModalItem = ModalLayout;
    const renderLayoutComponentsMenu = React.useMemo(() => {
        const layer1 = {};
        Object.keys(groupingComponents.layer1).forEach((layoutID) => {
            layer1[layoutID] = [
                ...(layer1[layoutID] || []),
                _jsx(PageMenu, { onDragEnd: (result) => handleLayer1DragEnd(result, groupingComponents.layer0.findIndex((comp) => comp.id === layoutID), layoutID), componentToEditID: state.componentBeingEdit, components: groupingComponents.layer1[layoutID], onBlurRenameItem: handleRenameOnBlur, onStateChange: setState, onRemoveComponent: runtimeActions.removeComponent, onSetActiveComponent: editorActions.setActiveComponent, activeComponent: activeComponent, onRemoveFromLayout: handleRemoveFromLayout }, layoutID),
            ];
        });
        return layer1;
    }, [
        groupingComponents,
        state.componentBeingEdit,
        activeComponent,
        handleDragEnd,
        handleRenameOnBlur,
        runtimeActions.removeComponent,
        editorActions.setActiveComponent,
    ]);
    return (_jsxs(Box, Object.assign({ height: "100%" }, { children: [_jsx(Flex, Object.assign({ height: "39px", px: "4", py: "3px", bg: "white", alignItems: "center" }, { children: _jsx(Text, Object.assign({ fontWeight: "semibold" }, { children: "Components" })) })), !ready ? (_jsx(Stack, Object.assign({ "data-test-id": "schema-loading-skeleton", p: 2, gap: 1 }, { children: Array.from(Array(5).keys()).map((key) => (_jsx(Skeleton, { height: "32px", startColor: "#E6F0FF" }, key))) }))) : (_jsxs(_Fragment, { children: [_jsx(ModalItem, { isOpen: isOpen, onClose: onClose }), _jsx(Box, Object.assign({ height: "full" }, { children: groupedComponents && (_jsx(PageMenuTree, { onDrop: handleOnDrop, data: groupedComponents, onDragEnd: handleDragEnd, componentToEditID: state.componentBeingEdit, components: groupingComponents.layer0, onBlurRenameItem: handleRenameOnBlur, onRemoveComponent: runtimeActions.removeComponents, onSetActiveComponent: editorActions.setActiveComponent, activeComponent: activeComponent, renderLayoutComponentsMenu: renderLayoutComponentsMenu, layer1: groupingComponents.layer1, onMoveToLayout: handleMoveToLayout })) })), _jsxs(Modal, Object.assign({ isOpen: !!modalCustomComponent, onClose: () => {
                            modalCustomComponent !== "loading" &&
                                setModalCustomComponent(null);
                        } }, { children: [_jsx(ModalOverlay, {}), modalCustomComponent &&
                                (modalCustomComponent === "register" ? (_jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: _jsx(Text, Object.assign({ fontSize: "xl" }, { children: "Add custom component" })) }), _jsx(ModalBody, { children: _jsxs(VStack, Object.assign({ spacing: 4 }, { children: [_jsx(Box, Object.assign({ padding: "16px", borderRadius: "4px", backgroundColor: "gray.100" }, { children: _jsxs(Text, Object.assign({ color: "gray.600" }, { children: ["Custom component can be used by editors to create components with the appearance and functionality according to their needs, if the official Qore component has not been able to meet these needs.", _jsx(Link, Object.assign({ href: "https://docs.qore.dev/component/custom-component", color: "blue.500", isExternal: true }, { children: "Learn more" }))] })) })), _jsxs(FormControl, Object.assign({ id: "name" }, { children: [_jsx(FormLabel, { children: "Name" }), _jsx(Input, { name: "name", ref: form.register({ required: true }), placeholder: "Component Name" })] })), _jsxs(FormControl, Object.assign({ id: "subdomain" }, { children: [_jsx(FormLabel, { children: "Component URL" }), _jsx(Input, { name: "url", ref: form.register({ required: true }), placeholder: "Component URL" })] }))] })) }), _jsxs(ModalFooter, Object.assign({ flex: 1 }, { children: [_jsx(Button, Object.assign({ width: "100%", mr: 3, onClick: () => setModalCustomComponent(null) }, { children: "Cancel" })), _jsx(Button, Object.assign({ width: "100%", colorScheme: "brand", onClick: form.handleSubmit(({ name, url }) => {
                                                        setModalCustomComponent("loading");
                                                        importCustomComponent(url)
                                                            .then(({ id }) => {
                                                            handleAddComponent(id, name, url);
                                                            setModalCustomComponent("success");
                                                        })
                                                            .catch((err) => {
                                                            console.error(err);
                                                            setModalCustomComponent("error");
                                                        });
                                                    }) }, { children: "Add component" }))] }))] })) : (_jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: _jsx(Text, Object.assign({ fontSize: "xl" }, { children: "Validation Component" })) }), _jsx(ModalBody, { children: _jsx(Box, Object.assign({ borderWidth: "1px", borderStyle: "solid", borderColor: "gray.200", borderRadius: "md", p: 3 }, { children: modalCustomComponent === "loading" ? (_jsxs(Flex, Object.assign({ alignItems: "flex-start" }, { children: [_jsx(Spinner, { thickness: "4px", speed: "0.65s", emptyColor: "gray.200", color: "blue.500", size: "xl" }), _jsxs(Box, Object.assign({ flex: 1, marginLeft: "3" }, { children: [_jsx(Text, Object.assign({ fontWeight: "bold" }, { children: "Creating Component" })), _jsx(Text, Object.assign({ color: "gray.500" }, { children: "Fetching and validating component from URL" }))] }))] }))) : modalCustomComponent === "success" ? (_jsxs(Flex, Object.assign({ alignItems: "flex-start" }, { children: [_jsx(Box, Object.assign({ w: 10, h: 10, borderRadius: "full", backgroundColor: "green.100", alignItems: "center", justifyContent: "center", boxSizing: "border-box", display: "flex" }, { children: _jsx(Icon, { as: ChevronCheck, color: "green" }) })), _jsxs(Box, Object.assign({ flex: 1, marginLeft: "3" }, { children: [_jsx(Text, Object.assign({ fontWeight: "bold" }, { children: "Component created" })), _jsx(Text, Object.assign({ color: "gray.500" }, { children: "Your component has been created" }))] }))] }))) : (_jsxs(Flex, Object.assign({ alignItems: "flex-start" }, { children: [_jsx(Box, Object.assign({ w: 10, h: 10, borderRadius: "full", backgroundColor: "red.100", alignItems: "center", justifyContent: "center", boxSizing: "border-box", display: "flex" }, { children: _jsx(Icon, { as: Close, color: "red" }) })), _jsxs(Box, Object.assign({ flex: 1, marginLeft: "3" }, { children: [_jsx(Text, Object.assign({ fontWeight: "bold" }, { children: "Failed create component" })), _jsx(Text, Object.assign({ color: "gray.500" }, { children: "Your component URL is invalid" }))] }))] }))) })) }), _jsxs(ModalFooter, Object.assign({ flex: 1 }, { children: [_jsx(Button, Object.assign({ width: "100%", mr: 3, disabled: modalCustomComponent === "loading", onClick: () => setModalCustomComponent("register") }, { children: "Create again" })), _jsx(Button, Object.assign({ width: "100%", colorScheme: "brand", disabled: modalCustomComponent === "loading", onClick: () => setModalCustomComponent(null) }, { children: "Done" }))] }))] })))] }))] }))] })));
});
//# sourceMappingURL=ComponentsManager.js.map