import { useState, useCallback, useRef, memo } from 'react'
import { CloudArrowUpIcon, XMarkIcon, DocumentIcon, ExclamationCircleIcon } from '@heroicons/react/24/outline'
import * as tus from 'tus-js-client'
import { motion, AnimatePresence } from 'framer-motion'
import { toast } from 'react-hot-toast'
interface FileUploaderProps {
    endpoint?: string
    onSuccess?: (url: string) => void
    onError?: (error: Error) => void
    maxSize?: number
    allowedTypes?: string[]
    placeholder?: string
}

const FileItem = memo(({ file, progress, onRemove }: {
    file: File
    progress?: number
    onRemove: (name: string) => void
}) => (
    <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>
            {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>
            )}
        </div>
    </motion.div>
))

export default function FileUploader({
    endpoint='',
    onSuccess,
    onError,
    maxSize = 100,
    placeholder = '点击或拖拽文件到这里上传',
    allowedTypes = ['*/*']
}: FileUploaderProps) {
    const [isDragging, setIsDragging] = useState(false)
    const [files, setFiles] = useState<File[]>([])
    const [progress, setProgress] = useState<{ [key: string]: number }>({})
    const fileInputRef = useRef<HTMLInputElement>(null)

    const handleError = useCallback((error: Error) => {
        toast.error(error.message)
        onError?.(error)
    }, [onError])

    const handleDrag = useCallback((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 = useCallback((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(', ')}`)
        }
    }, [maxSize, allowedTypes])

    const uploadFile = async (file: File) => {
        try {
            validateFile(file)

            const upload = new tus.Upload(file, {
                endpoint,
                retryDelays: [0, 3000, 5000, 10000, 20000],
                metadata: {
                    filename: file.name,
                    filetype: file.type
                },
                onError: handleError,
                onProgress: (bytesUploaded, bytesTotal) => {
                    const percentage = (bytesUploaded / bytesTotal * 100).toFixed(2)
                    setProgress(prev => ({
                        ...prev,
                        [file.name]: parseFloat(percentage)
                    }))
                },
                onSuccess: () => {
                    onSuccess?.(upload.url || '')
                    setProgress(prev => {
                        const newProgress = { ...prev }
                        delete newProgress[file.name]
                        return newProgress
                    })
                }
            })

            upload.start()
        } catch (error) {
            handleError(error as Error)
        }
    }

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

        const droppedFiles = Array.from(e.dataTransfer.files)
        setFiles(prev => [...prev, ...droppedFiles])
        droppedFiles.forEach(uploadFile)
    }, [])

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

    const removeFile = (fileName: string) => {
        setFiles(prev => prev.filter(file => file.name !== fileName))
        setProgress(prev => {
            const newProgress = { ...prev }
            delete newProgress[fileName]
            return newProgress
        })
    }

    return (
        <div className="w-full space-y-4">
            <motion.div
                className={`relative border-2 border-dashed rounded-xl p-8 transition-all
                    ${isDragging
                        ? 'border-blue-500 bg-blue-50/50 ring-4 ring-blue-100'
                        : 'border-gray-200 hover:border-blue-400 hover:bg-gray-50'
                    }`}
                onDragEnter={handleDrag}
                onDragLeave={handleDrag}
                onDragOver={handleDrag}
                onDrop={handleDrop}
                role="button"
                tabIndex={0}
                onClick={() => fileInputRef.current?.click()}
                aria-label="文件上传区域"
            >
                <input
                    type="file"
                    ref={fileInputRef}
                    className="hidden"
                    multiple
                    onChange={handleFileSelect}
                    accept={allowedTypes.join(',')}
                />

                <div className="flex flex-col items-center justify-center space-y-4">
                    <motion.div
                        animate={{ y: isDragging ? -10 : 0 }}
                        transition={{ type: "spring", stiffness: 300, damping: 20 }}
                    >
                        <CloudArrowUpIcon className="w-16 h-16 text-blue-500/80" />
                    </motion.div>
                    <div className="text-center">
                        <p className="text-gray-500">{placeholder}</p>
                    </div>
                    <p className="text-xs text-gray-400 flex items-center gap-1">
                        <ExclamationCircleIcon className="w-4 h-4" />
                        支持的文件类型: {allowedTypes.join(', ')} · 最大文件大小: {maxSize}MB
                    </p>
                </div>
            </motion.div>

            <AnimatePresence>
                <div className="space-y-3">
                    {files.map(file => (
                        <FileItem
                            key={file.name}
                            file={file}
                            progress={progress[file.name]}
                            onRemove={removeFile}
                        />
                    ))}
                </div>
            </AnimatePresence>
        </div>
    )
}