"use client";
import { findTreeNode, getAllChildrenNodes, getChildrenNodesValues } from "./get-children-nodes-values/get-children-nodes-values.mjs";
import { getAllCheckedNodes } from "./get-all-checked-nodes/get-all-checked-nodes.mjs";
import { memoizedIsNodeChecked } from "./is-node-checked/is-node-checked.mjs";
import { memoizedIsNodeIndeterminate } from "./is-node-indeterminate/is-node-indeterminate.mjs";
import { useCallback, useMemo, useRef, useState } from "react";
import { useUncontrolled } from "@mantine/hooks";
//#region packages/@mantine/core/src/components/Tree/use-tree.ts
function getInitialTreeExpandedState(initialState, data, value, acc = {}) {
	data.forEach((node) => {
		acc[node.value] = node.value in initialState ? initialState[node.value] : node.value === value;
		if (Array.isArray(node.children)) getInitialTreeExpandedState(initialState, node.children, value, acc);
	});
	return acc;
}
function getTreeExpandedState(data, expandedNodesValues) {
	const state = getInitialTreeExpandedState({}, data, []);
	if (expandedNodesValues === "*") {
		const result = {};
		const keys = Object.keys(state);
		for (let i = 0; i < keys.length; i++) result[keys[i]] = true;
		return result;
	}
	expandedNodesValues.forEach((node) => {
		state[node] = true;
	});
	return state;
}
function getInitialCheckedState(initialState, data, checkStrictly) {
	if (checkStrictly) return initialState;
	const acc = [];
	initialState.forEach((node) => acc.push(...getChildrenNodesValues(node, data)));
	return Array.from(new Set(acc));
}
function getAllNodeValues(data) {
	const acc = [];
	for (const node of data) {
		acc.push(node.value);
		if (Array.isArray(node.children) && node.children.length > 0) acc.push(...getAllNodeValues(node.children));
	}
	return acc;
}
function useTree({ initialSelectedState = [], expandedState, initialCheckedState = [], checkedState, initialExpandedState = {}, selectedState, multiple = false, onNodeCollapse, onNodeExpand, onCheckedStateChange, onSelectedStateChange, onExpandedStateChange, onLoadChildren, checkStrictly = false } = {}) {
	const [data, setData] = useState([]);
	const [_expandedState, setExpandedState] = useUncontrolled({
		value: expandedState,
		defaultValue: initialExpandedState,
		finalValue: {},
		onChange: onExpandedStateChange
	});
	const [_selectedState, setSelectedState] = useUncontrolled({
		value: selectedState,
		defaultValue: initialSelectedState,
		finalValue: [],
		onChange: onSelectedStateChange
	});
	const [_checkedState, setCheckedState] = useUncontrolled({
		value: checkedState,
		defaultValue: initialCheckedState,
		finalValue: [],
		onChange: onCheckedStateChange
	});
	const [anchorNode, setAnchorNode] = useState(null);
	const loadingNodesRef = useRef(/* @__PURE__ */ new Set());
	const loadedNodesRef = useRef(/* @__PURE__ */ new Set());
	const [loadingNodes, setLoadingNodes] = useState([]);
	const [loadErrors, setLoadErrors] = useState({});
	const initialize = useCallback((_data) => {
		setExpandedState(getInitialTreeExpandedState(_expandedState, _data, _selectedState));
		setCheckedState(getInitialCheckedState(_checkedState, _data, checkStrictly));
		setData(_data);
	}, [
		_selectedState,
		_checkedState,
		_expandedState,
		checkStrictly
	]);
	const loadNodeImpl = useCallback(async (value) => {
		if (!onLoadChildren) return;
		if (loadingNodesRef.current.has(value) || loadedNodesRef.current.has(value)) return;
		loadingNodesRef.current.add(value);
		setLoadingNodes(Array.from(loadingNodesRef.current));
		setLoadErrors((prev) => {
			if (!(value in prev)) return prev;
			const next = { ...prev };
			delete next[value];
			return next;
		});
		try {
			await onLoadChildren(value);
			loadedNodesRef.current.add(value);
		} catch (error) {
			const err = error instanceof Error ? error : new Error(String(error));
			setLoadErrors((prev) => ({
				...prev,
				[value]: err
			}));
		} finally {
			loadingNodesRef.current.delete(value);
			setLoadingNodes(Array.from(loadingNodesRef.current));
		}
	}, [onLoadChildren]);
	const tryLoadAsync = useCallback((value) => {
		if (!onLoadChildren) return;
		const node = findTreeNode(value, data);
		if (node && node.hasChildren && !Array.isArray(node.children)) loadNodeImpl(value);
	}, [
		onLoadChildren,
		data,
		loadNodeImpl
	]);
	const toggleExpanded = useCallback((value) => {
		const nextState = {
			..._expandedState,
			[value]: !_expandedState[value]
		};
		nextState[value] ? onNodeExpand?.(value) : onNodeCollapse?.(value);
		if (nextState[value]) tryLoadAsync(value);
		setExpandedState(nextState);
	}, [
		onNodeCollapse,
		onNodeExpand,
		_expandedState,
		tryLoadAsync
	]);
	const collapse = useCallback((value) => {
		if (_expandedState[value] !== false) onNodeCollapse?.(value);
		setExpandedState({
			..._expandedState,
			[value]: false
		});
	}, [onNodeCollapse, _expandedState]);
	const expand = useCallback((value) => {
		if (_expandedState[value] !== true) onNodeExpand?.(value);
		tryLoadAsync(value);
		setExpandedState({
			..._expandedState,
			[value]: true
		});
	}, [
		onNodeExpand,
		_expandedState,
		tryLoadAsync
	]);
	const expandAllNodes = useCallback(() => {
		const nextState = { ..._expandedState };
		Object.keys(nextState).forEach((key) => {
			nextState[key] = true;
			tryLoadAsync(key);
		});
		setExpandedState(nextState);
	}, [_expandedState, tryLoadAsync]);
	const collapseAllNodes = useCallback(() => {
		const nextState = { ..._expandedState };
		Object.keys(nextState).forEach((key) => {
			nextState[key] = false;
		});
		setExpandedState(nextState);
	}, [_expandedState]);
	const toggleSelected = useCallback((value) => {
		if (!multiple) {
			if (_selectedState.includes(value)) {
				setAnchorNode(null);
				return [];
			}
			setAnchorNode(value);
			return [value];
		}
		if (_selectedState.includes(value)) {
			setAnchorNode(null);
			return _selectedState.filter((item) => item !== value);
		}
		setAnchorNode(value);
		setSelectedState([..._selectedState, value]);
	}, [_selectedState]);
	const select = useCallback((value) => {
		setAnchorNode(value);
		setSelectedState(multiple ? _selectedState.includes(value) ? _selectedState : [..._selectedState, value] : [value]);
	}, [_selectedState]);
	const deselect = useCallback((value) => {
		anchorNode === value && setAnchorNode(null);
		setSelectedState(_selectedState.filter((item) => item !== value));
	}, [_selectedState]);
	const clearSelected = useCallback(() => {
		setSelectedState([]);
		setAnchorNode(null);
	}, []);
	const checkNode = useCallback((value) => {
		if (checkStrictly) {
			if (!_checkedState.includes(value)) setCheckedState([..._checkedState, value]);
		} else {
			const checkedNodes = getChildrenNodesValues(value, data);
			setCheckedState(Array.from(new Set([..._checkedState, ...checkedNodes])));
		}
	}, [
		data,
		_checkedState,
		checkStrictly
	]);
	const uncheckNode = useCallback((value) => {
		if (checkStrictly) setCheckedState(_checkedState.filter((item) => item !== value));
		else {
			const checkedNodes = getChildrenNodesValues(value, data);
			setCheckedState(_checkedState.filter((item) => !checkedNodes.includes(item)));
		}
	}, [
		data,
		_checkedState,
		checkStrictly
	]);
	const checkAllNodes = useCallback(() => {
		if (checkStrictly) setCheckedState(getAllNodeValues(data));
		else setCheckedState(getAllChildrenNodes(data));
	}, [data, checkStrictly]);
	const uncheckAllNodes = useCallback(() => {
		setCheckedState([]);
	}, []);
	const getCheckedNodes = useCallback(() => {
		if (checkStrictly) return _checkedState.map((value) => {
			const node = findTreeNode(value, data);
			return {
				checked: true,
				indeterminate: false,
				value,
				hasChildren: node ? Array.isArray(node.children) && node.children.length > 0 || !!node.hasChildren : false
			};
		});
		return getAllCheckedNodes(data, _checkedState).result;
	}, [
		checkStrictly,
		_checkedState,
		data
	]);
	const isNodeChecked = useCallback((value) => {
		if (checkStrictly) return _checkedState.includes(value);
		return memoizedIsNodeChecked(value, data, _checkedState);
	}, [
		checkStrictly,
		_checkedState,
		data
	]);
	const isNodeIndeterminate = useCallback((value) => {
		if (checkStrictly) return false;
		return memoizedIsNodeIndeterminate(value, data, _checkedState);
	}, [
		checkStrictly,
		_checkedState,
		data
	]);
	const isNodeLoading = useCallback((value) => loadingNodes.includes(value), [loadingNodes]);
	const getNodeLoadError = useCallback((value) => loadErrors[value] || null, [loadErrors]);
	const invalidateNode = useCallback((value) => {
		loadedNodesRef.current.delete(value);
		setLoadErrors((prev) => {
			if (!(value in prev)) return prev;
			const next = { ...prev };
			delete next[value];
			return next;
		});
	}, []);
	return useMemo(() => ({
		checkStrictly,
		multiple,
		expandedState: _expandedState,
		selectedState: _selectedState,
		checkedState: _checkedState,
		anchorNode,
		initialize,
		toggleExpanded,
		collapse,
		expand,
		expandAllNodes,
		collapseAllNodes,
		setExpandedState,
		checkNode,
		uncheckNode,
		checkAllNodes,
		uncheckAllNodes,
		setCheckedState,
		toggleSelected,
		select,
		deselect,
		clearSelected,
		setSelectedState,
		getCheckedNodes,
		isNodeChecked,
		isNodeIndeterminate,
		isNodeLoading,
		getNodeLoadError,
		loadNode: loadNodeImpl,
		invalidateNode
	}), [
		checkStrictly,
		multiple,
		_expandedState,
		_selectedState,
		_checkedState,
		anchorNode,
		initialize,
		toggleExpanded,
		collapse,
		expand,
		expandAllNodes,
		collapseAllNodes,
		setExpandedState,
		checkNode,
		uncheckNode,
		checkAllNodes,
		uncheckAllNodes,
		setCheckedState,
		toggleSelected,
		select,
		deselect,
		clearSelected,
		setSelectedState,
		getCheckedNodes,
		isNodeChecked,
		isNodeIndeterminate,
		isNodeLoading,
		getNodeLoadError,
		loadNodeImpl,
		invalidateNode
	]);
}
//#endregion
export { getTreeExpandedState, useTree };

//# sourceMappingURL=use-tree.mjs.map