import { useState, useRef, useCallback, useEffect, type DragEvent, type ChangeEvent } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { SectionWrapper } from '@/features/report/components/ui/SectionWrapper';
import { VideoFilled, FileTextFilled } from '@/shared/icons/FilledIcons';
import { Button } from '@/shared/ui/button';
import { Spinner } from '@/shared/ui/spinner';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/shared/ui/dialog';
import {
  useUploadAnalysisRunFile,
  useGetAnalysisRunFiles,
  useDeleteAnalysisRunFile,
} from '@/shared/api/generated/analysis/analysis';
import type { FileListItem } from '@/shared/api/model/fileListItem';
import {
  type UploadCategory,
  type AssetCategory,
  categoryConfig,
  categoryBadge,
  ALL_ACCEPT,
  categorize,
  formatSize,
  uid,
} from '../data/myAssetUploadConstants';

// ─── Types ───

type AssetStatus = 'idle' | 'uploading' | 'done' | 'error';

interface UploadedAsset {
  id: string;
  /** 로컬 업로드 직후엔 File 보유, 서버에서 받은 항목은 null. */
  file: File | null;
  category: AssetCategory;
  previewUrl: string | null;
  name: string;
  size: string;
  uploadedAt: Date;
  status: AssetStatus;
  remoteId?: number;
  errorMessage?: string;
}

interface MyAssetUploadProps {
  /** 업로드 대상 analysis run id. 없으면 로컬 미리보기만 동작. */
  analysisRunId?: string;
}

// 백엔드 FileType ('image'|'video'|'audio'|'document'|'file') → 로컬 AssetCategory 1:1.
function mapFileTypeToCategory(fileType: string): AssetCategory {
  if (fileType === 'image' || fileType === 'video' || fileType === 'audio' || fileType === 'document') {
    return fileType;
  }
  return 'file';
}

function serverFileToAsset(item: FileListItem): UploadedAsset {
  const cat = mapFileTypeToCategory(item.file_type);
  return {
    id: `remote-${item.id}`,
    file: null,
    category: cat,
    previewUrl: cat === 'image' || cat === 'video' ? item.file_url : null,
    name: item.file_name,
    size: typeof item.size_bytes === 'number' ? formatSize(item.size_bytes) : '',
    uploadedAt: new Date(item.created_at),
    status: 'done',
    remoteId: item.id,
  };
}

// ─── Component ───

export default function MyAssetUpload({ analysisRunId }: MyAssetUploadProps = {}) {
  const [assets, setAssets] = useState<UploadedAsset[]>([]);
  const [activeFilter, setActiveFilter] = useState<UploadCategory>('all');
  const [isDragOver, setIsDragOver] = useState(false);
  const [pendingDeleteId, setPendingDeleteId] = useState<string | null>(null);
  const [isDeleting, setIsDeleting] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  const { mutateAsync: uploadFile } = useUploadAnalysisRunFile();
  const { mutateAsync: deleteFile } = useDeleteAnalysisRunFile();
  const filesQuery = useGetAnalysisRunFiles(
    analysisRunId ?? '',
    { query: { enabled: !!analysisRunId } },
  );

  // 서버 파일 목록 → 로컬 assets 머지. 진행 중/실패 로컬 항목은 보존.
  useEffect(() => {
    if (filesQuery.data?.status !== 200) return;
    const serverAssets = filesQuery.data.data.map(serverFileToAsset);
    const serverIds = new Set(serverAssets.map((a) => a.remoteId));
    setAssets((prev) => {
      const localPending = prev.filter(
        (a) => a.remoteId == null || !serverIds.has(a.remoteId),
      );
      return [...localPending, ...serverAssets];
    });
  }, [filesQuery.data]);

  const processFiles = useCallback(
    (files: FileList | File[]) => {
      const newAssets: UploadedAsset[] = Array.from(files).map((file) => {
        const cat = categorize(file);
        const previewUrl =
          cat === 'image' || cat === 'video' ? URL.createObjectURL(file) : null;
        return {
          id: uid(),
          file,
          category: cat,
          previewUrl,
          name: file.name,
          size: formatSize(file.size),
          uploadedAt: new Date(),
          status: analysisRunId ? 'uploading' : 'idle',
        };
      });
      setAssets((prev) => [...newAssets, ...prev]);

      if (!analysisRunId) return;

      newAssets.forEach(async (asset) => {
        try {
          const res = await uploadFile({
            runId: analysisRunId,
            // orval 이 binary 필드를 string 으로 타입 생성하므로 캐스팅 필요. 런타임은 FormData 로 정상 처리.
            data: { file: asset.file as unknown as string, file_type: asset.category },
          });
          if (res.status === 201) {
            setAssets((prev) =>
              prev.map((a) =>
                a.id === asset.id ? { ...a, status: 'done', remoteId: res.data.id } : a,
              ),
            );
            filesQuery.refetch();
          } else {
            setAssets((prev) =>
              prev.map((a) =>
                a.id === asset.id
                  ? { ...a, status: 'error', errorMessage: '업로드 검증 실패' }
                  : a,
              ),
            );
          }
        } catch (err) {
          setAssets((prev) =>
            prev.map((a) =>
              a.id === asset.id
                ? {
                    ...a,
                    status: 'error',
                    errorMessage: err instanceof Error ? err.message : '업로드 실패',
                  }
                : a,
            ),
          );
        }
      });
    },
    [analysisRunId, uploadFile, filesQuery],
  );

  const handleDrop = useCallback(
    (e: DragEvent) => {
      e.preventDefault();
      setIsDragOver(false);
      if (e.dataTransfer.files.length) processFiles(e.dataTransfer.files);
    },
    [processFiles],
  );

  const handleInputChange = useCallback(
    (e: ChangeEvent<HTMLInputElement>) => {
      if (e.target.files?.length) {
        processFiles(e.target.files);
        e.target.value = '';
      }
    },
    [processFiles],
  );

  const confirmDelete = useCallback(async () => {
    if (!pendingDeleteId) return;
    const target = assets.find((a) => a.id === pendingDeleteId);
    if (!target) {
      setPendingDeleteId(null);
      return;
    }

    setIsDeleting(true);

    if (target.file && target.previewUrl) URL.revokeObjectURL(target.previewUrl);

    if (analysisRunId && target.remoteId != null) {
      try {
        await deleteFile({ runId: analysisRunId, fileId: target.remoteId });
        filesQuery.refetch();
      } catch {
        setIsDeleting(false);
        setPendingDeleteId(null);
        return;
      }
    }

    setAssets((prev) => prev.filter((a) => a.id !== pendingDeleteId));
    setIsDeleting(false);
    setPendingDeleteId(null);
  }, [pendingDeleteId, assets, analysisRunId, deleteFile, filesQuery]);

  const pendingDeleteAsset = pendingDeleteId
    ? assets.find((a) => a.id === pendingDeleteId) ?? null
    : null;

  const filtered =
    activeFilter === 'all' ? assets : assets.filter((a) => a.category === activeFilter);

  const counts: Record<UploadCategory, number> = {
    all: assets.length,
    image: assets.filter((a) => a.category === 'image').length,
    video: assets.filter((a) => a.category === 'video').length,
    audio: assets.filter((a) => a.category === 'audio').length,
    document: assets.filter((a) => a.category === 'document').length,
    file: assets.filter((a) => a.category === 'file').length,
  };

  return (
    <SectionWrapper
      id="my-asset-upload"
      title="나의 소재"
      subtitle="소재 업로드 및 관리"
    >
      {/* Drop Zone */}
      <div
        onDragOver={(e) => { e.preventDefault(); setIsDragOver(true); }}
        onDragLeave={() => setIsDragOver(false)}
        onDrop={handleDrop}
        onClick={() => inputRef.current?.click()}
        className={`relative rounded-2xl border-2 border-dashed p-10 md:p-14 text-center cursor-pointer transition-all mb-8 ${
          isDragOver
            ? 'border-brand-purple-soft bg-brand-tint-purple/60 scale-[1.01]'
            : 'border-slate-200 bg-slate-50/50 hover:border-brand-tint-lavender hover:bg-brand-tint-purple/20'
        }`}
      >
        <input
          ref={inputRef}
          type="file"
          multiple
          accept={ALL_ACCEPT}
          onChange={handleInputChange}
          className="hidden"
        />

        {/* Upload Icon */}
        <div className="flex justify-center mb-4">
          <div className="w-14 h-14 rounded-2xl bg-brand-tint-purple flex items-center justify-center shadow-[3px_4px_12px_rgba(155,138,212,0.15)]">
            <svg width="28" height="28" viewBox="0 0 24 24" fill="none">
              <path
                d="M12 16V4M12 4L8 8M12 4L16 8"
                stroke="#9B8AD4"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
              <path
                d="M4 14V18C4 19.1 4.9 20 6 20H18C19.1 20 20 19.1 20 18V14"
                stroke="#9B8AD4"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
          </div>
        </div>

        <p className="text-brand-navy font-semibold mb-1">
          파일을 드래그하거나 클릭하여 업로드
        </p>
        <p className="text-sm text-slate-400">
          Image, Video, Audio, Document, File 지원 (JPG, MP4, MP3, PDF, DOCX, ZIP 등)
        </p>

        {/* File Type Badges */}
        <div className="flex flex-wrap justify-center gap-2 mt-4">
          {(['image', 'video', 'audio', 'document', 'file'] as const).map((cat) => (
            <span
              key={cat}
              className={`rounded-full px-3 py-1 text-xs font-medium ${categoryBadge[cat]}`}
            >
              {categoryConfig[cat].label}
            </span>
          ))}
        </div>
      </div>

      {/* Filter Tabs + Count */}
      {assets.length > 0 && (
        <>
          <div className="flex flex-wrap items-center gap-2 mb-6">
            {(Object.keys(categoryConfig) as UploadCategory[]).map((key) => (
              <Button
                key={key}
                onClick={() => setActiveFilter(key)}
                variant={activeFilter === key ? 'default' : 'outline'}
                className={`rounded-full px-4 py-2 h-auto text-sm font-medium ${
                  activeFilter === key
                    ? 'bg-gradient-to-r from-brand-purple to-brand-purple-deep text-white shadow-md hover:from-brand-purple hover:to-brand-purple-deep'
                    : 'bg-white border-slate-200 text-slate-600 hover:bg-slate-50 shadow-[2px_3px_6px_rgba(0,0,0,0.04)]'
                }`}
              >
                {categoryConfig[key].label}
                <span className="ml-2 opacity-70">{counts[key]}</span>
              </Button>
            ))}
          </div>

          {/* Uploaded Assets Grid */}
          <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
            <AnimatePresence mode="popLayout">
              {filtered.map((asset) => (
                <motion.div
                  key={asset.id}
                  layout
                  className="bg-white rounded-2xl border border-slate-100 overflow-hidden shadow-[3px_4px_12px_rgba(0,0,0,0.06)] hover:shadow-[4px_6px_16px_rgba(0,0,0,0.09)] transition-shadow group"
                  initial={{ opacity: 0, scale: 0.95 }}
                  animate={{ opacity: 1, scale: 1 }}
                  exit={{ opacity: 0, scale: 0.9 }}
                  transition={{ duration: 0.25 }}
                >
                  {/* Preview Area */}
                  <div className="relative h-40 bg-slate-50 flex items-center justify-center overflow-hidden">
                    {asset.category === 'image' && asset.previewUrl && (
                      <img
                        src={asset.previewUrl}
                        alt={asset.name}
                        className="w-full h-full object-cover"
                      />
                    )}
                    {asset.category === 'video' && asset.previewUrl && (
                      <video
                        src={asset.previewUrl}
                        className="w-full h-full object-cover"
                        muted
                        playsInline
                        onMouseOver={(e) => (e.target as HTMLVideoElement).play()}
                        onMouseOut={(e) => {
                          const v = e.target as HTMLVideoElement;
                          v.pause();
                          v.currentTime = 0;
                        }}
                      />
                    )}
                    {(asset.category === 'audio' ||
                      asset.category === 'document' ||
                      asset.category === 'file') && (
                      <div className="flex flex-col items-center gap-2">
                        <FileTextFilled size={36} className="text-[#D4A872]" />
                        <span className="text-xs text-slate-400 font-medium">
                          {asset.name.split('.').pop()?.toUpperCase()}
                        </span>
                      </div>
                    )}

                    {/* Remove Button */}
                    <Button
                      type="button"
                      variant="ghost"
                      size="icon-sm"
                      onClick={() => setPendingDeleteId(asset.id)}
                      className="absolute top-2 right-2 w-7 h-7 size-7 rounded-full bg-white/90 backdrop-blur-sm border border-slate-100 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity shadow-sm hover:bg-brand-rose-bg"
                    >
                      <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
                        <path d="M2 2L10 10M10 2L2 10" stroke="#7C3A4B" strokeWidth="1.5" strokeLinecap="round" />
                      </svg>
                    </Button>

                    {/* Category Badge */}
                    <span
                      className={`absolute top-2 left-2 rounded-full px-3 py-1 text-xs font-semibold ${categoryBadge[asset.category]}`}
                    >
                      {categoryConfig[asset.category].label}
                    </span>

                    {/* Video Duration Overlay */}
                    {asset.category === 'video' && (
                      <div className="absolute bottom-2 right-2 flex items-center gap-1 bg-black/50 backdrop-blur-sm rounded-full px-2 py-1">
                        <VideoFilled size={10} className="text-white" />
                        <span className="text-xs text-white font-medium">Video</span>
                      </div>
                    )}
                  </div>

                  {/* Info */}
                  <div className="p-4">
                    <p className="text-sm font-medium text-brand-navy truncate mb-1">
                      {asset.name}
                    </p>
                    <div className="flex items-center justify-between gap-2">
                      <p className="text-xs text-slate-400">
                        {asset.size || categoryConfig[asset.category].label}
                      </p>
                      {asset.status === 'uploading' && (
                        <span className="inline-flex items-center gap-1 text-xs text-slate-500">
                          <Spinner size="xs" />
                          업로드 중
                        </span>
                      )}
                      {asset.status === 'done' && (
                        <span className="text-xs text-emerald-600">완료</span>
                      )}
                      {asset.status === 'error' && (
                        <span
                          className="text-xs text-[#7C3A4B] truncate"
                          title={asset.errorMessage}
                        >
                          업로드 실패
                        </span>
                      )}
                    </div>
                  </div>
                </motion.div>
              ))}
            </AnimatePresence>
          </div>
        </>
      )}

      <Dialog
        open={pendingDeleteId !== null}
        onOpenChange={(open) => {
          if (!open && !isDeleting) setPendingDeleteId(null);
        }}
      >
        <DialogContent className="sm:max-w-md">
          <DialogHeader>
            <DialogTitle>파일을 삭제할까요?</DialogTitle>
            <DialogDescription>
              {pendingDeleteAsset
                ? `"${pendingDeleteAsset.name}" 을(를) 삭제합니다. 이 동작은 되돌릴 수 없습니다.`
                : '이 동작은 되돌릴 수 없습니다.'}
            </DialogDescription>
          </DialogHeader>
          <DialogFooter className="gap-2 sm:gap-2">
            <Button
              type="button"
              variant="outline"
              onClick={() => setPendingDeleteId(null)}
              disabled={isDeleting}
            >
              취소
            </Button>
            <Button
              type="button"
              onClick={confirmDelete}
              disabled={isDeleting}
              className="bg-[#7C3A4B] hover:bg-[#7C3A4B]/90 text-white"
            >
              {isDeleting ? <Spinner size="xs" className="mr-1" /> : null}
              삭제
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </SectionWrapper>
  );
}
