import { createSignal, onCleanup, onMount, Show, type Accessor } from "solid-js";
import { Icon } from "./Icon";

const BACK_TO_TOP_POSITION_KEY = "ehpeek:back-to-top:position";

export function clearBackToTopPosition(): void {
  GM_deleteValue(BACK_TO_TOP_POSITION_KEY);
}

type ButtonPosition = {
  bottom: number;
  right: number;
};

export function BackToTop(props: { leftHanded: Accessor<boolean> }) {
  let button!: HTMLButtonElement;
  let drag: { bottom: number; pointerId: number; right: number; x: number; y: number } | null = null;
  let dragged = false;
  const [visible, setVisible] = createSignal(false);
  const [position, setPosition] = createSignal<ButtonPosition | null>(null);
  const positionStyle = () => {
    const current = position();
    return current
      ? { bottom: `${current.bottom}px`, right: `${current.right}px` }
      : undefined;
  };

  onMount(() => {
    const updateVisibility = () => {
      setVisible(window.scrollY > Math.max(320, window.innerHeight * 0.5));
    };

    updateVisibility();
    const savedPosition = GM_getValue<ButtonPosition | null>(BACK_TO_TOP_POSITION_KEY, null);

    if (savedPosition) {
      setPosition(savedPosition);
    }
    window.addEventListener("scroll", updateVisibility, { passive: true });
    onCleanup(() => window.removeEventListener("scroll", updateVisibility));
  });

  return (
    <Show when={visible()}>
      <button
        ref={button}
        type="button"
        class="fixed safe-right-lg bottom-[calc(max(16px,env(safe-area-inset-bottom,0px))_+_var(--ui-hit-size-lg)_+_var(--ui-space-md))] z-ui inline-flex ui-hit-square-lg items-center justify-center rounded-full border-0 bg-[var(--color-site-elevated)] ehp-color-site-accent shadow-[0_4px_14px_var(--color-shadow-floating)] cursor-pointer [touch-action:none] active:scale-96"
        classList={{
          "!right-auto safe-left-lg":
            props.leftHanded() && position() === null,
        }}
        style={positionStyle()}
        onPointerDown={(event) => {
          const rect = button.getBoundingClientRect();
          dragged = false;
          drag = {
            bottom: window.innerHeight - rect.bottom,
            pointerId: event.pointerId,
            right: window.innerWidth - rect.right,
            x: event.clientX,
            y: event.clientY,
          };
          button.setPointerCapture(event.pointerId);
        }}
        onPointerMove={(event) => {
          if (!drag || drag.pointerId !== event.pointerId) {
            return;
          }

          const dx = event.clientX - drag.x;
          const dy = event.clientY - drag.y;
          dragged ||= Math.hypot(dx, dy) > 4;
          setPosition(clampPosition({ bottom: drag.bottom - dy, right: drag.right - dx }, button));
        }}
        onPointerUp={(event) => {
          if (!drag || drag.pointerId !== event.pointerId) {
            return;
          }

          button.releasePointerCapture(event.pointerId);
          drag = null;
          const current = position();
          if (dragged && current) {
            GM_setValue(BACK_TO_TOP_POSITION_KEY, current);
          }
        }}
        onClick={(event) => {
          if (dragged) {
            event.preventDefault();
            dragged = false;
            return;
          }
          window.scrollTo({ top: 0, behavior: "smooth" });
        }}
      >
        <Icon name="arrow-up" size="var(--ui-icon-size-lg)" />
      </button>
    </Show>
  );
}

function clampPosition(position: ButtonPosition, button: HTMLButtonElement): ButtonPosition {
  return {
    bottom: Math.min(Math.max(0, position.bottom), Math.max(0, window.innerHeight - button.offsetHeight)),
    right: Math.min(Math.max(0, position.right), Math.max(0, window.innerWidth - button.offsetWidth)),
  };
}
