import { deepCopy } from "../../../utils";
// Утилита для поиска ключа в объекте по пути.
export const keyExists = (obj, path)=>{
    if (typeof obj !== 'object' || obj === null || !Array.isArray(path)) {
        return false;
    }
    let current = obj;
    for(let i = 0; i < path.length; i++){
        const key = path[i];
        if (current === null || typeof current !== 'object' || !(key in current)) {
            return false;
        }
        current = current[key];
    }
    return true;
};
function createObjectAtPath(obj, path) {
    if (typeof obj !== 'object' || obj === null) {
        return;
    }
    let current = obj;
    for(let i = 0; i < path.length; i++){
        const key = path[i];
        if (i === path.length - 1) {
            current[key] = {};
            break;
        }
        if (current[key] === undefined || current[key] === null) {
            current[key] = {};
        }
        current = current[key];
    }
    return obj;
}
function removeObjectAtPath(obj, path) {
    if (path.length === 0) {
        return obj;
    }
    let current = obj;
    for(let i = 0; i < path.length; i++){
        const key = path[i];
        if (current === null || typeof current !== 'object' || !(key in current)) {
            return obj;
        }
        if (i === path.length - 1) {
            delete current[key];
            break;
        }
        current = current[key];
    }
    return obj;
}
/**
 * Утилита, которая получает на вход список отфильтрованных элементов и
 * callback, с помощью которого эти элементы были отфильтрованы.
 * Возвращает TreePathState, т.е. список раскрытых групп.
 * По умолчанию группа не раскрывается, если в ней не было найдено ни одного элемента.
 * Но т.к. поиск может осуществляться как по элементам, так и по группам,
 * то может возникнуть ситуация, когда будет найдена как группа с определенным названием,
 * так и элемент с таким же названием внутри нее, и в таком случае мы должны частично раскрыть группу,
 * вплоть то найденного элемента.
 * Если найдена только группа, без элементов внутри нее, то саму эту группу
 * раскрывать не нужно. Нужно раскрыть только родителя этой группы.
 */ function generateNestedTreePathFromFilteredItems(filteredItems, filterCb, textValue) {
    const result = {};
    function traverse(items, currentPath) {
        let hasFoundItems = false;
        for (const item of items){
            const isFound = filterCb(item, textValue);
            if (item.items && item.items.length > 0) {
                // Обрабатываем детей
                const childPath = {};
                const foundInChildren = traverse(item.items, childPath);
                if (foundInChildren) {
                    // Если в детях найдены элементы - добавляем путь
                    currentPath[item.value] = childPath;
                    hasFoundItems = true;
                }
                // Если сама группа найдена, но в детях ничего нет - НЕ добавляем путь
                // Просто отмечаем что что-то найдено на этом уровне
                if (isFound && !foundInChildren) {
                    hasFoundItems = true;
                }
            } else if (isFound) {
                // Для конечного элемента не добавляем путь в currentPath,
                // но отмечаем что на этом уровне есть найденные элементы
                hasFoundItems = true;
            }
        }
        return hasFoundItems;
    }
    traverse(filteredItems, result);
    return result;
}
export function treePathReducer(state, action) {
    switch(action.type){
        case 'reset':
            {
                return {};
            }
        case 'toggled_level':
            {
                const stateCopy = deepCopy(state);
                const isTargetLevelOpened = keyExists(stateCopy, action.value);
                if (isTargetLevelOpened) {
                    // нужно закрыть текущий уровень
                    removeObjectAtPath(stateCopy, action.value);
                } else {
                    // нужно открыть требуемый уровень
                    createObjectAtPath(stateCopy, action.value);
                }
                return stateCopy;
            }
        case 'expand_by_key':
            {
                const { filteredItems, filterCb, textValue } = action.value;
                return generateNestedTreePathFromFilteredItems(filteredItems, filterCb, textValue);
            }
        case 'open_level':
            {
                const stateCopy = deepCopy(state);
                createObjectAtPath(stateCopy, action.value);
                return stateCopy;
            }
        case 'close_level':
            {
                const stateCopy = deepCopy(state);
                removeObjectAtPath(stateCopy, action.value);
                return stateCopy;
            }
        default:
            {
                return state;
            }
    }
}
