import { omit } from "remeda";
import { executeFilterCode } from "../pipeline/utils/sandbox/index.js";
import { InnerRowIndex } from "../dataReshape/index.js";
function nearlyEqual(a, b, epsilon = 1e-8) {
    if (Number.isNaN(a) || Number.isNaN(b)) return false;
    if (a === b) return true;
    const diff = Math.abs(a - b);
    return diff <= epsilon;
}
const selector_selector = (vchartDatum, selector, selectorMode = 'And')=>{
    if (!selector) return true;
    const vchartKeys = Object.keys(vchartDatum).filter((k)=>k.toLocaleLowerCase().startsWith('__vchart'));
    const datum = omit(vchartDatum, vchartKeys);
    const selectors = Array.isArray(selector) ? selector : [
        selector
    ];
    return selectors['And' === selectorMode ? 'every' : 'some']((selector)=>{
        if (isValueSelector(selector)) return selectByValue(selector, datum);
        if (isFieldSelector(selector)) return selectByField(selector, datum);
        if (isMeasureSelector(selector)) return selectByMeasure(selector, datum);
        if (isDimensionSelector(selector)) return selectByDmension(selector, datum);
        if (isPartialDatumSelector(selector)) return selectByPartial(selector, datum);
        return false;
    });
};
const isValueSelector = (selector)=>'string' == typeof selector || 'number' == typeof selector;
const isPartialDatumSelector = (selector)=>'object' == typeof selector && null !== selector;
const isFieldSelector = (selector)=>'object' == typeof selector && null !== selector && 'field' in selector && !('operator' in selector) && !('op' in selector) && !('value' in selector);
const isMeasureSelector = (selector)=>'object' == typeof selector && null !== selector && 'field' in selector && ('operator' in selector || 'op' in selector) && 'value' in selector && ([
        '=',
        '==',
        '!=',
        '>',
        '<',
        '>=',
        '<=',
        'between'
    ].includes(selector.operator) || [
        '=',
        '==',
        '!=',
        '>',
        '<',
        '>=',
        '<=',
        'between'
    ].includes(selector.op));
const isDimensionSelector = (selector)=>'object' == typeof selector && null !== selector && 'field' in selector && ('operator' in selector || 'op' in selector) && 'value' in selector && ([
        'in',
        'not in'
    ].includes(selector.operator) || [
        'in',
        'not in'
    ].includes(selector.op));
const selectByMeasure = (selector, datum)=>{
    const op = selector.operator || selector.op;
    const selectorValueArr = Array.isArray(selector.value) ? selector.value : [
        selector.value
    ];
    switch(op){
        case '=':
            if (String(datum[selector.field]) === String(selectorValueArr[0]) || nearlyEqual(Number(datum[selector.field]), Number(selectorValueArr[0]))) return true;
            break;
        case '==':
            if (datum[selector.field] === selectorValueArr[0]) return true;
            break;
        case '!=':
            if (datum[selector.field] !== selectorValueArr[0]) return true;
            break;
        case '>':
            if (datum[selector.field] > selectorValueArr[0] && !nearlyEqual(Number(datum[selector.field]), Number(selectorValueArr[0]))) return true;
            break;
        case '<':
            if (datum[selector.field] < selectorValueArr[0] && !nearlyEqual(Number(datum[selector.field]), Number(selectorValueArr[0]))) return true;
            break;
        case '>=':
            if (datum[selector.field] >= selectorValueArr[0] || nearlyEqual(Number(datum[selector.field]), Number(selectorValueArr[0]))) return true;
            break;
        case '<=':
            if (datum[selector.field] <= selectorValueArr[0] || nearlyEqual(Number(datum[selector.field]), Number(selectorValueArr[0]))) return true;
            break;
        case 'between':
            if (Array.isArray(selector.value) && (datum[selector.field] >= selectorValueArr[0] || nearlyEqual(Number(datum[selector.field]), Number(selectorValueArr[0]))) && (datum[selector.field] <= selectorValueArr[1] || nearlyEqual(Number(datum[selector.field]), Number(selectorValueArr[1])))) return true;
            break;
    }
    return false;
};
const selectByDmension = (selector, datum)=>{
    const op = selector.operator || selector.op;
    const selectorValueArr = Array.isArray(selector.value) ? selector.value : [
        selector.value
    ];
    switch(op){
        case 'in':
            if (selectorValueArr.includes(datum[selector.field])) return true;
            break;
        case 'not in':
            if (!selectorValueArr.includes(datum[selector.field])) return true;
            break;
    }
    return false;
};
const selectByField = (selector, datum)=>{
    const fields = Array.isArray(selector.field) ? selector.field : [
        selector.field
    ];
    const datumKeys = Object.keys(datum);
    return fields.some((field)=>datumKeys.includes(field));
};
const matchesFieldSelector = (field, fieldSelector)=>{
    const fields = Array.isArray(fieldSelector.field) ? fieldSelector.field : [
        fieldSelector.field
    ];
    return fields.includes(field);
};
const selectByPartial = (selector, datum)=>Object.keys(selector).every((key)=>datum[key] === selector[key]);
const selectByValue = (selector, datum)=>Object.values(datum).some((v)=>v === selector);
const matchesCellSelector = (cell, filterRes)=>{
    if (filterRes[InnerRowIndex] !== cell[InnerRowIndex]) return false;
    return '*' === filterRes.field || Object.keys(cell).includes(filterRes.field);
};
const matchesDatum = (target, candidate)=>Object.keys(candidate).every((key)=>target[key] === candidate[key]);
const isDynamicFilterLike = (selector, expectedTypes)=>'object' == typeof selector && null !== selector && 'type' in selector && expectedTypes.includes(selector.type) && 'code' in selector && 'string' == typeof selector.code;
const isRowWithFieldDynamicFilter = (selector)=>isDynamicFilterLike(selector, [
        'row-with-field'
    ]);
const isValueDynamicFilter = (selector)=>isDynamicFilterLike(selector, [
        'value'
    ]);
const isDynamicFilter = (selector)=>isDynamicFilterLike(selector, [
        'row-with-field',
        'value'
    ]);
const validateFilterResult = (result, filter)=>{
    if (isValueDynamicFilter(filter)) {
        if ('number' != typeof result && 'string' != typeof result) throw new TypeError(`ValueDynamicFilter must return a number or string, but got: ${typeof result}. Code: "${filter.code}"`);
        return;
    }
    if (!Array.isArray(result)) throw new TypeError(`${isRowWithFieldDynamicFilter(filter) ? 'TableDynamicFilter' : 'ChartDynamicFilter'} must return an array, but got: ${typeof result}. Code: "${filter.code}"`);
    if (isRowWithFieldDynamicFilter(filter)) for(let i = 0; i < result.length; i++){
        const item = result[i];
        if ('object' != typeof item || null === item) throw new TypeError(`TableDynamicFilter array element at index ${i} must be an object, got: ${typeof item}`);
        if (!(InnerRowIndex in item) && '__row_index' in item === false) throw new TypeError(`TableDynamicFilter array element at index ${i} must contain __row_index or InnerRowIndex field`);
        if (!('field' in item)) throw new TypeError(`TableDynamicFilter array element at index ${i} must contain 'field' field`);
    }
};
const executeDynamicFilter = async (filter, allData)=>{
    try {
        const { success, data, error } = await executeFilterCode({
            code: filter.code,
            data: allData
        });
        if (!success) {
            console.warn('[vseed] Dynamic filter execution failed:', error);
            return {
                success: false,
                data: isRowWithFieldDynamicFilter(filter) ? [] : '',
                error
            };
        }
        try {
            validateFilterResult(data, filter);
        } catch (validationError) {
            console.error('[vseed] Dynamic filter result validation failed:', validationError);
            return {
                success: false,
                data: isRowWithFieldDynamicFilter(filter) ? [] : '',
                error: validationError instanceof Error ? validationError.message : String(validationError)
            };
        }
        return {
            success,
            data: data
        };
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        console.error('[vseed] Dynamic filter execution threw exception:', errorMessage);
        return {
            success: false,
            data: isRowWithFieldDynamicFilter(filter) ? [] : '',
            error: errorMessage
        };
    }
};
const matchDynamicFilterResult = (result, datum, selectorType = 'table')=>{
    if ('number' == typeof result || 'string' == typeof result) throw new Error('matchDynamicFilterResult does not support ValueDynamicFilter results');
    if ('table' === selectorType) return result.some((item)=>matchesCellSelector(datum, item));
    return result.some((item)=>matchesDatum(datum, item));
};
const selectorWithDynamicFilter = (vchartDatum, selectorConfig, defaultSelector)=>{
    if (!selectorConfig) return true;
    if (isValueDynamicFilter(selectorConfig)) {
        if (selectorConfig.fallback) {
            const fallbackSelector = Array.isArray(selectorConfig.fallback) ? selectorConfig.fallback : [
                selectorConfig.fallback
            ];
            return selector_selector(vchartDatum, fallbackSelector);
        }
        return defaultSelector ? selector_selector(vchartDatum, defaultSelector) : false;
    }
    const selectorType = isRowWithFieldDynamicFilter(selectorConfig) ? 'table' : 'chart';
    if (selectorConfig.result?.success && selectorConfig.result.data) return matchDynamicFilterResult(selectorConfig.result.data, vchartDatum, selectorType);
    if (selectorConfig.fallback) {
        const fallbackSelector = Array.isArray(selectorConfig.fallback) ? selectorConfig.fallback : [
            selectorConfig.fallback
        ];
        return selector_selector(vchartDatum, fallbackSelector);
    }
    return defaultSelector ? selector_selector(vchartDatum, defaultSelector) : false;
};
export { executeDynamicFilter, isDimensionSelector, isDynamicFilter, isFieldSelector, isMeasureSelector, isPartialDatumSelector, isRowWithFieldDynamicFilter, isValueDynamicFilter, isValueSelector, matchDynamicFilterResult, matchesFieldSelector, selectByDmension, selectByField, selectByMeasure, selectByPartial, selectByValue, selectorWithDynamicFilter, selector_selector as selector };

//# sourceMappingURL=selector.js.map