import { useCallback, useMemo, useState } from 'react';

import {
  GetModalDataFn,
  GetModalSourceFn,
  ModalDataMap,
  ModalOpenStateMap,
  ModalSourceMap,
  ModalTypeDataMap,
  ModalTypeNoSources,
  ModalTypes,
  OpenModalFn,
  OpenModalWithDataFn,
} from '@/components/modal/constants/ModalTypes';

/**
 * WARNING: YOU PROBABLY WANT TO `useModalContext()`!!!
 *
 * This creates a new state object to track modal open/closed states
 */
export function useAppModals() {
  // Single state object to track all modal open/closed states
  const [modalOpenStateMap, setModalOpenStateMap] = useState<ModalOpenStateMap>(
    () =>
      Object.values(ModalTypes).reduce((acc, modalType) => {
        acc[modalType] = false;
        return acc;
      }, {} as ModalOpenStateMap)
  );
  const [modalSourceMap, setModalSourceMap] = useState<ModalSourceMap>({});
  const [modalDataMap, setModalDataMap] = useState<ModalDataMap>({});

  const openModal = useCallback((modalType: ModalTypes, source?: string) => {
    setModalOpenStateMap((prev) => ({ ...prev, [modalType]: true }));
    if (source !== undefined) {
      setModalSourceMap((prev) => ({ ...prev, [modalType]: source }));
    }
  }, []) as OpenModalFn;

  const openModalWithData = useCallback(
    (modalType: ModalTypes, data?: any, source?: string) => {
      setModalOpenStateMap((prev) => ({ ...prev, [modalType]: true }));
      if (data !== undefined) {
        setModalDataMap((prev) => ({ ...prev, [modalType]: data }));
      }
      if (source !== undefined) {
        setModalSourceMap((prev) => ({ ...prev, [modalType]: source }));
      }
    },
    []
  ) as OpenModalWithDataFn;

  const closeModal = useCallback(
    (modalType: ModalTypes) => {
      setModalOpenStateMap((prev) => ({ ...prev, [modalType]: false }));
      if (
        modalType in modalSourceMap &&
        modalSourceMap[modalType] !== undefined
      ) {
        setModalSourceMap((prev) => {
          const next = { ...prev };
          delete next[modalType];
          return next;
        });
      }
      if (modalType in modalDataMap && modalDataMap[modalType] !== undefined) {
        setModalDataMap((prev) => {
          const next = { ...prev };
          delete next[modalType];
          return next;
        });
      }
    },
    [modalSourceMap, modalDataMap]
  );

  const toggleModal = useCallback((modalType: ModalTypes) => {
    setModalOpenStateMap((prev) => ({
      ...prev,
      [modalType]: !prev[modalType],
    }));
  }, []);

  const modals = useMemo(() => {
    const result = {} as Record<
      ModalTypes,
      {
        isOpen: boolean;
        onOpen: () => void;
        onClose: () => void;
        onToggle: () => void;
      }
    >;

    Object.values(ModalTypes).forEach((modalType) => {
      result[modalType] = {
        isOpen: modalOpenStateMap[modalType],
        onOpen: () => openModal(modalType as ModalTypeNoSources),
        onClose: () => closeModal(modalType),
        onToggle: () => toggleModal(modalType),
      };
    });

    return result;
  }, [modalOpenStateMap, openModal, closeModal, toggleModal]);

  const isModalOpen = useCallback(
    (modalType: ModalTypes) => {
      return modalOpenStateMap[modalType];
    },
    [modalOpenStateMap]
  );

  const getModalSource = useCallback(
    (modalType: ModalTypes) => {
      return modalSourceMap[modalType];
    },
    [modalSourceMap]
  ) as GetModalSourceFn;

  const getModalData = useCallback(
    (modalType: ModalTypes) => {
      return modalType in modalDataMap
        ? modalDataMap[modalType as keyof ModalTypeDataMap]
        : undefined;
    },
    [modalDataMap]
  ) as GetModalDataFn;

  return useMemo(
    () => ({
      modals,
      isModalOpen,
      openModal,
      openModalWithData,
      closeModal,
      getModalSource,
      getModalData,
    }),
    [
      modals,
      isModalOpen,
      openModal,
      openModalWithData,
      closeModal,
      getModalSource,
      getModalData,
    ]
  );
}
