"use client";

import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
import { usePathname, useRouter } from "@/i18n/navigation";
import { routing } from "@/i18n/routing";
import { cn } from "@/lib/utils";
import { type Locale, useLocale, useTranslations } from "next-intl";
import { useParams } from "next/navigation";
import { useEffect, useRef, useState, useTransition } from "react";

type Props = {
  className?: string;
};

/** Grace period before a pointer leaving the trigger closes the panel. */
const CLOSE_DELAY_MS = 120;

export default function LanguageSelect({ className }: Props) {
  const t = useTranslations("LocaleSwitcher");
  const locale = useLocale();
  const router = useRouter();
  const pathname = usePathname();
  const params = useParams();
  const [isPending, startTransition] = useTransition();
  const [open, setOpen] = useState(false);
  const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  function clearCloseTimer() {
    if (closeTimer.current) {
      clearTimeout(closeTimer.current);
      closeTimer.current = null;
    }
  }

  // Without this, unmounting mid-delay leaves a timer that calls setState on a
  // dead component.
  useEffect(() => clearCloseTimer, []);

  function handleEnter() {
    // Opening on hover is a pointer affordance. On a touch screen the browser
    // still synthesises a `mouseenter` before the tap, which used to open the
    // panel and then have the tap immediately re-toggle it.
    if (!window.matchMedia("(hover: hover)").matches) return;
    clearCloseTimer();
    setOpen(true);
  }

  function handleLeave() {
    clearCloseTimer();
    closeTimer.current = setTimeout(() => setOpen(false), CLOSE_DELAY_MS);
  }

  function onValueChange(nextLocale: Locale | null) {
    if (!nextLocale) return;
    setOpen(false);
    startTransition(() => {
      router.replace(
        // @ts-expect-error -- params/pathname pair is guaranteed for current route
        { pathname, params },
        { locale: nextLocale },
      );
    });
  }

  return (
    <div onMouseEnter={handleEnter} onMouseLeave={handleLeave}>
      <Select
        value={locale}
        onValueChange={onValueChange}
        disabled={isPending}
        open={open}
        onOpenChange={setOpen}
      >
        <SelectTrigger
          aria-label={t("label")}
          size="sm"
          variant="bare"
          className={cn(
            // `min-h-7` (28px) lifts the tap target over the 24x24 floor of
            // WCAG 2.2 AA 2.5.8 — the trigger is 16px tall on its own. It is a
            // `min-h-*`, not padding, so it cannot collide with the `p-0` of the
            // `bare` variant, and the header row centres it: no layout shift.
            "min-h-7 text-foreground hover:text-tertiary text-xs font-semibold uppercase tracking-caps [&_svg]:size-3",
            className,
          )}
        >
          <span aria-hidden="true">{locale}</span>
        </SelectTrigger>
        <SelectContent
          align="end"
          alignItemWithTrigger={false}
          sideOffset={8}
          onMouseEnter={handleEnter}
          onMouseLeave={handleLeave}
          className="border-foreground/15 bg-background/95 min-w-40 rounded-none border p-0 shadow-none ring-0 backdrop-blur-md"
        >
          {routing.locales.map((cur) => (
            <SelectItem
              key={cur}
              value={cur}
              // The option is written in the language it names, so the screen
              // reader has to switch voice for it (WCAG 3.1.2).
              lang={cur}
              className="text-foreground data-selected:text-tertiary focus:bg-transparent focus:text-tertiary not-data-[variant=destructive]:focus:**:text-tertiary relative rounded-none px-4 py-3 text-xs font-semibold uppercase tracking-caps transition-colors"
            >
              {t("locale", { locale: cur })}
            </SelectItem>
          ))}
        </SelectContent>
      </Select>
    </div>
  );
}
