'use client';

import { useState, useCallback, useEffect, cloneElement, isValidElement } from 'react';
import { Search, X, Loader2 } from 'lucide-react';

import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';

import { useSearchableData } from './useSearchableData';
import type { SearchableSelectionModalProps } from './types';

export function SearchableSelectionModal<T>(props: SearchableSelectionModalProps<T>) {
  const {
    open,
    onOpenChange,
    title,
    searchPlaceholder = '검색...',
    fetchData,
    keyExtractor,
    renderItem,
    searchMode = 'debounce',
    debounceDelay = 300,
    validateSearch,
    invalidSearchMessage,
    loadOnOpen = false,
    emptyQueryMessage = '검색어를 입력하세요',
    noResultMessage = '검색 결과가 없습니다.',
    loadingMessage = '검색 중...',
    dialogClassName,
    listContainerClassName = 'max-h-[400px] overflow-y-auto border rounded-lg',
    listWrapper,
    infoText,
    mode,
    isItemDisabled,
  } = props;

  const {
    searchQuery,
    setSearchQuery,
    items,
    isLoading,
    error,
    triggerSearch,
    handleSearchKeyDown,
  } = useSearchableData<T>({
    open,
    fetchData,
    searchMode,
    debounceDelay,
    validateSearch,
    loadOnOpen,
  });

  // 다중선택 상태
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());

  // 모달 열릴 때 선택 초기화
  useEffect(() => {
    if (open) {
      setSelectedIds(new Set());
    }
  }, [open]);

  // 단일선택 핸들러
  const handleSingleSelect = useCallback((item: T) => {
    if (mode === 'single') {
      (props as { onSelect: (item: T) => void }).onSelect(item);
      onOpenChange(false);
    }
  }, [mode, props, onOpenChange]);

  // 다중선택 토글
  const handleToggle = useCallback((id: string) => {
    setSelectedIds((prev) => {
      const next = new Set(prev);
      if (next.has(id)) {
        next.delete(id);
      } else {
        next.add(id);
      }
      return next;
    });
  }, []);

  // 전체선택 토글 (비활성 아이템 제외)
  const handleToggleAll = useCallback(() => {
    const targetItems = isItemDisabled
      ? items.filter((item) => !isItemDisabled(item, items.filter((i) => selectedIds.has(keyExtractor(i)))))
      : items;
    setSelectedIds((prev) => {
      const targetIds = targetItems.map((item) => keyExtractor(item));
      const allSelected = targetIds.every((id) => prev.has(id));
      if (allSelected) {
        return new Set();
      }
      return new Set(targetIds);
    });
  }, [items, keyExtractor, isItemDisabled, selectedIds]);

  // 다중선택 확인
  const handleConfirm = useCallback(() => {
    if (mode === 'multiple') {
      const selectedItems = items.filter((item) => selectedIds.has(keyExtractor(item)));
      (props as { onSelect: (items: T[]) => void }).onSelect(selectedItems);
      onOpenChange(false);
    }
  }, [mode, items, selectedIds, keyExtractor, props, onOpenChange]);

  // 선택된 아이템 목록 (isItemDisabled 콜백용)
  const selectedItems = useCallback(() => {
    return items.filter((item) => selectedIds.has(keyExtractor(item)));
  }, [items, selectedIds, keyExtractor]);

  // 비활성 판정
  const checkDisabled = useCallback((item: T) => {
    if (!isItemDisabled) return false;
    // 이미 선택된 아이템은 disabled가 아님 (해제 가능해야 함)
    if (selectedIds.has(keyExtractor(item))) return false;
    return isItemDisabled(item, selectedItems());
  }, [isItemDisabled, selectedIds, keyExtractor, selectedItems]);

  // 클릭 핸들러: 모드에 따라 분기
  const handleItemClick = useCallback((item: T) => {
    if (checkDisabled(item)) return;
    if (mode === 'single') {
      handleSingleSelect(item);
    } else {
      handleToggle(keyExtractor(item));
    }
  }, [mode, handleSingleSelect, handleToggle, keyExtractor, checkDisabled]);

  // 전체선택 (비활성 아이템 제외)
  const enabledItems = isItemDisabled
    ? items.filter((item) => !checkDisabled(item))
    : items;
  const isAllSelected = enabledItems.length > 0 && enabledItems.every((item) => selectedIds.has(keyExtractor(item)));
  const isSelected = (item: T) => selectedIds.has(keyExtractor(item));

  // 빈 상태 메시지 결정
  const getEmptyMessage = () => {
    if (error) return null; // error는 별도 표시
    if (!searchQuery && !loadOnOpen) return emptyQueryMessage;
    if (searchQuery && validateSearch && !validateSearch(searchQuery)) {
      return invalidSearchMessage || emptyQueryMessage;
    }
    return noResultMessage;
  };

  // 리스트 콘텐츠 렌더링
  const renderListContent = () => {
    if (isLoading) {
      return (
        <div className="flex items-center justify-center p-8 text-muted-foreground">
          <Loader2 className="h-5 w-5 animate-spin mr-2" />
          <span>{loadingMessage}</span>
        </div>
      );
    }

    if (error) {
      return (
        <div className="p-4 text-center text-red-500 text-sm">
          {error}
        </div>
      );
    }

    if (items.length === 0) {
      return (
        <div className="p-4 text-center text-muted-foreground text-sm">
          {getEmptyMessage()}
        </div>
      );
    }

    const itemElements = items.map((item) => {
      const key = keyExtractor(item);
      const disabled = checkDisabled(item);
      const rendered = renderItem(item, isSelected(item), disabled);

      // renderItem이 유효한 React 엘리먼트를 반환하면 key와 onClick을 직접 주입 (div 래핑 없이)
      // 이렇게 하면 <TableRow> 등 테이블 요소를 <div>로 감싸는 HTML 유효성 에러를 방지
      if (isValidElement(rendered)) {
        return cloneElement(rendered as React.ReactElement<Record<string, unknown>>, {
          key,
          onClick: (e: React.MouseEvent) => {
            if (disabled) return;
            const existingOnClick = (rendered.props as Record<string, unknown>)?.onClick;
            if (typeof existingOnClick === 'function') {
              (existingOnClick as (e: React.MouseEvent) => void)(e);
            }
            handleItemClick(item);
          },
          className: [
            disabled ? 'cursor-not-allowed opacity-40' : 'cursor-pointer',
            (rendered.props as Record<string, unknown>)?.className || '',
          ].filter(Boolean).join(' '),
        });
      }

      // 일반 텍스트/fragment인 경우 기존 div 래핑 유지
      return (
        <div
          key={key}
          onClick={disabled ? undefined : () => handleItemClick(item)}
          className={disabled ? 'cursor-not-allowed opacity-40' : 'cursor-pointer'}
        >
          {rendered}
        </div>
      );
    });

    if (listWrapper) {
      const selectState = mode === 'multiple'
        ? { isAllSelected, onToggleAll: handleToggleAll }
        : undefined;
      return listWrapper(<>{itemElements}</>, selectState);
    }

    return <div className="divide-y">{itemElements}</div>;
  };

  const multiProps = mode === 'multiple' ? props as Extract<typeof props, { mode: 'multiple' }> : null;

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className={dialogClassName}>
        <DialogHeader>
          <DialogTitle>{title}</DialogTitle>
        </DialogHeader>

        {/* 검색 입력 */}
        {searchMode === 'enter' ? (
          <div className="flex gap-2">
            <div className="relative flex-1">
              <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
              <Input
                value={searchQuery}
                onChange={(e) => setSearchQuery(e.target.value)}
                onKeyDown={handleSearchKeyDown}
                placeholder={searchPlaceholder}
                className="pl-9"
              />
            </div>
            <Button variant="outline" onClick={triggerSearch}>
              검색
            </Button>
          </div>
        ) : (
          <div className="relative">
            <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
            <Input
              placeholder={searchPlaceholder}
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              className="pl-10 pr-10"
            />
            {searchQuery && (
              <button
                onClick={() => setSearchQuery('')}
                className="absolute right-3 top-1/2 -translate-y-1/2"
              >
                <X className="h-4 w-4 text-muted-foreground hover:text-foreground" />
              </button>
            )}
          </div>
        )}

        {/* 정보 텍스트 */}
        {infoText && (
          <div className="text-sm text-muted-foreground">
            {infoText(items, isLoading)}
          </div>
        )}

        {/* 다중선택 헤더 (전체선택 등) */}
        {mode === 'multiple' && multiProps?.renderHeader && (
          multiProps.renderHeader({ isAllSelected, onToggleAll: handleToggleAll })
        )}

        {/* 리스트 */}
        <div className={listContainerClassName}>
          {renderListContent()}
        </div>

        {/* 다중선택 푸터 */}
        {mode === 'multiple' && (
          <DialogFooter>
            <Button variant="outline" onClick={() => onOpenChange(false)}>
              취소
            </Button>
            <Button onClick={handleConfirm} disabled={selectedIds.size === 0}>
              {multiProps?.confirmLabel || '선택'} ({selectedIds.size}건)
            </Button>
          </DialogFooter>
        )}
      </DialogContent>
    </Dialog>
  );
}
