import type { ReactElement, FC } from "react";
import {
  useImperativeHandle,
  useEffect,
  useRef,
  useMemo,
  useLayoutEffect,
  useState, useCallback,
} from "react";
import type {
  IModal, TModals, TModalInstance, TModalRef, TModalContext,
} from "./config/types";
import { ModalContext } from "./context";
import { ModalCtrl } from "./ui/ModalCtrl";
import { ModalInner } from "./ui/ModalInner";
import { ModalWrapper } from "./ui/ModalWrapper";
import { Core } from "../../plugins/core";
import type { TAction } from "../../plugins/modals/config/types";
import type { IComponentBaseProps } from "../../types/components/index";
import { useCN } from "../../utils/react/useCN";
import { useIsSSR } from "../../utils/react/useIsSSR";
import "./style.pcss";

/**
 * Компонент модального окна
 * @returns {ReactElement}
 */
const Modal: FC<IModal & IComponentBaseProps> = ({
  baseClass = "modal",
  extraClasses = {},
  extraAttrs = {},
  children = null,
  id,
  ref,
  isOpen = false,
  isInline = false,
  footerSlot = null,
  headerSlot = null,
  titleSlot = null,
  ctrlSlot = null,
  onAction = null,
  async,
  assets,
  fancyboxCfg,
  preset,
  cfg = {},
}): ReactElement => {

  // SSR mode?
  const { isSSR } = useIsSSR();

  // Wrapper ref
  const templateRef = useRef(null);

  // Fancybox
  const plugin = useMemo<TModalInstance>(() => {
    return (Core.instance as unknown as Core)?.getPluginInstance("Modals") as TModalInstance;
  }, []);

  // Actual state
  const [ isModalOpen, setIsModalOpen ] = useState<boolean>(isOpen);

  // Opening state
  const [ isLoading, setIsLoading ] = useState<boolean>(false);

  // Prev state
  const prevOpen = useRef<boolean>(isModalOpen);

  // Parsed config
  const parsedCfg = useMemo<string>(() => {
    const {
      redirectAfterClose = null, isReloadAfterClose = false,
    } = cfg;
    return JSON.stringify(({
      redirectAfterClose, isReloadAfterClose,
    }));
  }, [ cfg ]);

  // Classnames
  const { getCN } = useCN(baseClass);

  // Modal action handler
  const onActionHandler = useCallback((data: TAction) => {
    const { action } = data;
    if (action === "close") {
      setIsLoading(false);
      setIsModalOpen(false);
      prevOpen.current = false;
    }
    if (typeof onAction === "function") {
      onAction(data);
    }
  }, [ onAction ]);

  useEffect(() => {
    if (isOpen !== prevOpen.current) {
      setIsModalOpen(isOpen);
    }
  }, [ isOpen ]);

  useLayoutEffect(() => {
    if (isModalOpen !== prevOpen.current && !isLoading) {
      if (plugin) {
        const staticMethods = (plugin.constructor as TModals);
        if (isModalOpen) { // open modal via Fancybox
          if (!staticMethods.isOpen(id)) {
            staticMethods.getOpen({
              id,
              src: "#" + id,
              assets,
              async,
              type: "inline" as const,
              onAction: onActionHandler,
              fancyboxCfg,
            })
              .then(() => {
                prevOpen.current = true;
                return Promise.resolve();
              })
              .catch(() => {
                prevOpen.current = false;
                return Promise.reject();
              })
              .finally(() => {
                setIsLoading(false);
              });
          }
        } else { // close modal via Fancybox
          if (staticMethods.isOpen(id)) {
            staticMethods.getClose(id)
              .catch(() => {
                return Promise.resolve();
              })
              .finally(() => {
                prevOpen.current = false;
              });
          }
        }
      } else {
        console.error(`[Modals] Attempt to use "Modal" element without plugin. Please run "Modals" in "Core" first`);
      }
    }
  }, [ isModalOpen, isLoading ]);

  useLayoutEffect(() => {
    return () => {
      if (plugin) {
        const staticMethods = (plugin.constructor as TModals);
        if (staticMethods.isOpen(id)) {
          staticMethods.getClose(id)
            .catch(() => {
              return Promise.resolve();
            })
            .finally(() => {
              setIsLoading(false);
            });
        }
      }
    };
  }, []);

  useImperativeHandle(ref, (): TModalRef => ({
    el: templateRef.current,
    ref: { current: null },
    utils: null,
    getData: () => null,
    stateActions: {
      setIsOpen: (isOpen = true) => setIsModalOpen(isOpen),
    },
  }), []);

  const contextValue = useMemo<TModalContext>(() => ({
    getCN, id, plugin, cfg, footerSlot, titleSlot, headerSlot, preset,
  }), [ plugin, parsedCfg, footerSlot, titleSlot, headerSlot, preset ]);

  return (
    <ModalContext value={contextValue}>
      <ModalWrapper isInline={isSSR ? isInline : true} ref={templateRef} hidden={true} aria-hidden={"true"} id={id}
        data-js-modal={parsedCfg} className={getCN("", extraClasses)} {...extraAttrs}>
        <div className={getCN("inner")}>
          <ModalCtrl>{ctrlSlot}</ModalCtrl>
          <div className={getCN("body")}>
            <ModalInner>
              {children}
            </ModalInner>
          </div>
        </div>
      </ModalWrapper>
    </ModalContext>
  );
};

export type { IModal };

export {
  Modal,
};
