import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { EditOutlined, WarningFilled } from '@ant-design/icons';
import { isQueryPresent } from '@cubejs-client/core';
import { Alert, Button, Divider, Input, notification, Space, Tabs, Typography, Skeleton, Radio, } from 'antd';
import deepEquals from 'fast-deep-equal';
import { useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import { CodeSnippet } from '../atoms';
import { Box, Flex } from '../grid';
import { useDeepEffect, useIsMounted, useToken } from '../hooks';
import { useDeepMemo } from '../hooks/deep-memo';
import { useCloud } from '../cloud';
import { SchemaFormat } from '../types';
import { prettifyObject } from '../utils';
import { Cubes } from './components/Cubes';
import { Members } from './components/Members';
import { Settings } from './components/Settings';
import { TimeDimension } from './components/TimeDimension';
import { useRollupDesignerContext } from './Context';
import { areReferencesEmpty, buildSettings, getPreAggregationReferences, getRollupDefinitionFromReferences, updateQuery, } from './utils';
import { request } from '../shared/request';
import { getNameMemberPairs } from '../shared/members';
import { FatalError } from '../components/Error/FatalError';
const { Paragraph, Text } = Typography;
const { TabPane } = Tabs;
const Wrapper = styled.div `
  display: grid;
  grid-template-columns: 1fr 400px;
  gap: 16px;
`;
const MainBox = styled(Box) `
  & .ant-tabs-nav {
    padding-left: 24px;
    margin: 0;
  }
`;
const RollupQueryBox = styled.div `
  padding: 0 24px 32px;
  background: #f6f6f8;

  & .ant-tabs-nav {
    margin-bottom: 24px;
  }

  & .ant-tabs-tab {
    div,
    .ant-typography {
      font-weight: 500;
    }
  }

  & .ant-typography > .anticon {
    padding-left: 12px;
  }
`;
function getSelectedKeys(references) {
    const keys = new Set();
    ['measures', 'dimensions', 'timeDimensions', 'segments'].map((memberKey) => {
        if (memberKey === 'timeDimensions') {
            const { dimension } = references[memberKey]?.[0] || {};
            if (dimension) {
                keys.add(`td:${dimension}`);
            }
        }
        else {
            references[memberKey]?.map((key) => key != null && keys.add(key));
        }
    });
    return Array.from(keys.values());
}
export function RollupDesigner({ apiUrl, memberTypeCubeMap, token: designerToken, }) {
    const initialMatching = useRef();
    const isMounted = useIsMounted();
    const appToken = useToken();
    const token = appToken || designerToken;
    const { isCloud, ...cloud } = useCloud();
    const { query, transformedQuery, isLoading, error, toggleModal, defaultSchemaFormat } = useRollupDesignerContext();
    const [isCronValid, setCronValidity] = useState(true);
    const [settings, setSettings] = useState({});
    const [openKeys, setOpenKeys] = useState([]);
    const [firstOpenCubeName, setFirstOpenCubeName] = useState(null);
    const [matching, setMatching] = useState();
    const [saving, setSaving] = useState(false);
    const [preAggName, setPreAggName] = useState('main');
    const [nonAdditiveMeasure, setNonAdditiveMeasure] = useState(null);
    const [schemaFormat, setSchemaFormat] = useState(defaultSchemaFormat);
    const { order, limit, filters, ...matchedQuery } = query || {};
    const segments = new Set();
    memberTypeCubeMap.segments.forEach(({ members }) => {
        members.forEach(({ name }) => segments.add(name));
    });
    const [references, setReferences] = useState(getPreAggregationReferences(transformedQuery, segments));
    const hideMatchRollupButton = useDeepMemo(() => {
        if (matching === undefined || !initialMatching.current) {
            return true;
        }
        return deepEquals(references, getPreAggregationReferences(transformedQuery, segments));
    }, [matching, transformedQuery, references, segments, initialMatching]);
    useDeepEffect(() => {
        const references = getPreAggregationReferences(transformedQuery, segments);
        setReferences(references);
        const openKeys = getSelectedKeys(references).map((key) => key.split('.')[0]);
        setOpenKeys(openKeys);
        setFirstOpenCubeName(openKeys[0] || null);
        if (transformedQuery?.measureToLeafMeasures != null) {
            for (const [measure, leafMeasures] of Object.entries(transformedQuery.measureToLeafMeasures)) {
                if (leafMeasures.length > 1) {
                    setNonAdditiveMeasure(measure);
                    break;
                }
            }
        }
    }, [transformedQuery, segments]);
    const selectedKeys = useDeepMemo(() => {
        const selectedKeys = getSelectedKeys(references);
        return selectedKeys;
    }, [references]);
    useDeepEffect(() => {
        let active = true;
        const { measures, segments, dimensions, timeDimensions } = references;
        async function load() {
            const { json } = await request(`${apiUrl}/pre-aggregations/can-use`, 'POST', {
                token: token,
                body: {
                    transformedQuery,
                    references: {
                        measures,
                        dimensions: dimensions.concat(segments),
                        timeDimensions,
                    },
                },
            });
            if (isMounted() && active) {
                setMatching((prevMatching) => {
                    if (prevMatching === undefined) {
                        initialMatching.current =
                            json.canUsePreAggregationForTransformedQuery;
                    }
                    return json.canUsePreAggregationForTransformedQuery;
                });
            }
        }
        if (token != null && transformedQuery) {
            load();
        }
        return () => (active = false);
    }, [isMounted, references, token, transformedQuery]);
    const cubeName = useMemo(() => {
        let cubeName = null;
        if (transformedQuery) {
            cubeName = (transformedQuery?.leafMeasures[0] ||
                transformedQuery?.ownedDimensions[0] ||
                'CubeName').split('.')[0];
        }
        else if (!areReferencesEmpty(references)) {
            const [key] = getSelectedKeys(references);
            cubeName = key.split('.')[0] || null;
        }
        return cubeName;
    }, [transformedQuery, references]);
    const [showDecomposedMeasureAlert, showNonAdditiveMeasureAlert, showCountDistinctAlert,] = useDeepMemo(() => {
        const { measureToLeafMeasures = {} } = transformedQuery || {};
        let showDecomposedMeasureAlert = false;
        let showNonAdditiveMeasureAlert = false;
        let showCountDistinctAlert = false;
        if (nonAdditiveMeasure && measureToLeafMeasures[nonAdditiveMeasure]) {
            showDecomposedMeasureAlert = measureToLeafMeasures[nonAdditiveMeasure].every(({ additive }) => additive);
        }
        if (transformedQuery && !transformedQuery.leafMeasureAdditive) {
            const allLeafMeasures = Object.values(measureToLeafMeasures).reduce((memo, leafMeasures) => [...memo, ...leafMeasures], []);
            showNonAdditiveMeasureAlert = references.measures.some((measure) => {
                const leafMeasure = allLeafMeasures.find((leafMeasure) => leafMeasure.measure === measure);
                if (!leafMeasure) {
                    return false;
                }
                return !leafMeasure.additive;
            });
            const hasCountDistinctMeasures = references.measures.some((measure) => {
                const leafMeasure = allLeafMeasures.find((leafMeasure) => leafMeasure.measure === measure);
                if (!leafMeasure) {
                    return false;
                }
                return leafMeasure.type === 'countDistinct';
            });
            showCountDistinctAlert =
                hasCountDistinctMeasures && !references.timeDimensions[0]?.granularity;
        }
        return [
            showDecomposedMeasureAlert,
            showNonAdditiveMeasureAlert,
            showCountDistinctAlert,
        ];
    }, [references, transformedQuery, nonAdditiveMeasure]);
    const indexedMembers = Object.fromEntries(getNameMemberPairs([
        ...memberTypeCubeMap.measures,
        ...memberTypeCubeMap.dimensions,
        ...memberTypeCubeMap.timeDimensions,
        ...memberTypeCubeMap.segments,
    ]));
    async function handleAddToSchemaClick() {
        if (!cubeName) {
            return;
        }
        const definition = {
            preAggregationName: preAggName,
            cubeName,
            code: getRollupDefinitionFromReferences(references, preAggName, settings, schemaFormat)
                .value,
        };
        function showSuccessMessage() {
            notification.success({
                message: `Rollup has been added to the ${cubeName} cube`,
            });
        }
        setSaving(true);
        if (!isCloud) {
            const response = await request('/playground/schema/pre-aggregation', 'POST', {
                body: definition,
            });
            if (response.ok) {
                showSuccessMessage();
                toggleModal();
            }
            else {
                const { error } = response.json;
                notification.error({
                    message: error,
                });
            }
        }
        else {
            if (cloud.addPreAggregationToSchema == null) {
                throw new Error('cloud.addPreAggregationToSchema is not defined');
            }
            const { error } = await cloud.addPreAggregationToSchema(definition);
            if (!error) {
                showSuccessMessage();
                toggleModal();
            }
            else {
                notification.error({
                    message: error,
                });
            }
        }
        setSaving(false);
    }
    function handleMemberToggle(memberType) {
        return (key) => {
            setReferences(updateQuery(references, memberType, key));
        };
    }
    function rollupBody() {
        return (_jsxs(_Fragment, { children: [_jsx(CodeSnippet, { style: { marginBottom: 16 }, code: getRollupDefinitionFromReferences(references, preAggName, settings, schemaFormat)
                        .code, copyMessage: "Rollup definition is copied", theme: "light" }), cubeName ? (_jsxs(Space, { direction: "vertical", style: { width: '100%' }, size: 32, children: [_jsxs(Flex, { justifyContent: "end", gap: 2, alignItems: "center", children: [_jsx(Box, { children: _jsx(Typography.Text, { children: "Data Model Format" }) }), _jsx(Box, { children: _jsx(Radio.Group, { options: [
                                            {
                                                label: 'JavaScript',
                                                value: SchemaFormat.js,
                                            },
                                            {
                                                label: 'YAML',
                                                value: SchemaFormat.yaml,
                                            },
                                        ], value: schemaFormat, optionType: "button", buttonStyle: "solid", onChange: (e) => setSchemaFormat(e.target.value) }) })] }), _jsx(Button, { "data-testid": "rd-add-btn", type: "primary", loading: saving, disabled: !isCronValid, style: { width: '100%' }, onClick: handleAddToSchemaClick, children: "Add to the Data Schema" })] })) : null] }));
    }
    if (isLoading) {
        return (_jsxs(Flex, { gap: 4, style: { margin: 24 }, children: [_jsx(Box, { style: { minWidth: 256 }, children: _jsx(Skeleton, {}) }), _jsxs(Box, { grow: 1, children: [_jsx(Skeleton, {}), _jsx(Skeleton, {})] }), _jsx(Box, { style: {
                        width: 420,
                        minWidth: 420,
                    }, children: _jsx(Skeleton, {}) })] }));
    }
    if (error) {
        return (_jsx("div", { style: { margin: '32px 24px' }, children: _jsx(FatalError, { error: error }) }));
    }
    return (_jsxs(Wrapper, { children: [_jsx(MainBox, { grow: 1, style: { overflowX: 'scroll' }, children: _jsxs(Tabs, { style: { minHeight: '100%' }, children: [_jsx(TabPane, { tab: _jsx("span", { "data-testid": "rd-members-tab", children: "Members" }), children: _jsxs(Flex, { gap: 2, children: [_jsx(Box, { style: {
                                            minWidth: 280,
                                            maxWidth: 280,
                                        }, children: _jsx(Cubes, { openKeys: openKeys, selectedKeys: selectedKeys, memberTypeCubeMap: memberTypeCubeMap, firstOpenCubeName: firstOpenCubeName, onSelect: (memberType, key) => {
                                                handleMemberToggle(memberType)(key);
                                            }, onOpenKeysChange: setOpenKeys }) }), _jsxs(Box, { grow: 1, style: {
                                            marginTop: 24,
                                            overflowX: 'scroll',
                                        }, children: [references.measures?.length ? (_jsxs(_Fragment, { children: [_jsx(Members, { title: "Measures", members: references.measures.map((name) => indexedMembers[name]), onRemove: handleMemberToggle('measures') }), _jsx(Divider, {})] })) : null, references.dimensions?.length ? (_jsxs(_Fragment, { children: [_jsx(Members, { title: "Dimensions", members: references.dimensions.map((name) => indexedMembers[name]), onRemove: handleMemberToggle('dimensions') }), _jsx(Divider, {})] })) : null, references.segments.length ? (_jsxs(_Fragment, { children: [_jsx(Members, { title: "Segments", members: references.segments.map((name) => indexedMembers[name]), onRemove: handleMemberToggle('segments') }), _jsx(Divider, {})] })) : null, references.timeDimensions.length ? (_jsx(TimeDimension, { member: indexedMembers[references.timeDimensions[0].dimension], granularity: references.timeDimensions[0].granularity, onGranularityChange: (granularity) => {
                                                    setReferences({
                                                        ...references,
                                                        timeDimensions: [
                                                            {
                                                                ...(references.timeDimensions[0] || {}),
                                                                ...(granularity ? { granularity } : null),
                                                            },
                                                        ],
                                                    });
                                                }, onRemove: handleMemberToggle('timeDimensions') })) : null] })] }) }, "members"), _jsx(TabPane, { tab: _jsx("span", { "data-testid": "rd-settings-tab", children: "Settings" }), children: _jsx(Settings, { hasTimeDimension: references.timeDimensions.length > 0, members: references.measures
                                    .concat(references.dimensions)
                                    .concat(references.timeDimensions.map((td) => td.dimension)), onCronExpressionValidityChange: setCronValidity, onChange: (values) => setSettings(buildSettings(values)) }) }, "settings")] }) }), _jsx(RollupQueryBox, { children: _jsxs(Tabs, { children: [_jsx(TabPane, { tab: _jsx("span", { "data-testid": "rd-definition-tab", children: "Rollup Definition" }), children: _jsxs(Flex, { direction: "column", justifyContent: "flex-start", children: [showNonAdditiveMeasureAlert && (_jsx(Box, { style: { marginBottom: 24 }, children: _jsx(Alert, { type: "info", message: _jsx(Text, { children: "This rollup contains a non-additive measure" }) }) })), showDecomposedMeasureAlert && (_jsx(Box, { style: { marginBottom: 24 }, children: _jsx(Alert, { type: "info", message: _jsxs(Text, { children: ["Because ", _jsx("b", { children: nonAdditiveMeasure }), " is a non-additive measure that is calculated with additive measures, this rollup is configured with the additive measures that calculate this non-additive measure. See more info in", ' ', _jsx(Typography.Link, { href: "https://cube.dev/recipes/pre-aggregations/non-additivity", target: "_blank", children: "our docs" }), "."] }) }) })), !areReferencesEmpty(references) &&
                                        !references.timeDimensions.length && (_jsx(Box, { style: { marginBottom: 24 }, children: _jsx(Alert, { type: "warning", message: "This rollup has no time dimension so it cannot be partitioned" }) })), _jsxs(Box, { style: { marginBottom: 16 }, children: [!areReferencesEmpty(references) ? (_jsxs(Paragraph, { children: ["Add the following rollup pre-aggregation", _jsx("br", {}), " to the ", _jsx("b", { children: cubeName }), " cube:"] })) : (_jsx(Alert, { type: "warning", message: "Add some references" })), _jsx(Paragraph, { style: { margin: '24px 0 4px' }, children: "Rollup Name" }), _jsx(Input, { value: preAggName, suffix: _jsx(EditOutlined, {}), onChange: (event) => setPreAggName(event.target.value) })] }), _jsx(Box, { children: rollupBody() })] }) }, "rollup"), isQueryPresent(query) ? (_jsx(TabPane, { tab: matching ? (_jsx("span", { "data-testid": "rd-query-tab", children: "Query Compatibility" })) : (_jsxs(Typography.Text, { "data-testid": "rd-query-tab", children: ["Query Compatibility", matching != null ? (_jsx(WarningFilled, { style: { color: '#FBBC05' } })) : null] })), children: _jsxs(Flex, { direction: "column", justifyContent: "flex-start", children: [_jsx(Box, { style: { marginBottom: 32 }, children: _jsxs(Space, { direction: "vertical", size: 24, children: [showCountDistinctAlert && (_jsx(Alert, { type: "warning", message: _jsx(Text, { children: "This query does not have any time dimension granularity, which prevents pre-aggregating any count distinct measures." }) })), matching ? (_jsx(Text, { children: "This rollup will match the following query:" })) : (_jsxs(_Fragment, { children: [_jsx(Alert, { "data-testid": "rd-incompatible-query", type: "warning", message: _jsxs(Text, { children: ["This rollup does ", _jsx("b", { children: "NOT" }), " match the following query:"] }) }), !hideMatchRollupButton && (_jsx(Button, { "data-testid": "rd-match-rollup-btn", type: "primary", ghost: true, onClick: () => {
                                                                setReferences(getPreAggregationReferences(transformedQuery, segments));
                                                                setMatching(true);
                                                            }, children: "Match Rollup" }))] }))] }) }), _jsx(CodeSnippet, { style: { minWidth: 200 }, code: prettifyObject(matchedQuery), copyMessage: "Query is copied", theme: "light" })] }) }, "query")) : null] }) })] }));
}
//# sourceMappingURL=RollupDesigner.js.map