"use client";

/**
 * FilePicker — canonical input 의 file 분기 picker.
 *
 * 옛 file-upload / v2-file-upload / v2-media / image-widget 의 본체 import 없이
 * 브라우저 File 선택 + 선택된 파일 표시(파일명 / 이미지 미리보기) + 삭제만 처리한다.
 * 업로드/저장 API 통합은 별도 phase. 이 컴포넌트는 InputComponent 의 propagate 흐름
 * (onChange) 를 그대로 타며, 값은 `File | File[]` 또는 기존 URL 문자열 모두 받는다.
 *
 * Props:
 *  - value: File | File[] | string | string[] | undefined
 *  - onChange(value): 부모 알림. multiple 이면 배열, 아니면 단일
 *  - accept: input[type=file] accept 와 동일
 *  - multiple: true 면 다중 선택
 *  - maxFiles: 다중 시 최대 개수 (초과 자르기)
 *  - disabled / readonly: 선택/삭제 모두 비활성
 *  - placeholder: 빈 상태 안내 텍스트
 *  - showPreview: image 미리보기 (accept 가 image/* 이거나 명시 시 자동)
 *
 * value 표시 규칙:
 *  - File 객체: file.name 또는 URL.createObjectURL preview (image)
 *  - 문자열: 그대로 URL/이름으로 표시 (서버 저장된 path 호환)
 */

import React, { useEffect, useMemo, useRef, useState } from "react";
import { Upload, X, FileText, Image as ImageIcon } from "lucide-react";
import { cn } from "@/lib/utils";

export interface FilePickerProps {
  value?: File | string | Array<File | string> | null;
  onChange?: (value: File | string | Array<File | string> | null) => void;
  accept?: string;
  multiple?: boolean;
  maxFiles?: number;
  disabled?: boolean;
  readonly?: boolean;
  placeholder?: string;
  showPreview?: boolean;
  className?: string;
}

type DisplayItem = {
  key: string;
  label: string;
  previewUrl?: string;
  source: File | string;
  isImage: boolean;
};

function detectIsImage(name: string | undefined, accept: string | undefined): boolean {
  if (!name) return !!accept && accept.startsWith("image/");
  const lower = name.toLowerCase();
  if (lower.match(/\.(png|jpe?g|gif|webp|svg|bmp|avif)$/)) return true;
  return !!accept && accept.startsWith("image/");
}

function toArray(value: FilePickerProps["value"]): Array<File | string> {
  if (value == null) return [];
  if (Array.isArray(value)) return value.filter((v): v is File | string => !!v);
  return [value];
}

function isFileValue(value: File | string): value is File {
  return typeof File !== "undefined" && value instanceof File;
}

export const FilePicker = React.forwardRef<HTMLDivElement, FilePickerProps>(
  (
    {
      value,
      onChange,
      accept,
      multiple,
      maxFiles,
      disabled,
      readonly,
      placeholder,
      showPreview,
      className,
    },
    ref,
  ) => {
    const inputRef = useRef<HTMLInputElement>(null);
    const lockEdit = !!(disabled || readonly);

    const items = useMemo<DisplayItem[]>(() => {
      const arr = toArray(value);
      return arr.map((item, idx) => {
        if (isFileValue(item)) {
          const isImage = detectIsImage(item.name, accept);
          return {
            key: `${item.name}-${item.size}-${item.lastModified}-${idx}`,
            label: item.name,
            previewUrl: isImage ? URL.createObjectURL(item) : undefined,
            source: item,
            isImage,
          };
        }
        // 문자열 (URL 또는 파일명) — 서버 저장된 경로 호환
        const str = String(item);
        const fileName = str.split("/").pop() || str;
        const isImage = detectIsImage(fileName, accept);
        return {
          key: `${str}-${idx}`,
          label: fileName,
          previewUrl: isImage ? str : undefined,
          source: str,
          isImage,
        };
      });
    }, [value, accept]);

    // File preview URL revoke (메모리 누수 방지)
    useEffect(() => {
      const urls = items
        .filter((it) => isFileValue(it.source) && it.previewUrl)
        .map((it) => it.previewUrl!);
      return () => {
        urls.forEach((u) => URL.revokeObjectURL(u));
      };
    }, [items]);

    const handleSelectFiles = (e: React.ChangeEvent<HTMLInputElement>) => {
      if (lockEdit) return;
      const picked = Array.from(e.target.files || []);
      if (picked.length === 0) return;

      if (!multiple) {
        onChange?.(picked[0]);
      } else {
        const existing = toArray(value);
        const combined: Array<File | string> = [...existing, ...picked];
        const limited = maxFiles && maxFiles > 0 ? combined.slice(0, maxFiles) : combined;
        onChange?.(limited);
      }
      // 같은 파일 다시 선택 가능하도록 input value 초기화
      if (inputRef.current) inputRef.current.value = "";
    };

    const handleRemove = (key: string) => {
      if (lockEdit) return;
      if (!multiple) {
        onChange?.(null);
        return;
      }
      const next = items.filter((it) => it.key !== key).map((it) => it.source);
      onChange?.(next);
    };

    const wantPreview = showPreview ?? (!!accept && accept.startsWith("image/"));
    const atLimit = multiple && maxFiles && items.length >= maxFiles;

    const openPicker = () => {
      if (lockEdit || atLimit) return;
      inputRef.current?.click();
    };

    return (
      <div
        ref={ref}
        className={cn(
          "flex h-full w-full flex-col gap-1 overflow-auto px-1 py-1 text-sm",
          lockEdit && "cursor-not-allowed",
          className,
        )}
      >
        <input
          ref={inputRef}
          type="file"
          accept={accept}
          multiple={!!multiple}
          onChange={handleSelectFiles}
          disabled={lockEdit}
          className="hidden"
          tabIndex={-1}
        />

        {items.length === 0 ? (
          <button
            type="button"
            onClick={openPicker}
            disabled={lockEdit}
            className={cn(
              "flex h-full w-full items-center justify-center gap-1.5 rounded-sm border border-dashed border-border bg-background px-2 py-1 text-xs text-muted-foreground",
              lockEdit ? "cursor-not-allowed opacity-60" : "hover:bg-muted/40",
            )}
          >
            <Upload className="h-3.5 w-3.5" />
            <span>{placeholder || (wantPreview ? "이미지 선택" : "파일 선택")}</span>
          </button>
        ) : (
          <>
            <div className="flex flex-wrap gap-1.5">
              {items.map((it) => (
                <div
                  key={it.key}
                  className="flex items-center gap-1 rounded-sm border border-border bg-background px-1.5 py-0.5 text-xs"
                >
                  {wantPreview && it.previewUrl ? (
                    // eslint-disable-next-line @next/next/no-img-element
                    <img
                      src={it.previewUrl}
                      alt={it.label}
                      className="h-6 w-6 rounded-sm object-cover"
                    />
                  ) : it.isImage ? (
                    <ImageIcon className="h-3.5 w-3.5 text-muted-foreground" />
                  ) : (
                    <FileText className="h-3.5 w-3.5 text-muted-foreground" />
                  )}
                  <span className="max-w-[160px] truncate" title={it.label}>
                    {it.label}
                  </span>
                  {!lockEdit && (
                    <button
                      type="button"
                      onClick={() => handleRemove(it.key)}
                      className="ml-0.5 text-muted-foreground hover:text-foreground"
                      title="제거"
                    >
                      <X className="h-3 w-3" />
                    </button>
                  )}
                </div>
              ))}
              {(multiple || items.length === 0) && (
                <button
                  type="button"
                  onClick={openPicker}
                  disabled={lockEdit || !!atLimit}
                  title={atLimit ? `최대 ${maxFiles}개까지` : "추가"}
                  className={cn(
                    "flex items-center gap-1 rounded-sm border border-dashed border-border bg-background px-1.5 py-0.5 text-xs text-muted-foreground",
                    (lockEdit || atLimit) ? "cursor-not-allowed opacity-50" : "hover:bg-muted/40",
                  )}
                >
                  <Upload className="h-3 w-3" />
                  <span>{multiple ? "추가" : "교체"}</span>
                </button>
              )}
            </div>
          </>
        )}
      </div>
    );
  },
);
FilePicker.displayName = "FilePicker";
