"use client";
import React, { useState, useMemo, useCallback, useEffect, isValidElement, ReactNode, Children } from "react";
import { SelectProps as RootProps } from "@radix-ui/react-select";
import { Popover } from "@/components/ui/popover";
import { SelectContext } from "@/components/ui/field/select/select.context";
import { SelectContent, SelectContentProps } from "@/components/ui/field/select/select-content";
import { SelectItem, SelectItemProps } from "@/components/ui/field/select/select-item";

type SingleProps = Omit<RootProps, "value" | "defaultValue" | "onValueChange"> & {
    value?: string;
    defaultValue?: string;
    onValueChange?: (value: string) => void;
};

type MultipleProps = Omit<RootProps, "value" | "defaultValue" | "onValueChange"> & {
    value?: string[];
    defaultValue?: string[];
    onValueChange?: (value: string[]) => void;
};

type SelectProps =
    | ({ multiple?: false; } & SingleProps)
    | ({ multiple: true; } & MultipleProps);

export const Select = (props: SelectProps) => {
    const {
        value,
        multiple = false,
        children,
        onValueChange,
        ...rest
    } = props;

    const values = useMemo(() => {
        if(Array.isArray(value)) {
            return value;
        }

        if(!value) {
            return [];
        }

        return [value];
    }, [value]);

    const [open, setOpen] = useState(props.open ?? props.defaultOpen ?? false);
    const [internalValues, setInternalValues] = useState<Set<string>>(
        new Set<string>(
            multiple
                ? ((props.value as string[]) ?? (props.defaultValue as string[]) ?? []) 
                : (props.value ? [props.value as string] : (props.defaultValue ? [props.defaultValue as string] : []))
        )
    );

    const [items, setItems] = useState<Map<string, ReactNode>>(new Map());

    const toggleValue = useCallback((value: string) => {
        setInternalValues((prev) => {
            let state = new Set(prev);

            if(!multiple) {
                state = state.has(value) ? new Set() : new Set([value]);
            }
            else {
                if(state.has(value)) {
                    state.delete(value);
                }
                else {
                    state.add(value);
                }
            }

            if(onValueChange) {
                if(multiple) {
                    (onValueChange as MultipleProps["onValueChange"])!(Array.from(state));
                }
                else {
                    (onValueChange as SingleProps["onValueChange"])!(Array.from(state).pop() ?? "");
                }
            }

            return state;
        });
    }, [multiple, onValueChange]);

    const onItemAdded = useCallback((value: string, label: ReactNode) => {
        setItems(prev => {
            if(prev.get(value) === label)
                return prev;

            return new Map(prev).set(value, label);
        });
    }, []);

    const handleOpenChange = (newOpen: boolean) => {
        setOpen(newOpen);
        props.onOpenChange?.(newOpen);
    };

    useEffect(() => {
        setInternalValues(new Set(values));
    }, [values]);

    useEffect(() => {
        const findAndRegisterItems = (nodes: ReactNode) => {
            Children.forEach(nodes, (child) => {
                if(!isValidElement(child)) return;

                if(child.type === SelectItem) {
                    onItemAdded(
                        (child.props as SelectItemProps).value,
                        (child.props as SelectItemProps).children
                    );
                }
                else if(child.props && (child.props as { children?: ReactNode }).children) {
                    findAndRegisterItems((child.props as { children?: ReactNode }).children);
                }
            });
        };

        findAndRegisterItems(children);
    }, [children, onItemAdded]);

    return (
        <SelectContext.Provider
          value={{
            multiple,
            open,
            setOpen: handleOpenChange,
            selectedValues: internalValues,
            toggleValue,
            items,
            onItemAdded
          }}>
            <Popover
              open={open}
              onOpenChange={handleOpenChange}>
                {children}
            </Popover>
        </SelectContext.Provider>
    );
};
