import { Variant, AnimatePresence, m } from "framer-motion";
import React from "react";

const SwitchableComponentContext = React.createContext<
  SwitchableComponentContextType | undefined
>(undefined);

export const SwitchableComponentProvider = ({
  children,
}: React.PropsWithChildren) => {
  const [visibleComponent, setVisibleComponent] =
    React.useState<SwitchableComponentType>();

  return (
    <SwitchableComponentContext.Provider
      value={{
        visibleComponent: visibleComponent ?? DEFAULT_COMPONENT,
        setVisibleComponent,
      }}>
      {children}
    </SwitchableComponentContext.Provider>
  );
};

export function useSwitchableComponent(initialValue?: SwitchableComponentType) {
  const contextValue = React.useContext(SwitchableComponentContext);

  if (contextValue === undefined) {
    throw new Error(
      "useSwitchableComponent hook is being used outside of its context. Please ensure that it is wrapped within a <SwitchableComponentProvider>.",
    );
  }
  React.useEffect(() => {
    // Set the initial value if it's provided
    if (initialValue && contextValue.visibleComponent) {
      contextValue.setVisibleComponent(initialValue);
    }
  }, [initialValue]);

  return contextValue;
}

const variants: Record<string, Variant> = {
  hidden: { y: 50, opacity: 0, position: "absolute" },
  show: {
    y: 0,
    x: 0,
    opacity: 1,
    position: "relative",
    transition: {
      type: "tween",
      ease: "easeInOut",
    },
  },
  exit: { y: -50, opacity: 0, position: "absolute" },
};

export const SwitchableComponent = ({
  children,
  index,
}: React.PropsWithChildren<{ index: string }>) => {
  const [width, setWidth] = React.useState<number>();
  return (
    <AnimatePresence>
      <m.div
        key={index}
        initial="hidden"
        animate="show"
        exit="exit"
        variants={variants}
        className="h-full w-full"
        style={{ maxWidth: width ?? "auto" }}
        onTransitionEnd={(e) =>
          setWidth(e.currentTarget.getBoundingClientRect().width!)
        }>
        {children}
      </m.div>
    </AnimatePresence>
  );
};

type SwitchableComponentType<T extends {} = {}> = {
  index: string;
  render: (props: T | any) => ReturnType<React.FC>;
};

type SwitchableComponentContextType<T = unknown> = {
  visibleComponent: SwitchableComponentType<T extends {} ? T : any>;
  setVisibleComponent: React.Dispatch<
    React.SetStateAction<
      SwitchableComponentType<T extends {} ? T : any> | undefined
    >
  >;
};

const DEFAULT_COMPONENT = {
  render: () => undefined,
  index: Symbol("DEFAULT_COMPONENT").toString(),
};

// Factory function
export function makeSwitchable<T extends {}>(
  Component: React.FC<T>,
  index: string,
) {
  return {
    render(props: T) {
      return <Component {...props} />;
    },
    index: index || Symbol(Component.name).toString(),
  };
}
