"use client";
import React, { useState, useRef, useMemo, useCallback, PropsWithChildren } from "react";
import {
    ProfilePickerContext,
    ProfilePickerContextType,
    ProfilePickerPickParams,
    ProfilePickerEventType,
    ProfilePickerEventHandler,
    ProfilePickerEvents,
    Profile
} from "@/components/ui/profile-picker/profile-picker.context";
import { ProfilePicker } from "@/components/ui/profile-picker/profile-picker";

type ProfilePickerProviderProps = PropsWithChildren;

export const ProfilePickerProvider: React.FC<ProfilePickerProviderProps> = (props) => {
    const {
        children
    } = props;

    const [isOpen, setOpen] = useState(false),
          [params, setParams] = useState<ProfilePickerPickParams>();
    const listeners = useRef<Map<ProfilePickerEventType, Set<ProfilePickerEventHandler<ProfilePickerEventType>>>>(new Map());
    const resolveRef = useRef<(ids: string[]) => void>(null);
    const rejectRef = useRef<(err?: Error) => void>(null);

    const handleEmit = useCallback(<T extends ProfilePickerEventType>(event: T, payload: ProfilePickerEvents[T]) => {
        listeners.current.get(event)?.forEach(handler => handler(payload));
    }, []);

    const handlePick = useCallback(async (params: ProfilePickerPickParams) => {
        setOpen(true);
        setParams(params);

        return new Promise<string[]>((resolve, reject) => {
            resolveRef.current = resolve;
            rejectRef.current = reject;
        });
    }, []);

    const handleSelect = useCallback((ids: string[]) => {
        if(resolveRef.current) {
            resolveRef.current(ids);

            resolveRef.current = null;
            rejectRef.current = null;
        }

        handleEmit("select", { selected: ids });
        setOpen(false);
    }, [handleEmit]);

    const handleOpenChange = useCallback((open: boolean) => {
        if(!open && rejectRef.current) {
            rejectRef.current(new Error("Profile picker cancelled"));

            resolveRef.current = null;
            rejectRef.current = null;
        }

        setOpen(open);
    }, []);

    const handleCreate = useCallback((profile: Profile) => {
        handleEmit("create", { profile });
    }, [handleEmit]);

    const handleOn = useCallback(<T extends ProfilePickerEventType>(event: T, handler: ProfilePickerEventHandler<T>) => {
        if(!listeners.current.has(event)) {
            listeners.current.set(event, new Set());
        }

        listeners.current.get(event)?.add(handler as ProfilePickerEventHandler<ProfilePickerEventType>);
    }, []);

    const handleOff = useCallback(<T extends ProfilePickerEventType>(event: T, handler: ProfilePickerEventHandler<T>) => {
        if(!listeners.current.has(event)) {
            return;
        }

        listeners.current.get(event)?.delete(handler as ProfilePickerEventHandler<ProfilePickerEventType>);
    }, []);

    const handleEdit = useCallback(async (id: string) => {
        return handlePick({
            editId: id,
            multiple: false,
            autosubmit: true,
            selected: [id]
        });
    }, [handlePick]);

    const context = useMemo((): ProfilePickerContextType => {
        return {
            pick: handlePick,
            edit: handleEdit,
            on: handleOn,
            off: handleOff,
            emit: handleEmit
        };
    }, [handlePick, handleEdit, handleOn, handleOff, handleEmit]);

    return (
        <ProfilePickerContext value={context}>
            {children}

            <ProfilePicker
              {...params}
              open={isOpen}
              onSelect={handleSelect}
              onCreate={handleCreate}
              onOpenChange={handleOpenChange} />
        </ProfilePickerContext>
    );
};
