import { jsx } from "react/jsx-runtime";
import { createContext, useCallback, useEffect, useMemo, useRef } from "react";
import { raw, untracked } from "@tachybase/schema";
import { getValuesByPath } from "@tachybase/utils/client";
import lodash from "lodash";
import { useAPIClient } from "../api-client/index.mjs";
import { useCollectionManager_deprecated } from "../collection-manager/index.mjs";
import { useCompile } from "../schema-component/index.mjs";
import useBuiltinVariables from "./hooks/useBuiltinVariables.mjs";
import { filterEmptyValues } from "./utils/filterEmptyValues.mjs";
import { getAction } from "./utils/getAction.mjs";
import { getPath } from "./utils/getPath.mjs";
import { clearRequested, getRequested, hasRequested, stashRequested } from "./utils/hasRequested.mjs";
import { isVariable } from "./utils/isVariable.mjs";
import { uniq } from "./utils/uniq.mjs";
const VariablesContext = /*#__PURE__*/ createContext(null);
VariablesContext.displayName = 'VariablesContext';
const VariablesProvider_variableToCollectionName = {};
const getFieldPath = (variablePath, variableToCollectionName)=>{
    const list = variablePath.split('.');
    const result = list.map((item)=>{
        if (variableToCollectionName[item]) return variableToCollectionName[item];
        return item;
    });
    return result.join('.');
};
const VariablesProvider_VariablesProvider = (param)=>{
    let { children } = param;
    const ctxRef = useRef({});
    const api = useAPIClient();
    const { getCollectionJoinField } = useCollectionManager_deprecated();
    const compile = useCompile();
    const { builtinVariables } = useBuiltinVariables();
    const setCtx = useCallback((ctx)=>{
        if (lodash.isFunction(ctx)) ctxRef.current = ctx(ctxRef.current);
        else ctxRef.current = ctx;
    }, []);
    const getValue = useCallback(async (variablePath, localVariables, options)=>{
        const list = variablePath.split('.');
        const variableName = list[0];
        const _variableToCollectionName = mergeVariableToCollectionNameWithLocalVariables(VariablesProvider_variableToCollectionName, localVariables);
        let current = mergeCtxWithLocalVariables(ctxRef.current, localVariables);
        let collectionName = getFieldPath(variableName, _variableToCollectionName);
        if (!(variableName in current)) throw new Error("VariablesProvider: ".concat(variableName, " is not found"));
        for(let index = 0; index < list.length; index++){
            if (null == current) return current;
            if ('$nParentRecord' === list[index]) {
                if (!(list[index + 1] in current[list[index]]) && '__parent' in current[list[index]]) list.splice(1, 0, '__parent');
            }
            const key = list[index];
            const associationField = getCollectionJoinField(getFieldPath(list.slice(0, index + 1).join('.'), _variableToCollectionName));
            if (Array.isArray(current)) {
                const result = current.map((item)=>{
                    if (shouldToRequest(null == item ? void 0 : item[key]) && (null == item ? void 0 : item.id) != null) {
                        if (null == associationField ? void 0 : associationField.target) {
                            const url = "/".concat(collectionName, "/").concat(item.id, "/").concat(key, ":").concat(getAction(associationField.type));
                            if (hasRequested(url)) return getRequested(url);
                            const result = api.request({
                                url,
                                params: {
                                    appends: null == options ? void 0 : options.appends
                                }
                            }).then((data)=>{
                                clearRequested(url);
                                item[key] = data.data.data;
                                return item[key];
                            });
                            stashRequested(url, result);
                            return result;
                        }
                    }
                    return null == item ? void 0 : item[key];
                });
                current = lodash.flatten(await Promise.all(result));
            } else if (shouldToRequest(current[key]) && null != current.id && (null == associationField ? void 0 : associationField.target) && (null == associationField ? void 0 : associationField.type) != 'virtual') {
                const url = "/".concat(collectionName, "/").concat(current.id, "/").concat(key, ":").concat(getAction(associationField.type));
                let data = null;
                if (hasRequested(url)) data = await getRequested(url);
                else {
                    const waitForData = api.request({
                        url,
                        params: {
                            appends: null == options ? void 0 : options.appends
                        }
                    });
                    stashRequested(url, waitForData);
                    data = await waitForData;
                    clearRequested(url);
                }
                if (!raw(current)[key]) raw(current)[key] = data.data.data;
                current = getValuesByPath(current, key);
            } else current = getValuesByPath(current, key);
            if (null == associationField ? void 0 : associationField.target) collectionName = associationField.target;
        }
        return compile(lodash.isFunction(current) ? current() : current);
    }, [
        getCollectionJoinField
    ]);
    const registerVariable = useCallback((variableOption)=>{
        if (!isVariable("{{".concat(variableOption.name, "}}"))) throw new Error("VariablesProvider: ".concat(variableOption.name, " is not a valid name"));
        setCtx((prev)=>({
                ...prev,
                [variableOption.name]: variableOption.ctx
            }));
        if (variableOption.collectionName) VariablesProvider_variableToCollectionName[variableOption.name] = variableOption.collectionName;
    }, [
        setCtx
    ]);
    const getVariable = useCallback((variableName)=>{
        if (!ctxRef.current[variableName]) return null;
        return {
            name: variableName,
            ctx: ctxRef.current[variableName],
            collectionName: VariablesProvider_variableToCollectionName[variableName]
        };
    }, []);
    const removeVariable = useCallback((variableName)=>{
        setCtx((prev)=>{
            const next = {
                ...prev
            };
            delete next[variableName];
            return next;
        });
        delete VariablesProvider_variableToCollectionName[variableName];
    }, [
        setCtx
    ]);
    const parseVariable = useCallback(async (str, localVariables, options)=>{
        if (!isVariable(str)) return str;
        if (localVariables) localVariables = lodash.isArray(localVariables) ? localVariables : [
            localVariables
        ];
        const path = getPath(str);
        const value = await getValue(path, localVariables, options);
        return uniq(filterEmptyValues(value));
    }, [
        getValue
    ]);
    const getCollectionField = useCallback(async (variableString, localVariables)=>{
        if (!isVariable(variableString)) throw new Error("VariablesProvider: ".concat(variableString, " is not a variable string"));
        if (localVariables) localVariables = lodash.isArray(localVariables) ? localVariables : [
            localVariables
        ];
        const path = getPath(variableString);
        let result = getCollectionJoinField(getFieldPath(path, mergeVariableToCollectionNameWithLocalVariables(VariablesProvider_variableToCollectionName, localVariables)));
        if (!result && !path.includes('.')) result = {
            target: VariablesProvider_variableToCollectionName[path]
        };
        return result;
    }, [
        getCollectionJoinField
    ]);
    useEffect(()=>{
        builtinVariables.forEach((variableOption)=>{
            registerVariable(variableOption);
        });
    }, [
        builtinVariables,
        registerVariable
    ]);
    const value = useMemo(()=>({
            ctxRef,
            setCtx,
            parseVariable,
            registerVariable,
            getVariable,
            getCollectionField,
            removeVariable
        }), [
        getCollectionField,
        getVariable,
        parseVariable,
        registerVariable,
        removeVariable,
        setCtx
    ]);
    return /*#__PURE__*/ jsx(VariablesContext.Provider, {
        value: value,
        children: children
    });
};
VariablesProvider_VariablesProvider.displayName = 'VariablesProvider';
const VariablesProvider = VariablesProvider_VariablesProvider;
function shouldToRequest(value) {
    let result = false;
    untracked(()=>{
        if ('[{}]' === JSON.stringify(value) || '{}' === JSON.stringify(value)) {
            result = true;
            return;
        }
        result = lodash.isEmpty(value);
    });
    return result;
}
function mergeCtxWithLocalVariables(ctx, localVariables) {
    ctx = {
        ...ctx
    };
    null == localVariables || localVariables.forEach((item)=>{
        ctx[item.name] = item.ctx;
    });
    return ctx;
}
function mergeVariableToCollectionNameWithLocalVariables(variableToCollectionName, localVariables) {
    variableToCollectionName = {
        ...variableToCollectionName
    };
    null == localVariables || localVariables.forEach((item)=>{
        if (item.collectionName) variableToCollectionName[item.name] = item.collectionName;
    });
    return variableToCollectionName;
}
export { VariablesContext, VariablesProvider as default };
