import { useWindowSize } from "@solid-primitives/resize-observer";
import { MenuList } from "@suid/material";
import {
  createEffect,
  createSignal,
  type JSX,
  type ParentComponent,
} from "solid-js";
import { ANIM_CURVE_STD } from "./theme";
import "./Menu.css";
import {
  animateGrowFromTopRight,
  animateShrinkToTopRight,
} from "../platform/anim";

type Props = {
  open?: boolean;
  onClose?: JSX.EventHandlerUnion<HTMLDialogElement, Event>;
  anchor: () => DOMRect;
};

function px(n?: number) {
  if (n) {
    return `${n}px`;
  } else {
    return undefined;
  }
}

const Menu: ParentComponent<Props> = (props) => {
  let root: HTMLDialogElement;
  const windowSize = useWindowSize();

  const [anchorPos, setAnchorPos] = createSignal<{
    left?: number;
    top?: number;
  }>({});

  let openAnimationOrigin: "lt" | "rt" = "lt";

  createEffect(() => {
    if (props.open) {
      const a = props.anchor();

      if (!root.open) {
        root.showModal();
        const rend = root.getBoundingClientRect();

        const { width } = windowSize;
        const { left, top, right } = a;
        if (left > width / 2) {
          openAnimationOrigin = "rt";
          setAnchorPos({
            left: right - rend.width,
            top,
          });

          animateGrowFromTopRight(root, { easing: ANIM_CURVE_STD });
        } else {
          openAnimationOrigin = "lt";
          setAnchorPos({ left, top });

          const overflow = root.style.overflow;
          root.style.overflow = "hidden";
          const duration = (rend.height / 1600) * 1000;
          const easing = ANIM_CURVE_STD;
          const animation = root.animate(
            {
              height: [`${rend.height / 2}px`, `${rend.height}px`],
              width: [`${(rend.width / 4) * 3}px`, `${rend.width}px`],
            },
            {
              duration,
              easing,
            },
          );
          animation.addEventListener(
            "finish",
            () => (root.style.overflow = overflow),
          );
        }
      } else {
        // TODO: update the pos
      }
    } else {
      animateClose();
    }
  });

  const animateClose = () => {
    const rend = root.getBoundingClientRect();
    if (openAnimationOrigin === "lt") {
      const overflow = root.style.overflow;
      root.style.overflow = "hidden";
      const animation = root.animate(
        {
          height: [`${rend.height}px`, `${rend.height / 2}px`],
          width: [`${rend.width}px`, `${(rend.width / 4) * 3}px`],
        },
        {
          duration: (rend.height / 2 / 1600) * 1000,
          easing: ANIM_CURVE_STD,
        },
      );
      animation.addEventListener("finish", () => {
        root.style.overflow = overflow;
        root.close();
      });
    } else {
      const animation = animateShrinkToTopRight(root, {
        easing: ANIM_CURVE_STD,
      });
      animation.addEventListener("finish", () => {
        root.close();
      });
    }
  };

  return (
    <dialog
      ref={root!}
      onClose={props.onClose}
      onClick={(e) => {
        if (e.target === root) {
          if (props.onClose) {
            if (Array.isArray(props.onClose)) {
              props.onClose[0](props.onClose[1], e);
            } else {
              (
                props.onClose as (
                  event: Event & { currentTarget: HTMLDialogElement },
                ) => void
              )(e);
            }
          }
          e.stopPropagation();
        }
      }}
      class="Menu"
      style={{
        left: px(anchorPos().left),
        top: px(anchorPos().top),
        /* FIXME: the content may be overflow */
      }}
    >
      <div
        style={{
          background: "var(--tutu-color-surface)",
        }}
      >
        <MenuList>{props.children}</MenuList>
      </div>
    </dialog>
  );
};

export default Menu;