// (C) 2025 GoodData Corporation
/**
 * @internal
 **/
export function makeItemId(treeViewId, path) {
    return `${treeViewId}/${convertPathToKey(path)}`;
}
/**
 * @internal
 **/
export function getItemOnFocusedPath(items, focusedPath) {
    let currentLevel = items;
    let currentItem = undefined;
    for (let i = 0; i < focusedPath.length; i++) {
        const index = focusedPath[i];
        if (index < currentLevel.length) {
            currentItem = currentLevel[index];
            currentLevel = currentItem.children ?? [];
        }
    }
    return currentItem;
}
/**
 * @internal
 **/
export function getRefOnFocusedPath(items, focusedPath) {
    const key = convertPathToKey(focusedPath);
    return items[key];
}
/**
 * @internal
 **/
export function itemsState(state, defaultItem, opts) {
    return (path) => {
        // Setter
        const setter = (item) => {
            state[1]((st) => {
                return setItemAtPath(st, path, item, defaultItem);
            });
        };
        // Getter
        const getter = () => {
            return getItemAtPath(state[0], path, defaultItem);
        };
        // Toggle
        const toggle = (state) => {
            // Collapse all if only one root open is allowed
            if (path.length === 1 && opts.expansionMode === "single") {
                toggleAll(false);
            }
            setter({ expanded: state });
        };
        // Toggle all
        const toggleAll = (expanded) => {
            state[1]((st) => {
                const updated = Object.entries(st ?? {}).map(([key, prop]) => {
                    return [key, { ...prop, expanded }];
                });
                return Object.fromEntries(updated);
            });
        };
        return [
            getter(),
            {
                toggle,
                toggleAll,
            },
        ];
    };
}
function getItemAtPath(state, path, defaultItem) {
    const key = convertPathToKey(path);
    let current = state[key];
    if (!current) {
        current = { ...defaultItem };
        state[key] = current;
    }
    return current;
}
function setItemAtPath(state, path, newItem, defaultItem) {
    const key = convertPathToKey(path);
    return {
        ...state,
        [key]: {
            ...defaultItem,
            ...state[key],
            ...newItem,
        },
    };
}
/**
 * @internal
 **/
export function findPath(items, id, isFocusableItem) {
    for (let i = 0; i < items.length; i++) {
        if (!id && isFocusableItem(items[i])) {
            return [i];
        }
        if (items[i].item.id === id && isFocusableItem(items[i])) {
            return [i];
        }
        const children = items[i].children;
        if (!children) {
            return [i];
        }
        const childPath = findPath(children, id, isFocusableItem);
        if (childPath) {
            return [i, ...childPath];
        }
    }
    return undefined;
}
export function getPrevPathIndex(items, getState, path, isFocusableItem) {
    const map = getItemsPathEntries(items, getState);
    const currentKey = convertPathToKey(path);
    let currentIndex = map.findIndex(([key]) => key === currentKey);
    // Index not found → prefer last focusable visible item
    if (currentIndex < 0) {
        const last = map[map.length - 1];
        const [key, item, expanded] = last;
        if (isFocusableItem(item) && expanded) {
            return convertKeyToPath(key);
        }
        return getPrevPathIndex(items, getState, convertKeyToPath(key), isFocusableItem);
    }
    // Previous item index
    currentIndex--;
    let prevItem = map[currentIndex];
    while (prevItem) {
        const [key, item, expanded] = prevItem;
        if (isFocusableItem(item) && expanded) {
            return convertKeyToPath(key);
        }
        currentIndex--;
        prevItem = map[currentIndex];
    }
    const first = map[0];
    const [key, item, expanded] = first;
    if (isFocusableItem(item) && expanded) {
        return convertKeyToPath(key);
    }
    return convertKeyToPath(currentKey);
}
export function getNextPathIndex(items, getState, path, isFocusableItem) {
    const map = getItemsPathEntries(items, getState);
    const currentKey = convertPathToKey(path);
    let currentIndex = map.findIndex(([key]) => key === currentKey);
    currentIndex = currentIndex === -1 ? map.length : currentIndex;
    // Index not found → prefer first focusable visible item
    if (currentIndex > map.length - 1) {
        const first = map[0];
        const [key, item, expanded] = first;
        if (isFocusableItem(item) && expanded) {
            return convertKeyToPath(key);
        }
        return getNextPathIndex(items, getState, convertKeyToPath(key), isFocusableItem);
    }
    // Next item index
    currentIndex++;
    let nextItem = map[currentIndex];
    while (nextItem) {
        const [key, item, expanded] = nextItem;
        if (isFocusableItem(item) && expanded) {
            return convertKeyToPath(key);
        }
        currentIndex++;
        nextItem = map[currentIndex];
    }
    const last = map[map.length - 1];
    const [key, item, expanded] = last;
    if (isFocusableItem(item) && expanded) {
        return convertKeyToPath(key);
    }
    return convertKeyToPath(currentKey);
}
export function getParentPathIndex(items, getState, path, isFocusableItem) {
    const map = getItemsPathEntries(items, getState);
    const currentKey = convertPathToKey(path);
    let currentIndex = map.findIndex(([key]) => key === currentKey);
    // Index not found
    if (currentIndex <= 0) {
        return path;
    }
    // Parent item index
    const parentPath = path.slice(0, path.length - 1);
    const parentKey = convertPathToKey(parentPath);
    currentIndex = map.findIndex(([key]) => key === parentKey);
    // Index not found
    if (currentIndex < 0) {
        return path;
    }
    let parentItem = map[currentIndex];
    while (parentItem && !isFocusableItem(parentItem[1])) {
        currentIndex--;
        parentItem = map[currentIndex];
    }
    return convertKeyToPath(parentItem[0]);
}
function getItemsPathEntries(items, getState, path = [], expanded = true) {
    const entries = [];
    for (let i = 0; i < items.length; i++) {
        const itemPath = [...path, i];
        const item = items[i];
        const [state] = getState(itemPath);
        entries.push([convertPathToKey(itemPath), item, expanded]);
        entries.push(...getItemsPathEntries(item.children ?? [], getState, itemPath, expanded && (state?.expanded ?? false)));
    }
    return entries;
}
export function convertPathToKey(path) {
    return path.join("-");
}
export function convertKeyToPath(key) {
    return key.split("-").map((s) => parseInt(s, 10));
}
