import type { Accessor } from "solid-js";

const FOCUSABLE_SELECTORS = [
  "a[href]",
  "button:not([disabled])",
  "input:not([disabled]):not([type='hidden'])",
  "select:not([disabled])",
  "textarea:not([disabled])",
  "[tabindex]:not([tabindex='-1'])",
  "details > summary",
].join(",");

function getFocusableElements(container: HTMLElement): HTMLElement[] {
  return Array.from(container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS));
}

export interface FocusTrap {
  activate: () => void;
  deactivate: () => void;
}

/**
 * Creates a focus trap that confines Tab focus within a container.
 * Restores focus to the previously focused element on deactivate.
 */
export function createFocusTrap(getContainer: Accessor<HTMLElement | null>): FocusTrap {
  let previouslyFocused: HTMLElement | null = null;

  const handleKeyDown = (e: KeyboardEvent) => {
    const container = getContainer();
    if (!container || e.key !== "Tab") return;

    const focusable = getFocusableElements(container);
    if (focusable.length === 0) {
      e.preventDefault();
      return;
    }

    const first = focusable[0] as HTMLElement;
    const last = focusable[focusable.length - 1] as HTMLElement;

    if (e.shiftKey) {
      if (document.activeElement === first) {
        e.preventDefault();
        last.focus();
      }
    } else {
      if (document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    }
  };

  let isActive = false;

  return {
    activate() {
      if (isActive) return;
      const container = getContainer();
      if (!container) return;
      isActive = true;

      previouslyFocused = document.activeElement as HTMLElement | null;
      const focusable = getFocusableElements(container);
      focusable[0]?.focus();
      document.addEventListener("keydown", handleKeyDown);
    },
    deactivate() {
      if (!isActive) return;
      isActive = false;
      document.removeEventListener("keydown", handleKeyDown);
      previouslyFocused?.focus();
      previouslyFocused = null;
    },
  };
}
