import { useCallback, useEffect, useState } from "react";
import { useFieldSchema } from "@tachybase/schema";
import { flatten, getValuesByPath } from "@tego/client";
import dayjs from "dayjs";
import lodash from "lodash";
import { findFilterTargets } from "../block-provider/hooks/index.mjs";
import { useCollectionManager_deprecated, useCollection_deprecated } from "../collection-manager/index.mjs";
import { removeNullCondition } from "../schema-component/index.mjs";
import { findFilterOperators, getDefaultFilterOperatorValue } from "../schema-component/antd/form-item/SchemaSettingOptions.mjs";
import { useFilterBlock } from "./FilterProvider.mjs";
import { getFilterSourceDefaultFilter } from "./incomingFilterFromSources.mjs";
var utils_FilterBlockType = /*#__PURE__*/ function(FilterBlockType) {
    FilterBlockType[FilterBlockType["FORM"] = 0] = "FORM";
    FilterBlockType[FilterBlockType["TABLE"] = 1] = "TABLE";
    FilterBlockType[FilterBlockType["TREE"] = 2] = "TREE";
    FilterBlockType[FilterBlockType["COLLAPSE"] = 3] = "COLLAPSE";
    return FilterBlockType;
}({});
const mergeFilter = function(filters) {
    let op = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : '$and';
    const items = filters.filter((f)=>{
        if (f && 'object' == typeof f && !Array.isArray(f)) return Object.values(f).filter((v)=>void 0 !== v).length;
    });
    if (0 === items.length) return {};
    if (1 === items.length) return items[0];
    return {
        [op]: items
    };
};
const FILTER_OPERATORS_WITH_ARRAY_VALUES = new Set([
    '$match',
    '$notMatch',
    '$anyOf',
    '$noneOf',
    '$childIn',
    '$childNotIn',
    '$dateBetween',
    '$in',
    '$notIn'
]);
const getSupportFieldsByAssociation = (inheritCollectionsChain, block)=>{
    var _block_associatedFields;
    return null == (_block_associatedFields = block.associatedFields) ? void 0 : _block_associatedFields.filter((field)=>null == inheritCollectionsChain ? void 0 : inheritCollectionsChain.some((collectionName)=>collectionName === field.target));
};
const getSupportFieldsByForeignKey = (filterBlockCollection, block)=>{
    var _block_foreignKeyFields;
    return null == (_block_foreignKeyFields = block.foreignKeyFields) ? void 0 : _block_foreignKeyFields.filter((foreignKeyField)=>filterBlockCollection.fields.some((field)=>'belongsTo' !== field.type && field.foreignKey === foreignKeyField.name));
};
const useSupportedBlocks = (filterBlockType)=>{
    const { getDataBlocks } = useFilterBlock();
    const fieldSchema = useFieldSchema();
    const collection = useCollection_deprecated();
    const { getAllCollectionsInheritChain } = useCollectionManager_deprecated();
    if (0 === filterBlockType || 3 === filterBlockType) return getDataBlocks().filter((block)=>isSameCollection(block.collection, collection));
    if (1 === filterBlockType || 2 === filterBlockType) return getDataBlocks().filter((block)=>{
        var _getSupportFieldsByAssociation, _getSupportFieldsByForeignKey;
        return fieldSchema['x-uid'] !== block.uid && (isSameCollection(block.collection, collection) || (null == (_getSupportFieldsByAssociation = getSupportFieldsByAssociation(getAllCollectionsInheritChain(collection.name, collection.dataSource), block)) ? void 0 : _getSupportFieldsByAssociation.length) || (null == (_getSupportFieldsByForeignKey = getSupportFieldsByForeignKey(collection, block)) ? void 0 : _getSupportFieldsByForeignKey.length));
    });
};
const findSchemaByName = (schema, name)=>{
    if (!schema) return null;
    if (schema.name === name) return schema;
    const properties = schema.properties || {};
    for (const key of Object.keys(properties)){
        if (key === name) return properties[key];
        const found = findSchemaByName(properties[key], name);
        if (found) return found;
    }
    return null;
};
const getFilterSchemaRoot = (schema)=>{
    let current = schema;
    while(current){
        if (current['x-filter-operators'] || current['x-filter-targets']) return current;
        current = current.parent;
    }
    return schema;
};
const resolveFilterOperator = function(schema, path) {
    let operators = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {}, getOperatorList = arguments.length > 3 ? arguments[3] : void 0, collectionField = arguments.length > 4 ? arguments[4] : void 0;
    if (operators[path]) return operators[path];
    if (!getOperatorList) return;
    const fieldSchema = findSchemaByName(getFilterSchemaRoot(schema), path);
    const operatorList = getOperatorList(path, {
        fieldSchema,
        collectionField
    }) || [];
    if (!operatorList.length || !fieldSchema) return;
    return getDefaultFilterOperatorValue(fieldSchema, operatorList);
};
const isDateOnlyString = (value)=>'string' == typeof value && /^\d{4}-\d{2}-\d{2}$/.test(value);
const hasExplicitTime = (value)=>'string' == typeof value && /[T\s]\d{2}:\d{2}/.test(value);
const isDefaultDateBoundaryValue = (value, boundary)=>{
    if (isDateOnlyString(value)) return true;
    if ('string' != typeof value) return false;
    const match = value.match(/^\d{4}-\d{2}-\d{2}[T\s](\d{2}:\d{2}:\d{2})(?:\.\d{3})?(?:Z|[+-]\d\d:\d\d)?$/);
    if (!match) return false;
    return 'start' === boundary ? '00:00:00' === match[1] : '23:59:59' === match[1];
};
const normalizeDefaultDateBoundaryInput = (value)=>{
    if ('string' != typeof value) return value;
    return value.replace(/Z$/, '').replace(/[+-]\d\d:\d\d$/, '');
};
const normalizeDateBetweenBoundary = function(value, boundary) {
    let options = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {};
    const rawValue = options.useDefaultDateBoundary ? normalizeDefaultDateBoundaryInput(value) : value;
    const m = dayjs(rawValue);
    if (!m.isValid()) return value;
    if (!options.useDefaultDateBoundary && hasExplicitTime(rawValue) && !isDateOnlyString(rawValue)) return m.toISOString();
    return ('start' === boundary ? m.startOf('day') : m.endOf('day')).toISOString();
};
const shouldApplyDefaultDateBoundary = function(start, end) {
    let options = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {};
    if (!options.useDefaultDateBoundary) return false;
    if (isDateOnlyString(start) || isDateOnlyString(end)) return true;
    return isDefaultDateBoundaryValue(start, 'start') && isDefaultDateBoundaryValue(end, 'end');
};
const normalizeDateBetweenValue = function(value) {
    let options = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {};
    const normalized = value.filter(Boolean);
    if (0 === normalized.length) return null;
    const start = normalized[0];
    const end = 1 === normalized.length ? normalized[0] : normalized[normalized.length - 1];
    const boundaryOptions = {
        ...options,
        useDefaultDateBoundary: shouldApplyDefaultDateBoundary(start, end, options)
    };
    return [
        normalizeDateBetweenBoundary(start, 'start', boundaryOptions),
        normalizeDateBetweenBoundary(end, 'end', boundaryOptions)
    ];
};
const shouldUseDefaultDateBoundary = (fieldSchema)=>{
    var _fieldSchema_xcomponentprops;
    if (!fieldSchema) return false;
    const component = fieldSchema['x-component'] || (null == (_fieldSchema_xcomponentprops = fieldSchema['x-component-props']) ? void 0 : _fieldSchema_xcomponentprops.component);
    return 'DatePicker.RangePicker' === component;
};
const transformToFilter = (values, fieldSchema, getCollectionJoinField, collectionName, getOperatorList)=>{
    const { operators } = findFilterOperators(fieldSchema);
    values = flatten(values, {
        breakOn (param) {
            let { value, path } = param;
            const collectionField = getCollectionJoinField("".concat(collectionName, ".").concat(path));
            const operator = resolveFilterOperator(fieldSchema, path, operators, getOperatorList, collectionField);
            if (FILTER_OPERATORS_WITH_ARRAY_VALUES.has(operator)) return true;
            if (null == collectionField ? void 0 : collectionField.target) {
                if (Array.isArray(value)) return true;
                const targetKey = collectionField.targetKey || 'id';
                if (value && null != value[targetKey]) return true;
            }
            return false;
        }
    });
    const result = {
        $and: Object.keys(values).map((key)=>{
            const defKey = key;
            let value = lodash.get(values, key);
            const collectionField = getCollectionJoinField("".concat(collectionName, ".").concat(key));
            const currentFieldSchema = findSchemaByName(getFilterSchemaRoot(fieldSchema), defKey);
            const operator = resolveFilterOperator(fieldSchema, defKey, operators, getOperatorList, collectionField);
            if (null == collectionField ? void 0 : collectionField.target) {
                value = getValuesByPath(value, collectionField.targetKey || 'id');
                key = "".concat(key, ".").concat(collectionField.targetKey || 'id');
            }
            if (!value) return null;
            if ('$isTruly' === operator || '$isFalsy' === operator) {
                if ('true' === value) return {
                    [key]: {
                        $isTruly: true
                    }
                };
                else if ('false' === value) return {
                    [key]: {
                        $isFalsy: true
                    }
                };
            } else if ('$dateBetween' === operator) {
                if (Array.isArray(value)) {
                    value = normalizeDateBetweenValue(value, {
                        useDefaultDateBoundary: shouldUseDefaultDateBoundary(currentFieldSchema)
                    });
                    if (!value) return null;
                }
            }
            return {
                [key]: {
                    [operators[key] || operator || '$eq']: value
                }
            };
        }).filter(Boolean)
    };
    return result;
};
const useAssociatedFields = ()=>{
    const { fields } = useCollection_deprecated();
    return fields.filter((field)=>isAssocField(field)) || [];
};
const isAssocField = (field)=>[
        'o2o',
        'oho',
        'obo',
        'm2o',
        'createdBy',
        'updatedBy',
        'o2m',
        'm2m',
        'linkTo',
        'chinaRegion'
    ].includes(null == field ? void 0 : field.interface);
const isSameCollection = (c1, c2)=>c1.name === c2.name && c1.dataSource === c2.dataSource;
const useFilterAPI = ()=>{
    const fieldSchema = useFieldSchema();
    const { getDataBlocks } = useFilterBlock();
    const { targets, uid } = findFilterTargets(fieldSchema);
    const dataBlocks = getDataBlocks();
    const [isConnected, setIsConnected] = useState(()=>targets && targets.some((target)=>dataBlocks.some((dataBlock)=>dataBlock.uid === target.uid)));
    const targetsKeys = Object.keys(targets || {});
    useEffect(()=>{
        setIsConnected(targets && targets.some((target)=>dataBlocks.some((dataBlock)=>dataBlock.uid === target.uid)));
    }, [
        targetsKeys.length,
        dataBlocks
    ]);
    const doFilter = useCallback(function(value) {
        let field = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 'id', operator = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : '$eq';
        dataBlocks.forEach((block)=>{
            var _block_service_params, _block_service_params_, _block_service_params1;
            const target = targets.find((target)=>target.uid === block.uid);
            if (!target) return;
            if (lodash.isFunction(field)) field = field(target);
            if (lodash.isFunction(operator)) operator = operator(target);
            const param = (null == (_block_service_params = block.service.params) ? void 0 : _block_service_params[0]) || {};
            const storedFilter = (null == (_block_service_params1 = block.service.params) ? void 0 : null == (_block_service_params_ = _block_service_params1[1]) ? void 0 : _block_service_params_.filters) || {};
            if (void 0 !== value) storedFilter[uid] = {
                $and: [
                    {
                        [field]: {
                            [operator]: value
                        }
                    }
                ]
            };
            else delete storedFilter[uid];
            const mergedFilter = mergeFilter([
                ...Object.values(storedFilter).map((filter)=>removeNullCondition(filter)),
                getFilterSourceDefaultFilter(dataBlocks, uid),
                block.defaultFilter
            ]);
            block.doFilter({
                ...param,
                page: 1,
                filter: mergedFilter
            }, {
                filters: storedFilter
            });
        });
    }, [
        dataBlocks
    ]);
    return {
        isConnected,
        doFilter
    };
};
const isInFilterFormBlock = (fieldSchema)=>{
    while(fieldSchema){
        if (fieldSchema['x-filter-targets']) return 'FilterFormBlockProvider' === fieldSchema['x-decorator'];
        fieldSchema = fieldSchema.parent;
    }
    return false;
};
export { FILTER_OPERATORS_WITH_ARRAY_VALUES, utils_FilterBlockType as FilterBlockType, getSupportFieldsByAssociation, getSupportFieldsByForeignKey, isAssocField, isInFilterFormBlock, isSameCollection, mergeFilter, normalizeDateBetweenValue, shouldUseDefaultDateBoundary, transformToFilter, useAssociatedFields, useFilterAPI, useSupportedBlocks };
