import {
  type FC,
  Fragment,
  type PropsWithChildren,
  useCallback,
  useReducer,
  useState,
} from 'react';

import { generateID } from '@quotalab/headless-hooks';

import {
  OverlayContextProvider,
  type OverlayContextValue,
  type OverlayControllerProps,
  useOverlayContext,
} from './context';
import { type OverlayReducerState, overlayReducer } from './reducer';
import { useOverlayEvent } from '../event/event';

export function OverlayProvider({ children }: PropsWithChildren<unknown>) {
  const [overlayState, overlayDispatch] = useReducer(overlayReducer, {
    overlayList: [],
    current: null,
  } satisfies OverlayReducerState);

  const open: OverlayContextValue['open'] = useCallback((controller) => {
    const overlayId = generateID('quotalab-overlay');
    overlayDispatch({
      type: 'OPEN',
      overlay: {
        id: overlayId,
        children: <ContentOverlayController overlayId={overlayId} controller={controller} />,
      },
    });

    return overlayId;
  }, []);
  const close: OverlayContextValue['close'] = useCallback((id: string) => {
    overlayDispatch({ type: 'CLOSE', overlayId: id });
  }, []);
  const closeAll: OverlayContextValue['closeAll'] = useCallback(() => {
    overlayDispatch({ type: 'CLOSE_ALL' });
  }, []);

  useOverlayEvent({ open, close, closeAll });

  const contextValue: OverlayContextValue = {
    overlayList: overlayState.overlayList,
    open,
    close,
    closeAll,
  };

  return (
    <OverlayContextProvider value={contextValue}>
      {children}
      {overlayState.overlayList.map((item) => {
        return <Fragment key={item.id}>{item.children}</Fragment>;
      })}
    </OverlayContextProvider>
  );
}

type ContentOverlayControllerProps = {
  overlayId: string;
  controller: FC<OverlayControllerProps>;
};

function ContentOverlayController({
  controller: Controller,
  overlayId,
}: ContentOverlayControllerProps) {
  const [isOpen, setOpen] = useState(true);
  const { close } = useOverlayContext();

  return (
    <Controller
      overlayId={overlayId}
      isOpen={isOpen}
      onClose={() => {
        setOpen(false);
        setTimeout(() => close(overlayId), 100);
      }}
    />
  );
}
