"use client";

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

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

type FormMultiSearchSelectProps = {
  id: string;
  label: string;
  values: string[];
  options: FormMultiSearchSelectOption[];
  onChange: (values: string[]) => void;
  placeholder?: string;
  searchPlaceholder?: string;
  emptyMessage?: string;
  disabled?: boolean;
};

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

  const selectedOptions = useMemo(
    () => options.filter((option) => values.includes(option.value)),
    [options, values],
  );

  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 toggleValue(optionValue: string) {
    if (values.includes(optionValue)) {
      onChange(values.filter((value) => value !== optionValue));
      return;
    }

    onChange([...values, optionValue]);
  }

  function removeValue(optionValue: string) {
    onChange(values.filter((value) => value !== optionValue));
  }

  return (
    <div className="flex flex-col gap-2">
      <label htmlFor={id} className="text-m-medium text-neutral-100">
        {label}
      </label>

      <div ref={containerRef} className="relative">
        <button
          id={id}
          type="button"
          onClick={handleOpen}
          disabled={disabled}
          aria-expanded={isOpen}
          aria-haspopup="listbox"
          aria-controls={listboxId}
          className="text-m-regular flex min-h-10 w-full cursor-pointer items-center justify-between gap-2 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"
        >
          <div className="flex min-w-0 flex-1 flex-wrap gap-2">
            {selectedOptions.length === 0 ? (
              <span className="text-neutral-70">{placeholder}</span>
            ) : (
              selectedOptions.map((option) => (
                <span
                  key={option.value}
                  className="text-s-medium inline-flex max-w-full items-center gap-1 rounded-md border border-secondary-300 bg-secondary-50 px-2 py-0.5 text-secondary-700"
                >
                  <span className="truncate">{option.label}</span>
                  <span
                    role="button"
                    tabIndex={-1}
                    aria-label={`Hapus ${option.label}`}
                    className="inline-flex shrink-0"
                    onClick={(event) => {
                      event.stopPropagation();
                      removeValue(option.value);
                    }}
                    onKeyDown={(event) => {
                      if (event.key === "Enter" || event.key === " ") {
                        event.preventDefault();
                        event.stopPropagation();
                        removeValue(option.value);
                      }
                    }}
                  >
                    <IconClose className="size-3.5 text-secondary-600" />
                  </span>
                </span>
              ))
            )}
          </div>
          <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}
              aria-multiselectable
              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 = values.includes(option.value);

                  return (
                    <li
                      key={option.value}
                      role="option"
                      aria-selected={isSelected}
                    >
                      <button
                        type="button"
                        onClick={() => toggleValue(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>
  );
}
