"use client";

/**
 * UnifiedSelect
 *
 * 통합 선택 컴포넌트
 * - dropdown: 드롭다운 선택
 * - radio: 라디오 버튼 그룹
 * - check: 체크박스 그룹
 * - tag: 태그 선택
 * - toggle: 토글 스위치
 * - swap: 스왑 선택 (좌우 이동)
 */

import React, { forwardRef, useCallback, useContext, useEffect, useMemo, useState } from "react";
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Checkbox } from "@/components/ui/checkbox";
import { Switch } from "@/components/ui/switch";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { cn } from "@/lib/utils";
import { UnifiedSelectProps, SelectOption } from "@/types/unified-components";
import { Check, ChevronsUpDown, X, ArrowLeftRight } from "lucide-react";
import { apiClient } from "@/lib/api/client";
import UnifiedFormContext from "./UnifiedFormContext";

/**
 * 드롭다운 선택 컴포넌트
 */
const DropdownSelect = forwardRef<
  HTMLButtonElement,
  {
    options: SelectOption[];
    value?: string | string[];
    onChange?: (value: string | string[]) => void;
    placeholder?: string;
    searchable?: boolean;
    multiple?: boolean;
    maxSelect?: number;
    allowClear?: boolean;
    disabled?: boolean;
    className?: string;
  }
>(
  (
    {
      options,
      value,
      onChange,
      placeholder = "선택",
      searchable,
      multiple,
      maxSelect,
      allowClear = true,
      disabled,
      className,
    },
    ref,
  ) => {
    const [open, setOpen] = useState(false);

    // 단일 선택 + 검색 불가능 → 기본 Select 사용
    if (!searchable && !multiple) {
      return (
        <Select
          value={typeof value === "string" ? value : (value?.[0] ?? "")}
          onValueChange={(v) => onChange?.(v)}
          disabled={disabled}
        >
          <SelectTrigger ref={ref} className={cn("h-10", className)}>
            <SelectValue placeholder={placeholder} />
          </SelectTrigger>
          <SelectContent>
            {options.map((option) => (
              <SelectItem key={option.value} value={option.value}>
                {option.label}
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
      );
    }

    // 검색 가능 또는 다중 선택 → Combobox 사용
    const selectedValues = useMemo(() => {
      if (!value) return [];
      return Array.isArray(value) ? value : [value];
    }, [value]);

    const selectedLabels = useMemo(() => {
      return selectedValues.map((v) => options.find((o) => o.value === v)?.label).filter(Boolean) as string[];
    }, [selectedValues, options]);

    const handleSelect = useCallback(
      (selectedValue: string) => {
        if (multiple) {
          const newValues = selectedValues.includes(selectedValue)
            ? selectedValues.filter((v) => v !== selectedValue)
            : maxSelect && selectedValues.length >= maxSelect
              ? selectedValues
              : [...selectedValues, selectedValue];
          onChange?.(newValues);
        } else {
          onChange?.(selectedValue);
          setOpen(false);
        }
      },
      [multiple, selectedValues, maxSelect, onChange],
    );

    const handleClear = useCallback(
      (e: React.MouseEvent) => {
        e.stopPropagation();
        onChange?.(multiple ? [] : "");
      },
      [multiple, onChange],
    );

    return (
      <Popover open={open} onOpenChange={setOpen}>
        <PopoverTrigger asChild>
          <Button
            ref={ref}
            variant="outline"
            role="combobox"
            aria-expanded={open}
            disabled={disabled}
            className={cn("h-10 w-full justify-between font-normal", className)}
          >
            <span className="flex-1 truncate text-left">
              {selectedLabels.length > 0
                ? multiple
                  ? `${selectedLabels.length}개 선택됨`
                  : selectedLabels[0]
                : placeholder}
            </span>
            <div className="ml-2 flex items-center gap-1">
              {allowClear && selectedValues.length > 0 && (
                <X className="h-4 w-4 opacity-50 hover:opacity-100" onClick={handleClear} />
              )}
              <ChevronsUpDown className="h-4 w-4 shrink-0 opacity-50" />
            </div>
          </Button>
        </PopoverTrigger>
        <PopoverContent className="p-0" style={{ width: "var(--radix-popover-trigger-width)" }} align="start">
          <Command
            filter={(value, search) => {
              // value는 CommandItem의 value (라벨)
              // search는 검색어
              if (!search) return 1;
              const normalizedValue = value.toLowerCase();
              const normalizedSearch = search.toLowerCase();
              if (normalizedValue.includes(normalizedSearch)) return 1;
              return 0;
            }}
          >
            {searchable && <CommandInput placeholder="검색..." className="h-9" />}
            <CommandList>
              <CommandEmpty>검색 결과가 없습니다.</CommandEmpty>
              <CommandGroup>
                {options.map((option) => {
                  const displayLabel = option.label || option.value || "(빈 값)";
                  return (
                    <CommandItem key={option.value} value={displayLabel} onSelect={() => handleSelect(option.value)}>
                      <Check
                        className={cn(
                          "mr-2 h-4 w-4",
                          selectedValues.includes(option.value) ? "opacity-100" : "opacity-0",
                        )}
                      />
                      {displayLabel}
                    </CommandItem>
                  );
                })}
              </CommandGroup>
            </CommandList>
          </Command>
        </PopoverContent>
      </Popover>
    );
  },
);
DropdownSelect.displayName = "DropdownSelect";

/**
 * 라디오 선택 컴포넌트
 */
const RadioSelect = forwardRef<
  HTMLDivElement,
  {
    options: SelectOption[];
    value?: string;
    onChange?: (value: string) => void;
    disabled?: boolean;
    className?: string;
  }
>(({ options, value, onChange, disabled, className }, ref) => {
  return (
    <RadioGroup
      ref={ref}
      value={value ?? ""}
      onValueChange={onChange}
      disabled={disabled}
      className={cn("flex flex-wrap gap-4", className)}
    >
      {options.map((option) => (
        <div key={option.value} className="flex items-center space-x-2">
          <RadioGroupItem value={option.value} id={`radio-${option.value}`} />
          <Label htmlFor={`radio-${option.value}`} className="cursor-pointer text-sm">
            {option.label}
          </Label>
        </div>
      ))}
    </RadioGroup>
  );
});
RadioSelect.displayName = "RadioSelect";

/**
 * 체크박스 선택 컴포넌트
 */
const CheckSelect = forwardRef<
  HTMLDivElement,
  {
    options: SelectOption[];
    value?: string[];
    onChange?: (value: string[]) => void;
    maxSelect?: number;
    disabled?: boolean;
    className?: string;
  }
>(({ options, value = [], onChange, maxSelect, disabled, className }, ref) => {
  const handleChange = useCallback(
    (optionValue: string, checked: boolean) => {
      if (checked) {
        if (maxSelect && value.length >= maxSelect) return;
        onChange?.([...value, optionValue]);
      } else {
        onChange?.(value.filter((v) => v !== optionValue));
      }
    },
    [value, maxSelect, onChange],
  );

  return (
    <div ref={ref} className={cn("flex flex-wrap gap-4", className)}>
      {options.map((option) => (
        <div key={option.value} className="flex items-center space-x-2">
          <Checkbox
            id={`check-${option.value}`}
            checked={value.includes(option.value)}
            onCheckedChange={(checked) => handleChange(option.value, checked as boolean)}
            disabled={disabled || (maxSelect && value.length >= maxSelect && !value.includes(option.value))}
          />
          <Label htmlFor={`check-${option.value}`} className="cursor-pointer text-sm">
            {option.label}
          </Label>
        </div>
      ))}
    </div>
  );
});
CheckSelect.displayName = "CheckSelect";

/**
 * 태그 선택 컴포넌트
 */
const TagSelect = forwardRef<
  HTMLDivElement,
  {
    options: SelectOption[];
    value?: string[];
    onChange?: (value: string[]) => void;
    maxSelect?: number;
    disabled?: boolean;
    className?: string;
  }
>(({ options, value = [], onChange, maxSelect, disabled, className }, ref) => {
  const handleToggle = useCallback(
    (optionValue: string) => {
      const isSelected = value.includes(optionValue);
      if (isSelected) {
        onChange?.(value.filter((v) => v !== optionValue));
      } else {
        if (maxSelect && value.length >= maxSelect) return;
        onChange?.([...value, optionValue]);
      }
    },
    [value, maxSelect, onChange],
  );

  return (
    <div ref={ref} className={cn("flex flex-wrap gap-2", className)}>
      {options.map((option) => {
        const isSelected = value.includes(option.value);
        return (
          <Badge
            key={option.value}
            variant={isSelected ? "default" : "outline"}
            className={cn("cursor-pointer transition-colors", disabled && "cursor-not-allowed opacity-50")}
            onClick={() => !disabled && handleToggle(option.value)}
          >
            {option.label}
            {isSelected && <X className="ml-1 h-3 w-3" />}
          </Badge>
        );
      })}
    </div>
  );
});
TagSelect.displayName = "TagSelect";

/**
 * 토글 선택 컴포넌트 (Boolean용)
 */
const ToggleSelect = forwardRef<
  HTMLDivElement,
  {
    options: SelectOption[];
    value?: string;
    onChange?: (value: string) => void;
    disabled?: boolean;
    className?: string;
  }
>(({ options, value, onChange, disabled, className }, ref) => {
  // 토글은 2개 옵션만 지원
  const [offOption, onOption] =
    options.length >= 2
      ? [options[0], options[1]]
      : [
          { value: "false", label: "아니오" },
          { value: "true", label: "예" },
        ];

  const isOn = value === onOption.value;

  return (
    <div ref={ref} className={cn("flex items-center gap-3", className)}>
      <span className={cn("text-sm", !isOn && "font-medium")}>{offOption.label}</span>
      <Switch
        checked={isOn}
        onCheckedChange={(checked) => onChange?.(checked ? onOption.value : offOption.value)}
        disabled={disabled}
      />
      <span className={cn("text-sm", isOn && "font-medium")}>{onOption.label}</span>
    </div>
  );
});
ToggleSelect.displayName = "ToggleSelect";

/**
 * 스왑 선택 컴포넌트 (좌우 이동 방식)
 */
const SwapSelect = forwardRef<
  HTMLDivElement,
  {
    options: SelectOption[];
    value?: string[];
    onChange?: (value: string[]) => void;
    maxSelect?: number;
    disabled?: boolean;
    className?: string;
  }
>(({ options, value = [], onChange, disabled, className }, ref) => {
  const available = useMemo(() => options.filter((o) => !value.includes(o.value)), [options, value]);

  const selected = useMemo(() => options.filter((o) => value.includes(o.value)), [options, value]);

  const handleMoveRight = useCallback(
    (optionValue: string) => {
      onChange?.([...value, optionValue]);
    },
    [value, onChange],
  );

  const handleMoveLeft = useCallback(
    (optionValue: string) => {
      onChange?.(value.filter((v) => v !== optionValue));
    },
    [value, onChange],
  );

  const handleMoveAllRight = useCallback(() => {
    onChange?.(options.map((o) => o.value));
  }, [options, onChange]);

  const handleMoveAllLeft = useCallback(() => {
    onChange?.([]);
  }, [onChange]);

  return (
    <div ref={ref} className={cn("flex items-stretch gap-2", className)}>
      {/* 왼쪽: 선택 가능 */}
      <div className="flex-1 rounded-md border">
        <div className="bg-muted border-b p-2 text-xs font-medium">선택 가능</div>
        <div className="max-h-40 space-y-1 overflow-y-auto p-2">
          {available.map((option) => (
            <div
              key={option.value}
              className={cn(
                "hover:bg-accent cursor-pointer rounded p-2 text-sm",
                disabled && "cursor-not-allowed opacity-50",
              )}
              onClick={() => !disabled && handleMoveRight(option.value)}
            >
              {option.label}
            </div>
          ))}
          {available.length === 0 && <div className="text-muted-foreground p-2 text-xs">항목 없음</div>}
        </div>
      </div>

      {/* 중앙: 이동 버튼 */}
      <div className="flex flex-col justify-center gap-1">
        <Button
          variant="outline"
          size="icon"
          className="h-8 w-8"
          onClick={handleMoveAllRight}
          disabled={disabled || available.length === 0}
        >
          <ArrowLeftRight className="h-4 w-4 rotate-180" />
        </Button>
        <Button
          variant="outline"
          size="icon"
          className="h-8 w-8"
          onClick={handleMoveAllLeft}
          disabled={disabled || selected.length === 0}
        >
          <ArrowLeftRight className="h-4 w-4" />
        </Button>
      </div>

      {/* 오른쪽: 선택됨 */}
      <div className="flex-1 rounded-md border">
        <div className="bg-primary/10 border-b p-2 text-xs font-medium">선택됨</div>
        <div className="max-h-40 space-y-1 overflow-y-auto p-2">
          {selected.map((option) => (
            <div
              key={option.value}
              className={cn(
                "hover:bg-accent flex cursor-pointer items-center justify-between rounded p-2 text-sm",
                disabled && "cursor-not-allowed opacity-50",
              )}
              onClick={() => !disabled && handleMoveLeft(option.value)}
            >
              <span>{option.label}</span>
              <X className="h-3 w-3 opacity-50" />
            </div>
          ))}
          {selected.length === 0 && <div className="text-muted-foreground p-2 text-xs">선택 없음</div>}
        </div>
      </div>
    </div>
  );
});
SwapSelect.displayName = "SwapSelect";

/**
 * 메인 UnifiedSelect 컴포넌트
 */
export const UnifiedSelect = forwardRef<HTMLDivElement, UnifiedSelectProps>((props, ref) => {
  const {
    id,
    label,
    required,
    readonly,
    disabled,
    style,
    size,
    config: configProp,
    value,
    onChange,
    tableName,
    columnName,
  } = props;

  // config가 없으면 기본값 사용
  const config = configProp || { mode: "dropdown" as const, source: "static" as const, options: [] };

  const [options, setOptions] = useState<SelectOption[]>(config.options || []);
  const [loading, setLoading] = useState(false);
  const [optionsLoaded, setOptionsLoaded] = useState(false);

  // 옵션 로딩에 필요한 값들만 추출 (객체 참조 대신 원시값 사용)
  const rawSource = config.source;
  const categoryTable = (config as any).categoryTable;
  const categoryColumn = (config as any).categoryColumn;

  // category 소스 유지 (category_values 테이블에서 로드)
  const source = rawSource;
  const codeGroup = config.codeGroup;

  const entityTable = config.entityTable;
  const entityValueColumn = config.entityValueColumn || config.entityValueField;
  const entityLabelColumn = config.entityLabelColumn || config.entityLabelField;
  const table = config.table;
  const valueColumn = config.valueColumn;
  const labelColumn = config.labelColumn;
  const apiEndpoint = config.apiEndpoint;
  const staticOptions = config.options;

  // 계층 코드 연쇄 선택 관련
  const hierarchical = config.hierarchical;
  const parentField = config.parentField;

  // FormContext에서 부모 필드 값 가져오기 (Context가 없으면 null)
  const formContext = useContext(UnifiedFormContext);

  // 부모 필드의 값 계산
  const parentValue = useMemo(() => {
    if (!hierarchical || !parentField) return null;

    // FormContext가 있으면 거기서 값 가져오기
    if (formContext) {
      const val = formContext.getValue(parentField);
      return val as string | null;
    }

    return null;
  }, [hierarchical, parentField, formContext]);

  // 데이터 소스에 따른 옵션 로딩 (원시값 의존성만 사용)
  useEffect(() => {
    // 계층 구조인 경우 부모 값이 변경되면 다시 로드
    if (hierarchical && source === "code") {
      setOptionsLoaded(false);
    }
  }, [parentValue, hierarchical, source]);

  useEffect(() => {
    // 이미 로드된 경우 스킵 (static 제외, 계층 구조 제외)
    if (optionsLoaded && source !== "static") {
      return;
    }

    const loadOptions = async () => {
      if (source === "static") {
        setOptions(staticOptions || []);
        setOptionsLoaded(true);
        return;
      }

      setLoading(true);
      try {
        let fetchedOptions: SelectOption[] = [];

        if (source === "code" && codeGroup) {
          // 계층 구조 사용 시 자식 코드만 로드
          if (hierarchical) {
            const params = new URLSearchParams();
            if (parentValue) {
              params.append("parentCodeValue", parentValue);
            }
            const queryString = params.toString();
            const url = `/common-codes/categories/${codeGroup}/children${queryString ? `?${queryString}` : ""}`;
            const response = await apiClient.get(url);
            const data = response.data;
            if (data.success && data.data) {
              fetchedOptions = data.data.map((item: { value: string; label: string; hasChildren: boolean }) => ({
                value: item.value,
                label: item.label,
              }));
            }
          } else {
            // 일반 공통코드에서 로드 (올바른 API 경로: /common-codes/categories/:categoryCode/options)
            const response = await apiClient.get(`/common-codes/categories/${codeGroup}/options`);
            const data = response.data;
            if (data.success && data.data) {
              fetchedOptions = data.data.map((item: { value: string; label: string }) => ({
                value: item.value,
                label: item.label,
              }));
            }
          }
        } else if (source === "db" && table) {
          // DB 테이블에서 로드
          const response = await apiClient.get(`/entity/${table}/options`, {
            params: {
              value: valueColumn || "id",
              label: labelColumn || "name",
            },
          });
          const data = response.data;
          if (data.success && data.data) {
            fetchedOptions = data.data;
          }
        } else if (source === "entity" && entityTable) {
          // 엔티티(참조 테이블)에서 로드
          const valueCol = entityValueColumn || "id";
          const labelCol = entityLabelColumn || "name";
          const response = await apiClient.get(`/entity/${entityTable}/options`, {
            params: {
              value: valueCol,
              label: labelCol,
            },
          });
          const data = response.data;
          if (data.success && data.data) {
            fetchedOptions = data.data;
          }
        } else if (source === "api" && apiEndpoint) {
          // 외부 API에서 로드
          const response = await apiClient.get(apiEndpoint);
          const data = response.data;
          if (Array.isArray(data)) {
            fetchedOptions = data;
          }
        } else if (source === "category") {
          // 카테고리에서 로드 (category_values 테이블)
          // tableName, columnName은 props에서 가져옴
          const catTable = categoryTable || tableName;
          const catColumn = categoryColumn || columnName;

          if (catTable && catColumn) {
            const response = await apiClient.get(`/table-categories/${catTable}/${catColumn}/values`);
            const data = response.data;
            if (data.success && data.data) {
              // 트리 구조를 평탄화하여 옵션으로 변환
              // value로 valueId를 사용하여 채번 규칙 매핑과 일치하도록 함
              const flattenTree = (
                items: { valueId: number; valueCode: string; valueLabel: string; children?: any[] }[],
                depth: number = 0,
              ): SelectOption[] => {
                const result: SelectOption[] = [];
                for (const item of items) {
                  const prefix = depth > 0 ? "\u00A0\u00A0\u00A0".repeat(depth) + "└ " : "";
                  result.push({
                    value: String(item.valueId), // valueId를 value로 사용 (채번 매핑과 일치)
                    label: prefix + item.valueLabel,
                  });
                  if (item.children && item.children.length > 0) {
                    result.push(...flattenTree(item.children, depth + 1));
                  }
                }
                return result;
              };
              fetchedOptions = flattenTree(data.data);
            }
          }
        } else if (source === "select" || source === "distinct") {
          // 해당 테이블의 해당 컬럼에서 DISTINCT 값 조회
          // tableName, columnName은 props에서 가져옴
          // 🆕 columnName이 컴포넌트 ID 형식(comp_xxx)이면 유효하지 않으므로 건너뜀
          const isValidColumnName = columnName && !columnName.startsWith("comp_");
          if (tableName && isValidColumnName) {
            const response = await apiClient.get(`/entity/${tableName}/distinct/${columnName}`);
            const data = response.data;
            if (data.success && data.data) {
              fetchedOptions = data.data.map((item: { value: string; label: string }) => ({
                value: String(item.value),
                label: String(item.label),
              }));
            }
          } else if (!isValidColumnName) {
            // columnName이 없거나 유효하지 않으면 빈 옵션
            console.warn("UnifiedSelect: 유효한 columnName이 없어 옵션을 로드하지 않습니다.", {
              tableName,
              columnName,
            });
          }
        }

        setOptions(fetchedOptions);
        setOptionsLoaded(true);
      } catch (error) {
        console.error("옵션 로딩 실패:", error);
        setOptions([]);
      } finally {
        setLoading(false);
      }
    };

    loadOptions();
  }, [
    source,
    entityTable,
    entityValueColumn,
    entityLabelColumn,
    codeGroup,
    table,
    valueColumn,
    labelColumn,
    apiEndpoint,
    staticOptions,
    optionsLoaded,
    hierarchical,
    parentValue,
  ]);

  // 모드별 컴포넌트 렌더링
  const renderSelect = () => {
    if (loading) {
      return <div className="text-muted-foreground flex h-10 items-center text-sm">로딩 중...</div>;
    }

    const isDisabled = disabled || readonly;

    switch (config.mode) {
      case "dropdown":
        return (
          <DropdownSelect
            options={options}
            value={value}
            onChange={onChange}
            placeholder="선택"
            searchable={config.searchable}
            multiple={config.multiple}
            maxSelect={config.maxSelect}
            allowClear={config.allowClear}
            disabled={isDisabled}
          />
        );

      case "radio":
        return (
          <RadioSelect
            options={options}
            value={typeof value === "string" ? value : value?.[0]}
            onChange={(v) => onChange?.(v)}
            disabled={isDisabled}
          />
        );

      case "check":
        return (
          <CheckSelect
            options={options}
            value={Array.isArray(value) ? value : value ? [value] : []}
            onChange={onChange}
            maxSelect={config.maxSelect}
            disabled={isDisabled}
          />
        );

      case "tag":
        return (
          <TagSelect
            options={options}
            value={Array.isArray(value) ? value : value ? [value] : []}
            onChange={onChange}
            maxSelect={config.maxSelect}
            disabled={isDisabled}
          />
        );

      case "toggle":
        return (
          <ToggleSelect
            options={options}
            value={typeof value === "string" ? value : value?.[0]}
            onChange={(v) => onChange?.(v)}
            disabled={isDisabled}
          />
        );

      case "swap":
        return (
          <SwapSelect
            options={options}
            value={Array.isArray(value) ? value : value ? [value] : []}
            onChange={onChange}
            maxSelect={config.maxSelect}
            disabled={isDisabled}
          />
        );

      default:
        return <DropdownSelect options={options} value={value} onChange={onChange} disabled={isDisabled} />;
    }
  };

  const showLabel = label && style?.labelDisplay !== false;
  const componentWidth = size?.width || style?.width;
  const componentHeight = size?.height || style?.height;

  return (
    <div
      ref={ref}
      id={id}
      className="flex flex-col"
      style={{
        width: componentWidth,
        height: componentHeight,
      }}
    >
      {showLabel && (
        <Label
          htmlFor={id}
          style={{
            fontSize: style?.labelFontSize,
            color: style?.labelColor,
            fontWeight: style?.labelFontWeight,
            marginBottom: style?.labelMarginBottom,
          }}
          className="flex-shrink-0 text-sm font-medium"
        >
          {label}
          {required && <span className="ml-0.5 text-amber-500">*</span>}
        </Label>
      )}
      <div className="min-h-0 flex-1">{renderSelect()}</div>
    </div>
  );
});

UnifiedSelect.displayName = "UnifiedSelect";

export default UnifiedSelect;
