import {
  createEffect,
  createRenderEffect,
  onCleanup,
  onMount,
  startTransition,
  useTransition,
  type ParentComponent,
} from "solid-js";
import styles from "./BottomSheet.module.css";
import { useHeroSignal } from "../platform/anim";

export type BottomSheetProps = {
  open?: boolean;
};

export const HERO = Symbol("BottomSheet Hero Symbol");

function composeAnimationFrame(
  {
    top,
    left,
    height,
    width,
  }: Record<"top" | "left" | "height" | "width", number>,
  x: Record<string, unknown>,
) {
  return {
    top: `${top}px`,
    left: `${left}px`,
    height: `${height}px`,
    width: `${width}px`,
    ...x,
  };
}

const MOVE_SPEED = 1400; // 1400px/s, bottom sheet is big and a bit heavier than small papers

const BottomSheet: ParentComponent<BottomSheetProps> = (props) => {
  let element: HTMLDialogElement;
  let animation: Animation | undefined;
  const hero = useHeroSignal(HERO);

  const [pending] = useTransition()

  createEffect(() => {
    if (props.open) {
      if (!element.open && !pending()) {
        animatedOpen();
      }
    } else {
      if (element.open) {
        animatedClose();
      }
    }
  });

  const animatedClose = () => {
    const endRect = hero();
    if (endRect) {
      const startRect = element.getBoundingClientRect();
      const animation = animateHero(startRect, endRect, element, true);
      const onClose = () => {
        element.close();
      };
      animation.addEventListener("finish", onClose);
      animation.addEventListener("cancel", onClose);
    } else {
      element.close();
    }
  };

  const animatedOpen = () => {
    element.showModal();
    const startRect = hero();
    if (!startRect) return;
    const endRect = element.getBoundingClientRect();
    animateHero(startRect, endRect, element);
  };

  const animateHero = (
    startRect: DOMRect,
    endRect: DOMRect,
    element: HTMLElement,
    reserve?: boolean,
  ) => {
    const easing = "cubic-bezier(0.4, 0, 0.2, 1)";
    element.classList.add(styles.animated);
    const distance = Math.sqrt(
      Math.pow(Math.abs(startRect.top - endRect.top), 2) +
        Math.pow(Math.abs(startRect.left - startRect.top), 2),
    );
    const duration = (distance / MOVE_SPEED) * 1000;
    animation = element.animate(
      [
        composeAnimationFrame(startRect, { opacity: reserve ? 1 : 0.5 }),
        composeAnimationFrame(endRect, { opacity: reserve ? 0.5 : 1 }),
      ],
      { easing, duration },
    );
    const onAnimationEnd = () => {
      element.classList.remove(styles.animated);
      animation = undefined;
    };
    animation.addEventListener("finish", onAnimationEnd);
    animation.addEventListener("cancel", onAnimationEnd);
    return animation;
  };

  onCleanup(() => {
    if (animation) {
      animation.cancel();
    }
  });

  return (
    <dialog class={styles.bottomSheet} ref={element!}>
      {props.children}
    </dialog>
  );
};

export default BottomSheet;
