"use client";

/**
 * SmartSelect
 *
 * 옵션 개수에 따라 자동으로 검색 기능을 제공하는 셀렉트 컴포넌트.
 * - 옵션 5개 미만: 기본 Select
 * - 옵션 5개 이상: 검색 + 가상 스크롤 Combobox (대용량 옵션도 빠르게 처리)
 */

import React, { useState, useMemo, useEffect, useRef } from "react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Check, ChevronsUpDown, Search as SearchIcon, X } from "lucide-react";
import { cn } from "@/lib/utils";
import { useVirtualizer } from "@tanstack/react-virtual";

const SEARCH_THRESHOLD = 5;
const ITEM_HEIGHT = 36;
const LIST_HEIGHT = 280;

export interface SmartSelectOption {
  code: string;
  label: string;
}

interface SmartSelectProps {
  options: SmartSelectOption[];
  value: string;
  onValueChange: (value: string) => void;
  placeholder?: string;
  disabled?: boolean;
  className?: string;
  /** 값이 있을 때 ✕(선택 해제) 버튼 노출 (기본 true). 필수 필드는 false로 둘 것. */
  clearable?: boolean;
}

export function SmartSelect({
  options,
  value,
  onValueChange,
  placeholder = "선택",
  disabled = false,
  className,
  clearable = true,
}: SmartSelectProps) {
  const [open, setOpen] = useState(false);
  const [search, setSearch] = useState("");
  const [activeIndex, setActiveIndex] = useState(0);
  const scrollRef = useRef<HTMLDivElement | null>(null);

  // code가 비어있는 옵션은 자동 제외 (Radix Select value 제약 + key 중복 방지)
  const safeOptions = useMemo(
    () => options.filter((o) => o.code !== null && o.code !== undefined && o.code !== ""),
    [options],
  );

  const selectedLabel = useMemo(
    () => safeOptions.find((o) => o.code === value)?.label,
    [safeOptions, value],
  );

  // 팝오버 닫힐 때 검색어 리셋
  useEffect(() => {
    if (!open) setSearch("");
  }, [open]);

  // 검색어로 옵션 필터 (대소문자 무시)
  const filtered = useMemo(() => {
    const q = search.trim().toLowerCase();
    if (!q) return safeOptions;
    return safeOptions.filter((o) => o.label.toLowerCase().includes(q));
  }, [safeOptions, search]);

  const virtualizer = useVirtualizer({
    count: filtered.length,
    getScrollElement: () => scrollRef.current,
    estimateSize: () => ITEM_HEIGHT,
    overscan: 10,
  });

  // 팝오버 열릴 때 측정 강제 (Portal 렌더 타이밍 대응)
  useEffect(() => {
    if (!open) return;
    const id = requestAnimationFrame(() => virtualizer.measure());
    return () => cancelAnimationFrame(id);
  }, [open, virtualizer, filtered.length]);

  // 팝오버 열릴 때 현재 선택값 위치로 활성 인덱스 초기화 (없으면 0)
  useEffect(() => {
    if (!open) return;
    const idx = filtered.findIndex((o) => o.code === value);
    setActiveIndex(idx >= 0 ? idx : 0);
  // 의도적으로 filtered.length 변화 시에도 재계산 안 함 (검색 입력 중 0번 유지)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  // 검색어가 바뀌면 첫 항목으로 리셋
  useEffect(() => {
    if (open) setActiveIndex(0);
  }, [search, open]);

  // 활성 인덱스가 바뀌면 가시 영역으로 스크롤
  useEffect(() => {
    if (!open) return;
    if (activeIndex < 0 || activeIndex >= filtered.length) return;
    virtualizer.scrollToIndex(activeIndex, { align: "auto" });
  }, [activeIndex, open, virtualizer, filtered.length]);

  const onSearchKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (filtered.length === 0) {
      if (e.key === "Escape") { e.preventDefault(); setOpen(false); }
      return;
    }
    switch (e.key) {
      case "ArrowDown":
        e.preventDefault();
        setActiveIndex((i) => Math.min(filtered.length - 1, (i < 0 ? -1 : i) + 1));
        break;
      case "ArrowUp":
        e.preventDefault();
        setActiveIndex((i) => Math.max(0, (i < 0 ? 0 : i) - 1));
        break;
      case "Home":
        e.preventDefault();
        setActiveIndex(0);
        break;
      case "End":
        e.preventDefault();
        setActiveIndex(filtered.length - 1);
        break;
      case "PageDown":
        e.preventDefault();
        setActiveIndex((i) => Math.min(filtered.length - 1, (i < 0 ? 0 : i) + 8));
        break;
      case "PageUp":
        e.preventDefault();
        setActiveIndex((i) => Math.max(0, (i < 0 ? 0 : i) - 8));
        break;
      case "Enter": {
        e.preventDefault();
        const hit = filtered[activeIndex];
        if (hit) {
          onValueChange(hit.code);
          setOpen(false);
        }
        break;
      }
      case "Escape":
        e.preventDefault();
        setOpen(false);
        break;
    }
  };

  const showClear = clearable && !disabled && !!value;
  // Radix Select/Popover trigger는 onPointerDown으로 열린다 → 같은 단계에서 차단해야 X 클릭이 trigger를 안 깨움
  const stopAndClear = (e: React.PointerEvent | React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
    onValueChange("");
  };
  const blockTrigger = (e: React.PointerEvent | React.MouseEvent) => {
    e.preventDefault();
    e.stopPropagation();
  };
  const ClearBtn = (
    <button
      type="button"
      tabIndex={-1}
      aria-label="선택 해제"
      onPointerDown={blockTrigger}
      onMouseDown={blockTrigger}
      onClick={stopAndClear}
      className="absolute right-8 top-1/2 -translate-y-1/2 z-10 inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-muted"
    >
      <X className="h-3.5 w-3.5" />
    </button>
  );

  if (safeOptions.length < SEARCH_THRESHOLD) {
    return (
      <div className={cn("relative", className)}>
        {/* key: 빈값↔값 전환 시 Radix Select remount — controlled value=undefined 시 selection 미해제 우회 */}
        <Select key={value ? "filled" : "empty"} value={value || undefined} onValueChange={onValueChange} disabled={disabled}>
          <SelectTrigger className={cn("h-9", showClear && "pr-12")}>
            <SelectValue placeholder={placeholder} />
          </SelectTrigger>
          <SelectContent>
            {safeOptions.map((o, idx) => (
              <SelectItem key={`${o.code}-${idx}`} value={o.code}>
                {o.label}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
        {showClear && ClearBtn}
      </div>
    );
  }

  return (
    <div className="relative">
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <Button
          variant="outline"
          role="combobox"
          aria-expanded={open}
          disabled={disabled}
          className={cn("h-9 w-full justify-between font-normal", showClear && "pr-12", className)}
        >
          <span className="truncate">
            {selectedLabel || <span className="text-muted-foreground">{placeholder}</span>}
          </span>
          <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
        </Button>
      </PopoverTrigger>
      <PopoverContent
        className="p-0"
        style={{ width: "var(--radix-popover-trigger-width)" }}
        align="start"
      >
        <div className="flex items-center border-b px-2">
          <SearchIcon className="h-4 w-4 text-muted-foreground mr-1 shrink-0" />
          <Input
            autoFocus
            placeholder="검색..."
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            onKeyDown={onSearchKeyDown}
            className="h-9 border-0 px-1 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
          />
        </div>
        {filtered.length === 0 ? (
          <div className="py-6 text-center text-sm text-muted-foreground">검색 결과가 없습니다.</div>
        ) : (
          <div
            ref={scrollRef}
            className="overflow-auto py-1"
            style={{ height: LIST_HEIGHT }}
          >
            <div
              style={{
                height: `${virtualizer.getTotalSize()}px`,
                width: "100%",
                position: "relative",
              }}
            >
              {virtualizer.getVirtualItems().map((vItem) => {
                const o = filtered[vItem.index];
                const isSelected = value === o.code;
                const isActive = activeIndex === vItem.index;
                return (
                  <button
                    key={`${o.code}-${vItem.index}`}
                    type="button"
                    role="option"
                    aria-selected={isSelected}
                    onMouseEnter={() => setActiveIndex(vItem.index)}
                    onClick={() => {
                      onValueChange(o.code);
                      setOpen(false);
                    }}
                    className={cn(
                      "absolute left-0 top-0 w-full flex items-center px-2 text-sm text-left",
                      isActive ? "bg-accent" : "hover:bg-accent/40",
                      isSelected && !isActive && "bg-accent/60",
                    )}
                    style={{
                      height: `${vItem.size}px`,
                      transform: `translateY(${vItem.start}px)`,
                    }}
                  >
                    <Check className={cn("mr-2 h-4 w-4 shrink-0", isSelected ? "opacity-100" : "opacity-0")} />
                    <span className="truncate">{o.label}</span>
                  </button>
                );
              })}
            </div>
          </div>
        )}
      </PopoverContent>
    </Popover>
    {showClear && ClearBtn}
    </div>
  );
}
