import { useCallback, useEffect, useRef, useState } from "react";
import { subscribeControllerToken } from "./controller";

export interface MenuAction {
  id: string;
  run: () => void;
  enabled?: boolean;
  neighbors?: Partial<Record<"up" | "down" | "left" | "right", string>>;
}

interface MenuControllerOptions {
  columns?: number;
  onBack?: () => void;
}

type Direction = "up" | "down" | "left" | "right";

function nextIndex(index: number, direction: Direction, count: number, columns: number) {
  const delta = direction === "left" ? -1 : direction === "right" ? 1 : direction === "up" ? -columns : columns;
  return (index + delta + count) % count;
}

export function useMenuController(actions: MenuAction[], options: MenuControllerOptions = {}) {
  const enabled = actions.filter((action) => action.enabled !== false);
  const [focusedId, setFocusedId] = useState(enabled[0]?.id ?? "");
  const actionsRef = useRef(enabled);
  const backRef = useRef(options.onBack);
  const columnsRef = useRef(options.columns ?? 1);
  actionsRef.current = enabled;
  backRef.current = options.onBack;
  columnsRef.current = options.columns ?? 1;

  useEffect(() => {
    if (!actionsRef.current.some((action) => action.id === focusedId)) {
      setFocusedId(actionsRef.current[0]?.id ?? "");
    }
  }, [actions, focusedId]);

  const move = useCallback((direction: Direction) => {
    const current = actionsRef.current;
    if (!current.length) return;
    const index = Math.max(0, current.findIndex((action) => action.id === focusedId));
    const neighborId = current[index]?.neighbors?.[direction];
    if (neighborId && current.some((action) => action.id === neighborId)) {
      setFocusedId(neighborId);
      return;
    }
    setFocusedId(current[nextIndex(index, direction, current.length, columnsRef.current)].id);
  }, [focusedId]);

  const confirm = useCallback(() => {
    actionsRef.current.find((action) => action.id === focusedId)?.run();
  }, [focusedId]);

  useEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      if (event.repeat || event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) return;
      const key = event.key.toLowerCase();
      if (["arrowup", "w"].includes(key)) move("up");
      else if (["arrowdown", "s"].includes(key)) move("down");
      else if (["arrowleft", "a"].includes(key)) move("left");
      else if (["arrowright", "d"].includes(key)) move("right");
      else if (key === "enter" || key === " ") confirm();
      else if (key === "escape" || key === "backspace") backRef.current?.();
      else return;
      event.preventDefault();
    };
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [confirm, move]);

  useEffect(() => subscribeControllerToken(({ token, repeat }) => {
    const direction: Direction | null = token === "Button12" || token === "Axis1-" ? "up"
      : token === "Button13" || token === "Axis1+" ? "down"
        : token === "Button14" || token === "Axis0-" ? "left"
          : token === "Button15" || token === "Axis0+" ? "right"
            : null;
    if (direction) move(direction);
    else if (!repeat && token === "Button0") confirm();
    else if (!repeat && token === "Button1") backRef.current?.();
  }), [confirm, move]);

  return {
    focusedId,
    focus: setFocusedId,
    isFocused: (id: string) => id === focusedId,
  };
}
