import {BranchData, BranchNode, StateBranchNode} from "./StateBranchNode";
import {getGroveForReading, Grove, TreeNodeData} from "./Grove";
import {OfferData, TestableData, useNodesStore, NodesStore} from "./store";
import {TreeNode} from "./TreeNode";
import {useShallow} from "zustand/react/shallow";
import {RefObject} from "react";
import {useRaf} from "react-use";
import {TestableCompositeNode} from "./TestableCompositeNode";

export const useStoreAPI = <K extends keyof NodesStore>(method: K) : NodesStore[K] => {
    return useNodesStore((state) => state[method]);
}
export const useBranch = (id: BranchData['id']) : StateBranchNode | undefined => {
    const branch: { current: StateBranchNode | undefined } = {
        current: undefined
    }
    /**
     * but this will, we're returning the branch data only to trigger a re-render
     * when the branch data changes
     */
    useNodesStore(store => {
        const grove = Grove.fromState(store);
        branch.current = grove.get<StateBranchNode>(id);

        if (!branch.current) {
            console.error('useBranch: branch not found', id);
            return;
        }

        // This will trigger a re-render when the branch data changes
        return branch.current.data;
    })

    return branch.current;
}

export const useTreeNode = (id: TreeNodeData['id']) : TreeNode | undefined => {
    const treeNode: { current: TreeNode | undefined } = {
        current: undefined
    }
    /**
     * re-renders when the branch testable data changes
     */
    useNodesStore(store => {
        const grove = Grove.fromState(store);
        treeNode.current = grove.get<TreeNode>(id);

        if (!treeNode.current) {
            console.error('useTreeNode: tree node not found', id);
            return;
        }

        // will probably want to return the tree node object from the grove so that changes to the tree node data will trigger a re-render
        return treeNode.current.getId();
    })

    return treeNode.current

}
export const useTreeNodeTestable = (id: TestableData['id']) : TestableCompositeNode | undefined => {
    const testableNode: { current: TestableCompositeNode | undefined } = {
        current: undefined
    }
    /**
     * re-renders when the branch testable data changes
     */
    useNodesStore(store => {
        const grove = Grove.fromState(store);
        testableNode.current = grove.get<TestableCompositeNode>(id);

        if (!testableNode.current) {
            console.error('useTreeNodeTestable: testable node not found', id);
            return;
        }

        // This will trigger a re-render when the branch data changes
        // getData() should return the same reference to the data object in the store
        return testableNode.current.getTestable().getData();
    })

    return testableNode.current

}

/**
 * Will only re-render when the number of children of the branch changes.
 * @param id
 */
export const useBranchChildrenShallow = <ChildrenType = OfferData | TreeNode>(id: BranchData['id']) : ChildrenType[] => {
    const branch: { current: StateBranchNode | undefined } = {
        current: undefined
    }
    const grove: { current: Grove | undefined } = {
        current: undefined
    }
    /**
     * This will trigger a re-render when the children of the branch change.
     */
    const childIds = useNodesStore(useShallow(store => {
        grove.current = Grove.fromState(store)

        branch.current = grove.current.get<StateBranchNode>(id)

        return branch.current ? branch.current.getChildrenIds() : [];
    }))

    const getOffer = useNodesStore(store => store.getOffer)

    if (!branch.current) {
        return []
    }

    return branch.current.getChildren((child => {
        if (branch.current!.isOffers()) {
            return getOffer(child as string)
        }

        return grove.current!.get<TreeNode>((child as TreeNodeData).id)
    })).filter(v => !!v) as ChildrenType[]
}

