import {
    type ReactNode,
    createContext,
    useEffect,
    useRef
} from "react";
import {
    type ViewStyle,
    View
} from "react-native";
import {
    useKey
} from "./hooks/useKey";
import {
    type IManagerHandles,
    Manager
} from "./Manager";
import {
    context
} from "./context";

interface IHostProps {
    children: ReactNode;
    style?: ViewStyle;
    name?: string;
}

export interface IProvider {
    update(key?: string, children?: ReactNode, name?: string): void;
    mount(children: ReactNode, name?: string): string;
    unmount(key?: string): void;
    name?: string;
}

export const Context = createContext<IProvider | null>(null);

export const Host = ({
    children,
    style,
    name
}: IHostProps): ReactNode => {
    const managerRef = useRef<IManagerHandles>(null);

    const queue: Array<{
        type: "mount" | "update" | "unmount";
        children?: ReactNode;
        name?: string;
        key: string;
    }> = [];

    const {
        generateKey,
        removeKey
    } = useKey();

    useEffect(() => {
        while (queue.length && managerRef.current) {
            const action = queue.pop();

            if (action) {
                switch (action.type) {
                    case "mount":
                        managerRef.current?.mount(action.key, action.children, action.name);
                        break;
                    case "update":
                        managerRef.current?.update(action.key, action.children, action.name);
                        break;
                    case "unmount":
                        managerRef.current?.unmount(action.key);
                        break;
                }
            }
        }
    }, []);

    const mount = (children: ReactNode, _name?: string): string => {
        const key = generateKey();

        const targetName = _name ?? name;

        context.mount(key, children, targetName);

        return key;
    };

    const update = (key: string, children: ReactNode, _name?: string): void => {
        const targetName = _name ?? name;

        context.update(key, children, targetName);
    };

    const unmount = (key: string): void => {
        context.unmount(key);

        removeKey(key);
    };

    return <Context.Provider value={{
        unmount,
        update,
        mount,
        name
    }}>
        <View
            collapsable={false}
            style={[
                {
                    pointerEvents: "box-none",
                    flex: 1
                },
                style
            ]}
        >
            {children}
        </View>

        <Manager
            ref={managerRef}
            name={name}
        />
    </Context.Provider>;
};
