import { useEffect, useRef, useState } from "preact/hooks";
import type { ComponentChildren } from "preact";

type ModalAction = {
  label: string;
  onClick: () => void;
  variant?: "primary" | "secondary" | "link";
};

type ModalProps = {
  title?: string;
  /**
   * Called after the modal has finished its exit animation.
   * The Modal will run the exit animation internally and then call onClose().
   */
  onClose: () => void;
  children: ComponentChildren;
  ariaLabel?: string;
  actions?: ModalAction[]; // rendered in footer; each button will be given flex-1 so buttons fill the width together
  /**
   * Optional: duration (ms) of enter/exit animation. Defaults to 200.
   * Keep this in sync with the CSS transition-duration classes used below.
   */
  animationDurationMs?: number;
};

export function Modal({
  title,
  onClose,
  children,
  ariaLabel,
  actions,
  animationDurationMs = 200,
}: ModalProps) {
  // Controls the entrance/exit animation state. true => visible (enter), false => hidden (exit)
  const [isVisible, setIsVisible] = useState(false);
  // Prevent double-triggering the close flow
  const closingRef = useRef(false);
  // Hold the timeout id for cleanup
  const timeoutRef = useRef<number | null>(null);

  useEffect(() => {
    // Defer to next frame so initial "hidden" styles are applied before animating to visible.
    const raf = requestAnimationFrame(() => setIsVisible(true));

    function onKey(e: KeyboardEvent) {
      if (e.key === "Escape") {
        startCloseFlow();
      }
    }
    document.addEventListener("keydown", onKey);

    return () => {
      cancelAnimationFrame(raf);
      document.removeEventListener("keydown", onKey);
      if (timeoutRef.current !== null) {
        clearTimeout(timeoutRef.current);
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // If no actions provided, render a single Close button
  const footerActions: ModalAction[] =
    actions && actions.length > 0
      ? actions
      : [{ label: "Close", onClick: startCloseFlow, variant: "primary" }];

  // Start exit animation and call onClose after animationDurationMs
  function startCloseFlow() {
    if (closingRef.current) return;
    closingRef.current = true;
    setIsVisible(false);
    // Wait for the CSS transition to finish before signalling parent to actually unmount
    timeoutRef.current = window.setTimeout(() => {
      timeoutRef.current = null;
      onClose();
    }, animationDurationMs);
  }

  // Animation classes (enter & exit):
  // - panel: transitions opacity + transform for a subtle fade + pop
  // - backdrop: transitions opacity for fade
  const panelBase =
    "relative z-10 max-w-lg w-full bg-white dark:bg-[#1f2937] rounded-lg shadow-xl p-6 mx-4 transform transition-all";
  // We explicitly set the CSS transition duration inline to keep class + timeout in sync.
  const panelVisible = "opacity-100 translate-y-0 scale-100";
  const panelHidden = "opacity-0 translate-y-2 scale-95";

  const backdropBase =
    "absolute inset-0 bg-black/50 backdrop-blur-sm transition-opacity";
  const backdropVisible = "opacity-100";
  const backdropHidden = "opacity-0";

  // Footer button class generator
  const renderActionButton = (act: ModalAction) => {
    const base =
      "flex-1 w-full px-4 py-2 rounded-md font-semibold focus:outline-none";
    const styles =
      act.variant === "primary"
        ? "bg-[#94e2d5] text-black hover:brightness-95"
        : act.variant === "link"
          ? "bg-transparent text-[#075985] underline"
          : "bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:brightness-95";

    return (
      <button key={act.label} onClick={act.onClick} class={`${base} ${styles}`}>
        {act.label}
      </button>
    );
  };

  return (
    <div
      class="fixed inset-0 z-50 flex items-center justify-center"
      aria-modal="true"
      role="dialog"
      aria-label={ariaLabel ?? title ?? "Modal dialog"}
    >
      {/* Backdrop */}
      <div
        // inline style for transitionDuration to keep JS timeout and CSS synced
        style={{ transitionDuration: `${animationDurationMs}ms` }}
        class={`${backdropBase} ${isVisible ? backdropVisible : backdropHidden}`}
        onClick={startCloseFlow}
      />
      {/* Modal panel */}
      <div
        style={{ transitionDuration: `${animationDurationMs}ms` }}
        class={`${panelBase} ${isVisible ? panelVisible : panelHidden}`}
        onClick={(e) => e.stopPropagation()}
      >
        <div class="flex items-start justify-between">
          <h3 class="text-lg font-semibold text-gray-900 dark:text-white">
            {title}
          </h3>
          <button
            onClick={startCloseFlow}
            aria-label="Close modal"
            class="ml-4 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 p-1"
          >
            ✕
          </button>
        </div>

        <div class="mt-4 text-sm text-gray-700 dark:text-gray-300">
          {children}
        </div>

        <div class="mt-6">
          <div class="flex gap-3 w-full">
            {footerActions.map(renderActionButton)}
          </div>
        </div>
      </div>
    </div>
  );
}
