import type { FilterCondition, FilterDef, FilterGroup } from './types';

export const OPERATOR_LABELS: Record<string, string> = {
    contains: 'contient',
    not_contains: 'ne contient pas',
    equals: 'est égal à',
    not_equals: 'est différent de',
    starts_with: 'commence par',
    ends_with: 'finit par',
    is_empty: 'est vide',
    is_not_empty: "n'est pas vide",
    eq: 'égal à',
    neq: 'différent de',
    gt: 'supérieur à',
    gte: 'supérieur ou égal à',
    lt: 'inférieur à',
    lte: 'inférieur ou égal à',
    between: 'entre',
    date_is: 'le',
    date_before: 'avant le',
    date_after: 'après le',
    date_between: 'entre',
    in: 'est parmi',
    not_in: "n'est pas parmi",
    is_true: 'est vrai',
    is_false: 'est faux',
};

export const operatorNeedsValue = (operator: string): boolean =>
    !['is_empty', 'is_not_empty', 'is_true', 'is_false'].includes(operator);

export const operatorIsRange = (operator: string): boolean =>
    operator === 'between' || operator === 'date_between';

export const operatorIsMulti = (operator: string): boolean =>
    operator === 'in' || operator === 'not_in';

export const uid = (): string =>
    typeof crypto !== 'undefined' && 'randomUUID' in crypto
        ? crypto.randomUUID()
        : Math.random().toString(36).slice(2);

export const makeCondition = (filter: FilterDef): FilterCondition => ({
    id: uid(),
    type: 'condition',
    field: filter.key,
    operator: filter.operators[0],
    value: defaultValueFor(filter, filter.operators[0]),
});

export const makeGroup = (match: 'and' | 'or' = 'and'): FilterGroup => ({
    id: uid(),
    type: 'group',
    match,
    conditions: [],
});

export const defaultValueFor = (
    filter: FilterDef,
    operator: string,
): unknown => {
    if (!operatorNeedsValue(operator)) {
        return null;
    }

    if (operatorIsMulti(operator)) {
        return [];
    }

    if (operatorIsRange(operator)) {
        return { from: '', to: '' };
    }

    return '';
};

/** Nombre de conditions effectives (récursif, valeurs vides exclues). */
export const countConditions = (group: FilterGroup | null): number => {
    if (!group) {
        return 0;
    }

    return group.conditions.reduce((total, node) => {
        if (node.type === 'group') {
            return total + countConditions(node);
        }

        return total + (conditionIsComplete(node) ? 1 : 0);
    }, 0);
};

export const conditionIsComplete = (condition: FilterCondition): boolean => {
    if (!operatorNeedsValue(condition.operator)) {
        return true;
    }

    const value = condition.value;

    if (Array.isArray(value)) {
        return value.length > 0;
    }

    if (value !== null && typeof value === 'object') {
        const range = value as { from?: string; to?: string };

        return Boolean(range.from) || Boolean(range.to);
    }

    return value !== null && value !== '';
};

/** Élimine les conditions incomplètes et groupes vides avant envoi au serveur. */
export const pruneGroup = (group: FilterGroup): FilterGroup | null => {
    const conditions = group.conditions
        .map((node) => (node.type === 'group' ? pruneGroup(node) : node))
        .filter((node): node is FilterCondition | FilterGroup => {
            if (node === null) {
                return false;
            }

            return node.type === 'group' || conditionIsComplete(node);
        });

    if (conditions.length === 0) {
        return null;
    }

    return { ...group, conditions };
};

export const summarizeCondition = (
    condition: FilterCondition,
    filters: FilterDef[],
): string => {
    const filter = filters.find((item) => item.key === condition.field);
    const label = filter?.label ?? condition.field;
    const operator = OPERATOR_LABELS[condition.operator] ?? condition.operator;

    if (!operatorNeedsValue(condition.operator)) {
        return `${label} ${operator}`;
    }

    const value = condition.value;
    let display = '';

    if (Array.isArray(value)) {
        display = value
            .map(
                (item) =>
                    filter?.options?.find((option) => option.value === item)
                        ?.label ?? String(item),
            )
            .join(', ');
    } else if (value !== null && typeof value === 'object') {
        const range = value as { from?: string; to?: string };
        display = [range.from, range.to].filter(Boolean).join(' → ');
    } else {
        display = String(value ?? '');
    }

    return `${label} ${operator} ${display}`.trim();
};
