"use client";

import { useEffect, useId, useRef, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { IoListOutline, IoArrowUpOutline, IoArrowDownOutline } from "react-icons/io5";
import { useClickOutside } from "@/hooks/useClickOutside";
import { IconButton } from "@/components/IconButton";

export type SortOption<T extends string = string> = {
  value: T;
  label: string;
  iconDirection?: "asc" | "desc";
};

type Props<T extends string> = {
  options: SortOption<T>[];
  value: T;
  onChange: (v: T) => void;
  ariaLabel: string;
  className?: string;
  menuClassName?: string;
};

export function SortDropdown<T extends string>({
  options,
  value,
  onChange,
  ariaLabel,
  className,
  menuClassName,
}: Props<T>) {
  const [open, setOpen] = useState(false);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const menuRef = useRef<HTMLDivElement>(null);
  const listboxId = useId();

  const active = options.find((o) => o.value === value) ?? options[0];

  useClickOutside([buttonRef, menuRef], () => setOpen(false), open);

  useEffect(() => {
    if (!open) return;

    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape") setOpen(false);
    };

    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [open]);

  return (
    <div className={`relative ${className ?? ""}`}>
      <IconButton
        ref={buttonRef}
        onClick={() => setOpen((v) => !v)}
        className="text-lg"
        aria-haspopup="listbox"
        aria-expanded={open}
        aria-controls={listboxId}
      >
        <IoListOutline />
      </IconButton>

      <AnimatePresence>
        {open ? (
          <motion.div
            ref={menuRef}
            id={listboxId}
            role="listbox"
            aria-label={ariaLabel}
            initial={{ opacity: 0, y: -8 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -8 }}
            transition={{ type: "tween", ease: "easeInOut", duration: 0.25 }}
            className={
              menuClassName ??
              "absolute right-0 z-20 mt-4 min-w-40 overflow-hidden rounded-2xl bg-lightbg shadow-lg ring-1 ring-lightline transition-colors duration-300 ease-in-out hover:ring-lightline-hover dark:bg-darkbg dark:ring-darkline dark:hover:ring-darkline-hover"
            }
          >
            <div className="flex flex-col">
              {options.map((opt, idx) => {
                const selected = opt.value === active.value;

                return (
                  <button
                    key={opt.value}
                    type="button"
                    role="option"
                    aria-selected={selected}
                    onClick={() => {
                      onChange(opt.value);
                      setOpen(false);
                    }}
                    className={[
                      "relative w-full p-4 text-left text-sm",
                      "transition-colors duration-300 ease-in-out",
                      selected
                        ? "bg-lightline font-medium text-current dark:bg-darkline"
                        : "text-lightsec dark:text-darksec",
                      "hover:bg-lightline dark:hover:bg-darkline",
                      idx === 0 && "rounded-t-2xl",
                      idx === options.length - 1 && "rounded-b-2xl",
                    ]
                      .filter(Boolean)
                      .join(" ")}
                  >
                    <span className="flex items-center justify-between gap-3">
                      <span>{opt.label}</span>

                      {opt.iconDirection ? (
                        <span className="shrink-0 text-base opacity-80">
                          {opt.iconDirection === "asc" ? (
                            <IoArrowUpOutline aria-hidden="true" />
                          ) : (
                            <IoArrowDownOutline aria-hidden="true" />
                          )}
                        </span>
                      ) : null}
                    </span>
                  </button>
                );
              })}
            </div>
          </motion.div>
        ) : null}
      </AnimatePresence>
    </div>
  );
}
