"use client";

import { useEffect, useId, useMemo, useRef, useState } from "react";
import { IconChevronDown, IconSearch } from "./icons";

const triggerClassName =
  "text-m-regular flex h-10 w-full items-center justify-between rounded-lg border border-neutral-40 bg-neutral-10 px-[22px] py-2 text-left outline-none focus:border-primary-500 disabled:cursor-not-allowed disabled:opacity-60";

export type FormSearchSelectOption = {
  value: string;
  label: string;
};

export type FormSearchSelectProps = {
  id: string;
  label?: string;
  value: string;
  placeholder: string;
  options: FormSearchSelectOption[];
  onChange: (value: string) => void;
  searchPlaceholder?: string;
  emptyMessage?: string;
  disabled?: boolean;
  required?: boolean;
  className?: string;
};

export function FormSearchSelect({
  id,
  label,
  value,
  placeholder,
  options,
  onChange,
  searchPlaceholder = "Cari...",
  emptyMessage = "Tidak ada hasil.",
  disabled = false,
  required = false,
  className,
}: FormSearchSelectProps) {
  const listboxId = useId();
  const containerRef = useRef<HTMLDivElement>(null);
  const searchInputRef = useRef<HTMLInputElement>(null);
  const [isOpen, setIsOpen] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");

  const selectedLabel =
    options.find((option) => option.value === value)?.label ?? "";

  const filteredOptions = useMemo(() => {
    const query = searchQuery.trim().toLowerCase();

    if (!query) {
      return options;
    }

    return options.filter((option) =>
      option.label.toLowerCase().includes(query),
    );
  }, [options, searchQuery]);

  useEffect(() => {
    if (!isOpen) {
      return;
    }

    function handleClickOutside(event: MouseEvent) {
      if (
        containerRef.current &&
        !containerRef.current.contains(event.target as Node)
      ) {
        setIsOpen(false);
      }
    }

    function handleEscape(event: KeyboardEvent) {
      if (event.key === "Escape") {
        setIsOpen(false);
      }
    }

    document.addEventListener("mousedown", handleClickOutside);
    document.addEventListener("keydown", handleEscape);

    const frameId = requestAnimationFrame(() => {
      searchInputRef.current?.focus();
    });

    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
      document.removeEventListener("keydown", handleEscape);
      cancelAnimationFrame(frameId);
    };
  }, [isOpen]);

  function handleOpen() {
    if (disabled) {
      return;
    }

    setSearchQuery("");
    setIsOpen(true);
  }

  function handleSelect(optionValue: string) {
    onChange(optionValue);
    setIsOpen(false);
    setSearchQuery("");
  }

  return (
    <div className={`flex flex-col ${label ? "gap-2" : ""} ${className ?? ""}`}>
      {label ? (
        <label htmlFor={id} className="text-m-medium text-neutral-100">
          {label}
          {required && <span className="text-danger-500"> *</span>}
        </label>
      ) : null}

      <div ref={containerRef} className="relative">
        <button
          id={id}
          type="button"
          onClick={handleOpen}
          disabled={disabled}
          aria-expanded={isOpen}
          aria-haspopup="listbox"
          aria-controls={listboxId}
          aria-label={label || placeholder}
          className={`${triggerClassName} ${
            selectedLabel ? "text-neutral-100" : "text-neutral-70"
          }`}
        >
          <span className="truncate pr-2">
            {selectedLabel || placeholder}
          </span>
          <IconChevronDown className="size-5 shrink-0 text-neutral-100" />
        </button>

        {isOpen && (
          <div className="absolute top-[calc(100%+4px)] left-0 z-50 w-full overflow-hidden rounded-lg border border-neutral-40 bg-neutral-10 shadow-sm">
            <div className="border-b border-neutral-40 p-2">
              <div className="relative">
                <input
                  ref={searchInputRef}
                  type="search"
                  value={searchQuery}
                  onChange={(event) => setSearchQuery(event.target.value)}
                  placeholder={searchPlaceholder}
                  className="text-m-regular h-9 w-full rounded-lg border border-neutral-40 bg-neutral-10 px-3 py-2 pr-9 text-neutral-100 placeholder:text-neutral-70 outline-none focus:border-primary-500"
                />
                <IconSearch className="pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-neutral-70" />
              </div>
            </div>

            <ul
              id={listboxId}
              role="listbox"
              aria-label={label || placeholder}
              className="max-h-56 overflow-y-auto py-1"
            >
              {filteredOptions.length === 0 ? (
                <li className="text-m-regular px-4 py-2 text-neutral-70">
                  {emptyMessage}
                </li>
              ) : (
                filteredOptions.map((option) => {
                  const isSelected = option.value === value;

                  return (
                    <li
                      key={option.value}
                      role="option"
                      aria-selected={isSelected}
                    >
                      <button
                        type="button"
                        onClick={() => handleSelect(option.value)}
                        className={`text-m-regular w-full truncate px-4 py-2 text-left transition-colors hover:bg-neutral-20 ${
                          isSelected
                            ? "bg-primary-50 text-primary-700"
                            : "text-neutral-100"
                        }`}
                      >
                        {option.label}
                      </button>
                    </li>
                  );
                })
              )}
            </ul>
          </div>
        )}
      </div>
    </div>
  );
}
