import { useField, useFieldSchema } from "@tachybase/schema";
import flat from "flat";
import lodash from "lodash";
import { useTranslation } from "react-i18next";
import { useBlockRequestContext } from "../../../block-provider/index.mjs";
import { useCollectionManager_deprecated, useCollection_deprecated } from "../../../collection-manager/index.mjs";
import { FILTER_OPERATORS_WITH_ARRAY_VALUES, mergeFilter, normalizeDateBetweenValue, shouldUseDefaultDateBoundary } from "../../../filter-provider/utils.mjs";
import { useDataLoadingMode } from "../../../modules/blocks/data-blocks/details-multi/setDataLoadingModeSettingsItem.mjs";
import { hasDuplicateKeys } from "./utils.mjs";
const useGetFilterOptions = ()=>{
    const { getCollectionFields } = useCollectionManager_deprecated();
    const getFilterFieldOptions = useGetFilterFieldOptions();
    return (collectionName, dataSource)=>{
        const fields = getCollectionFields(collectionName, dataSource);
        const options = getFilterFieldOptions(fields);
        return options;
    };
};
const useFilterOptions = (collectionName)=>{
    const { getCollectionFields } = useCollectionManager_deprecated();
    const fields = getCollectionFields(collectionName);
    const options = useFilterFieldOptions(fields);
    return options;
};
const useGetFilterFieldOptions = ()=>{
    var _fieldSchema_xcomponentprops;
    const fieldSchema = useFieldSchema();
    const nonfilterable = (null == fieldSchema ? void 0 : null == (_fieldSchema_xcomponentprops = fieldSchema['x-component-props']) ? void 0 : _fieldSchema_xcomponentprops.nonfilterable) || [];
    const { getCollectionFields, getInterface } = useCollectionManager_deprecated();
    const field2option = (field, depth)=>{
        var _field_uiSchema, _operators_filter;
        if (nonfilterable.length && 1 === depth && nonfilterable.includes(field.name)) return;
        if (!field.interface) return;
        const fieldInterface = getInterface(field.interface);
        if (!(null == fieldInterface ? void 0 : fieldInterface.filterable)) return;
        const { nested, children, operators } = fieldInterface.filterable;
        const option = {
            name: field.name,
            type: field.type,
            target: field.target,
            title: (null == field ? void 0 : null == (_field_uiSchema = field.uiSchema) ? void 0 : _field_uiSchema.title) || field.name,
            schema: null == field ? void 0 : field.uiSchema,
            interface: field.interface,
            operators: (null == operators ? void 0 : null == (_operators_filter = operators.filter) ? void 0 : _operators_filter.call(operators, (operator)=>!(null == operator ? void 0 : operator.visible) || operator.visible(field))) || []
        };
        if (field.target && depth > 2) return;
        if (depth > 2) return option;
        if (null == children ? void 0 : children.length) option['children'] = children;
        if (nested) {
            const targetFields = getCollectionFields(field.target);
            const options = getOptions(targetFields, depth + 1).filter(Boolean);
            option['children'] = option['children'] || [];
            option['children'].push(...options);
        }
        return option;
    };
    const getOptions = (fields, depth)=>{
        const options = [];
        fields.forEach((field)=>{
            const option = field2option(field, depth);
            if (option) options.push(option);
        });
        return options;
    };
    return (fields)=>getOptions(fields, 1);
};
const useFilterFieldOptions = (fields)=>{
    var _fieldSchema_xcomponentprops;
    const fieldSchema = useFieldSchema();
    const nonfilterable = (null == fieldSchema ? void 0 : null == (_fieldSchema_xcomponentprops = fieldSchema['x-component-props']) ? void 0 : _fieldSchema_xcomponentprops.nonfilterable) || [];
    const { getCollectionFields, getInterface } = useCollectionManager_deprecated();
    const field2option = (field, depth)=>{
        var _field_uiSchema, _operators_filter;
        if (nonfilterable.length && 1 === depth && nonfilterable.includes(field.name)) return;
        if (!field.interface) return;
        const fieldInterface = getInterface(field.interface);
        if (!(null == fieldInterface ? void 0 : fieldInterface.filterable)) return;
        const { nested, children, operators } = fieldInterface.filterable;
        const option = {
            name: field.name,
            type: field.type,
            target: field.target,
            title: (null == field ? void 0 : null == (_field_uiSchema = field.uiSchema) ? void 0 : _field_uiSchema.title) || field.name,
            schema: null == field ? void 0 : field.uiSchema,
            operators: (null == operators ? void 0 : null == (_operators_filter = operators.filter) ? void 0 : _operators_filter.call(operators, (operator)=>!(null == operator ? void 0 : operator.visible) || operator.visible(field))) || []
        };
        if (field.target && depth > 2) return;
        if (depth > 2) return option;
        if (null == children ? void 0 : children.length) option['children'] = children;
        if (nested) {
            const targetFields = getCollectionFields(field.target);
            const options = getOptions(targetFields, depth + 1).filter(Boolean);
            option['children'] = option['children'] || [];
            option['children'].push(...options);
        }
        return option;
    };
    const getOptions = (fields, depth)=>{
        const options = [];
        fields.forEach((field)=>{
            const option = field2option(field, depth);
            if (option) options.push(option);
        });
        return options;
    };
    return getOptions(fields, 1);
};
const isEmpty = (obj)=>Array.isArray(obj) && 0 === obj.length || obj && 0 === Object.keys(obj).length && Object.getPrototypeOf(obj) === Object.prototype;
const CUSTOM_FILTER_VARIABLE_REGEXP = /^\{\{\$nFilter\.([^}]+)\}\}$/;
const getCustomFilterValue = (items, key)=>{
    if (key in items) return items[key];
    const arrayItems = Object.keys(items).filter((itemKey)=>itemKey.startsWith("".concat(key, ".")) && /^\d+$/.test(itemKey.slice(key.length + 1))).sort((a, b)=>Number(a.slice(key.length + 1)) - Number(b.slice(key.length + 1))).map((itemKey)=>items[itemKey]);
    return arrayItems.length ? arrayItems : void 0;
};
const findCustomFieldSchemaInTree = (schema, key)=>{
    if (!schema) return null;
    if (schema.name === "__custom.".concat(key) || schema.name === key) return schema;
    const properties = schema.properties || {};
    for (const propertyKey of Object.keys(properties)){
        if (propertyKey === "__custom.".concat(key) || propertyKey === key) return properties[propertyKey];
        const found = findCustomFieldSchemaInTree(properties[propertyKey], key);
        if (found) return found;
    }
    return null;
};
const findCustomFieldSchema = (schema, key)=>{
    let current = schema;
    while(current){
        const found = findCustomFieldSchemaInTree(current, key);
        if (found) return found;
        current = current.parent;
    }
    return null;
};
const expandArrayValueFilter = function(filterSchemaItem, filterKey, value, customFlat) {
    let options = arguments.length > 4 && void 0 !== arguments[4] ? arguments[4] : {};
    const pathParts = filterKey.split('.');
    const operator = pathParts.pop();
    if ('$dateBetween' === operator && Array.isArray(value)) {
        filterSchemaItem[filterKey] = normalizeDateBetweenValue(value, options);
        return;
    }
    if (!Array.isArray(value) || FILTER_OPERATORS_WITH_ARRAY_VALUES.has(operator || '')) {
        filterSchemaItem[filterKey] = value;
        return;
    }
    let branchEndIndex = -1;
    for(let index = pathParts.length - 2; index >= 0; index--)if ([
        '$and',
        '$or'
    ].includes(pathParts[index]) && /^\d+$/.test(pathParts[index + 1])) {
        branchEndIndex = index;
        break;
    }
    const branchPath = branchEndIndex >= 0 ? pathParts.slice(0, branchEndIndex + 2).join('.') : '';
    const fieldPath = pathParts.slice(branchEndIndex + 2).join('.');
    const conditions = value.map((item)=>customFlat.unflatten({
            ["".concat(fieldPath, ".").concat(operator)]: item
        }));
    delete filterSchemaItem[filterKey];
    filterSchemaItem[branchPath ? "".concat(branchPath, ".$or") : '$or'] = conditions;
};
const getCustomCondition = function(filter, fieldSchema) {
    let customFlat = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : flat;
    var _filterSchema_$and, _filterSchema_$or;
    const filterSchema = fieldSchema ? fieldSchema['x-filter-rules'] : '';
    const filterSchemaItem = customFlat(filterSchema || '');
    const items = customFlat(filter || {});
    const isCustomFilter = (null == filterSchema ? void 0 : null == (_filterSchema_$and = filterSchema.$and) ? void 0 : _filterSchema_$and.length) || (null == filterSchema ? void 0 : null == (_filterSchema_$or = filterSchema.$or) ? void 0 : _filterSchema_$or.length);
    const isFilterCustom = isCustomFilter ? hasDuplicateKeys(items, filterSchemaItem) : false;
    if (isFilterCustom) return customFlat.unflatten(items);
    if (!isCustomFilter) return customFlat.unflatten({});
    {
        var _flatFieldSchema_$and, _flatFieldSchema_$or;
        for(const filterKey in filterSchemaItem){
            const match = 'string' == typeof filterSchemaItem[filterKey] && filterSchemaItem[filterKey].match(CUSTOM_FILTER_VARIABLE_REGEXP);
            if (!match) continue;
            const customFieldSchema = findCustomFieldSchema(fieldSchema, match[1]);
            expandArrayValueFilter(filterSchemaItem, filterKey, getCustomFilterValue(items, match[1]), customFlat, {
                useDefaultDateBoundary: shouldUseDefaultDateBoundary(customFieldSchema)
            });
        }
        for(const item in filterSchemaItem)if (!filterSchemaItem[item] || filterSchemaItem[item].includes('$nFilter')) delete filterSchemaItem[item];
        const flatFieldSchema = customFlat.unflatten(filterSchemaItem);
        flatFieldSchema['$and'] = null == flatFieldSchema ? void 0 : null == (_flatFieldSchema_$and = flatFieldSchema['$and']) ? void 0 : _flatFieldSchema_$and.filter(Boolean);
        flatFieldSchema['$or'] = null == flatFieldSchema ? void 0 : null == (_flatFieldSchema_$or = flatFieldSchema['$or']) ? void 0 : _flatFieldSchema_$or.filter(Boolean);
        return flatFieldSchema;
    }
};
const removeNullCondition = function(filter) {
    let customFlat = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : flat;
    const items = customFlat(filter || {});
    const values = {};
    for(const key in items){
        const value = items[key];
        if (null != value && !isEmpty(value)) values[key] = value;
    }
    for (const key of Object.keys(values))if (key.endsWith('$dateBetween.0')) {
        const key1 = key.replace(/\$dateBetween\.0$/, '$dateBetween.1');
        if (!(key1 in values)) values[key1] = values[key];
    } else if (key.endsWith('$dateBetween.1')) {
        const key0 = key.replace(/\$dateBetween\.1$/, '$dateBetween.0');
        if (!(key0 in values)) values[key0] = values[key];
    }
    return customFlat.unflatten(values);
};
const useFilterActionProps = ()=>{
    const { name } = useCollection_deprecated();
    const options = useFilterOptions(name);
    const { service, props } = useBlockRequestContext();
    return useFilterFieldProps({
        options,
        service,
        params: null == props ? void 0 : props.params
    });
};
const useFilterFieldProps = (param)=>{
    let { options, service, params } = param;
    const { t } = useTranslation();
    const field = useField();
    const dataLoadingMode = useDataLoadingMode();
    return {
        options,
        onSubmit (values) {
            var _service_params_, _service_params, _service_params1;
            const defaultFilter = params.filter;
            const filter = removeNullCondition(null == values ? void 0 : values.filter);
            if ('manual' === dataLoadingMode && lodash.isEmpty(filter)) return service.mutate(void 0);
            const filters = (null == (_service_params = service.params) ? void 0 : null == (_service_params_ = _service_params[1]) ? void 0 : _service_params_.filters) || {};
            filters["filterAction"] = filter;
            service.run({
                ...null == (_service_params1 = service.params) ? void 0 : _service_params1[0],
                page: 1,
                filter: mergeFilter([
                    ...Object.values(filters),
                    defaultFilter
                ])
            }, {
                filters
            });
            const items = (null == filter ? void 0 : filter.$and) || (null == filter ? void 0 : filter.$or);
            if (null == items ? void 0 : items.length) field.title = t('{{count}} filter items', {
                count: (null == items ? void 0 : items.length) || 0
            });
            else field.title = t('Filter');
        },
        onReset () {
            var _service_params_, _service_params, _service_params1;
            const filter = params.filter;
            const filters = (null == (_service_params = service.params) ? void 0 : null == (_service_params_ = _service_params[1]) ? void 0 : _service_params_.filters) || {};
            delete filters["filterAction"];
            const newParams = [
                {
                    ...null == (_service_params1 = service.params) ? void 0 : _service_params1[0],
                    filter: mergeFilter([
                        ...Object.values(filters),
                        filter
                    ]),
                    page: 1
                },
                {
                    filters
                }
            ];
            field.title = t('Filter');
            if ('manual' === dataLoadingMode) {
                service.params = newParams;
                return service.mutate(void 0);
            }
            service.run(...newParams);
        }
    };
};
export { getCustomCondition, removeNullCondition, useFilterActionProps, useFilterFieldOptions, useFilterFieldProps, useFilterOptions, useGetFilterFieldOptions, useGetFilterOptions };
