// FileUploader.tsx
import React, { useRef, memo, useState } from "react";
import {
	CloudArrowUpIcon,
	XMarkIcon,
	DocumentIcon,
	ExclamationCircleIcon,
	CheckCircleIcon,
} from "@heroicons/react/24/outline";
import { motion, AnimatePresence } from "framer-motion";
import { toast } from "react-hot-toast";
import { useTusUpload } from "@web/src/hooks/useTusUpload";

interface FileUploaderProps {
	endpoint?: string;
	onSuccess?: (url: string) => void;
	onError?: (error: Error) => void;
	maxSize?: number;
	allowedTypes?: string[];
	placeholder?: string;
}

interface FileItemProps {
	file: File;
	progress?: number;
	onRemove: (name: string) => void;
	isUploaded: boolean;
}

const FileItem: React.FC<FileItemProps> = memo(
	({ file, progress, onRemove, isUploaded }) => (
		<motion.div
			initial={{ opacity: 0, y: 20 }}
			animate={{ opacity: 1, y: 0 }}
			exit={{ opacity: 0, x: -20 }}
			className="group flex items-center p-4 bg-white rounded-xl border border-gray-100 shadow-sm hover:shadow-md transition-all duration-200">
			<DocumentIcon className="w-8 h-8 text-blue-500/80" />
			<div className="ml-3 flex-1">
				<div className="flex items-center justify-between">
					<p className="text-sm font-medium text-gray-900 truncate max-w-[200px]">
						{file.name}
					</p>
					<button
						onClick={() => onRemove(file.name)}
						className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-gray-100 rounded-full"
						aria-label={`Remove ${file.name}`}>
						<XMarkIcon className="w-5 h-5 text-gray-500" />
					</button>
				</div>
				{!isUploaded && progress !== undefined && (
					<div className="mt-2">
						<div className="bg-gray-100 rounded-full h-1.5 overflow-hidden">
							<motion.div
								className="bg-blue-500 h-1.5 rounded-full"
								initial={{ width: 0 }}
								animate={{ width: `${progress}%` }}
								transition={{ duration: 0.3 }}
							/>
						</div>
						<span className="text-xs text-gray-500 mt-1">
							{progress}%
						</span>
					</div>
				)}
				{isUploaded && (
					<div className="mt-2 flex items-center text-green-500">
						<CheckCircleIcon className="w-4 h-4 mr-1" />
						<span className="text-xs">上传完成</span>
					</div>
				)}
			</div>
		</motion.div>
	)
);

const FileUploader: React.FC<FileUploaderProps> = ({
	endpoint = "",
	onSuccess,
	onError,
	maxSize = 100,
	placeholder = "点击或拖拽文件到这里上传",
	allowedTypes = ["*/*"],
}) => {
	const [isDragging, setIsDragging] = useState(false);
	const [files, setFiles] = useState<
		Array<{ file: File; isUploaded: boolean }>
	>([]);
	const fileInputRef = useRef<HTMLInputElement>(null);

	const { progress, isUploading, uploadError, handleFileUpload } =
		useTusUpload();

	const handleError = (error: Error) => {
		toast.error(error.message);
		onError?.(error);
	};

	const handleDrag = (e: React.DragEvent) => {
		e.preventDefault();
		e.stopPropagation();
		if (e.type === "dragenter" || e.type === "dragover") {
			setIsDragging(true);
		} else if (e.type === "dragleave") {
			setIsDragging(false);
		}
	};

	const validateFile = (file: File) => {
		if (file.size > maxSize * 1024 * 1024) {
			throw new Error(`文件大小不能超过 ${maxSize}MB`);
		}
		if (
			!allowedTypes.includes("*/*") &&
			!allowedTypes.includes(file.type)
		) {
			throw new Error(
				`不支持的文件类型。支持的类型: ${allowedTypes.join(", ")}`
			);
		}
	};

	const uploadFile = (file: File) => {
		try {
			validateFile(file);
			handleFileUpload(
				file,
				(upload) => {
					onSuccess?.(upload.url || "");
					setFiles((prev) =>
						prev.map((f) =>
							f.file.name === file.name
								? { ...f, isUploaded: true }
								: f
						)
					);
				},
				handleError
			);
		} catch (error) {
			handleError(error as Error);
		}
	};

	const handleDrop = (e: React.DragEvent) => {
		e.preventDefault();
		e.stopPropagation();
		setIsDragging(false);

		const droppedFiles = Array.from(e.dataTransfer.files);
		setFiles((prev) => [
			...prev,
			...droppedFiles.map((file) => ({ file, isUploaded: false })),
		]);
		droppedFiles.forEach(uploadFile);
	};

	const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
		if (e.target.files) {
			const selectedFiles = Array.from(e.target.files);
			setFiles((prev) => [
				...prev,
				...selectedFiles.map((file) => ({ file, isUploaded: false })),
			]);
			selectedFiles.forEach(uploadFile);
		}
	};

	const removeFile = (fileName: string) => {
		setFiles((prev) => prev.filter(({ file }) => file.name !== fileName));
	};

	const handleClick = () => {
		fileInputRef.current?.click();
	};

	return (
		<div className="w-full space-y-4">
			<div
				onClick={handleClick}
				onDragEnter={handleDrag}
				onDragLeave={handleDrag}
				onDragOver={handleDrag}
				onDrop={handleDrop}
				className={`
          relative flex flex-col items-center justify-center w-full h-32
          border-2 border-dashed rounded-lg cursor-pointer
          transition-colors duration-200 ease-in-out
          ${
				isDragging
					? "border-blue-500 bg-blue-50"
					: "border-gray-300 hover:border-blue-500"
			}
        `}>
				<input
					ref={fileInputRef}
					type="file"
					multiple
					onChange={handleFileSelect}
					accept={allowedTypes.join(",")}
					className="hidden"
				/>
				<CloudArrowUpIcon className="w-10 h-10 text-gray-400" />
				<p className="mt-2 text-sm text-gray-500">{placeholder}</p>
				{isDragging && (
					<div className="absolute inset-0 flex items-center justify-center bg-blue-100/50 rounded-lg">
						<p className="text-blue-500 font-medium">
							释放文件以上传
						</p>
					</div>
				)}
			</div>

			<AnimatePresence>
				<div className="space-y-3">
					{files.map(({ file, isUploaded }) => (
						<FileItem
							key={file.name}
							file={file}
							progress={isUploaded ? 100 : progress}
							onRemove={removeFile}
							isUploaded={isUploaded}
						/>
					))}
				</div>
			</AnimatePresence>

			{uploadError && (
				<div className="flex items-center text-red-500 text-sm">
					<ExclamationCircleIcon className="w-4 h-4 mr-1" />
					<span>{uploadError}</span>
				</div>
			)}
		</div>
	);
};

export default FileUploader;
