"use client"

import { useCallback, useRef, useState } from "react"
import { UploadCloud, X } from "lucide-react"
import { toast } from "sonner"

import { Button } from "@/shared/components/ui/button"
import { Progress } from "@/shared/components/ui/progress"
import { cn } from "@/shared/lib/utils"
import {
  ALLOWED_MIME_TYPES,
  formatFileSize,
  MAX_FILE_SIZE,
} from "@/shared/lib/file-storage"
import { FileIcon } from "./file-icon"
import type { FileTargetType, FileUploadResult } from "../types"

interface FileUploadProps {
  targetType?: FileTargetType
  targetId?: string
  onUploaded?: (result: FileUploadResult) => void
  multiple?: boolean
  className?: string
}

interface UploadTask {
  file: File
  progress: number
  status: "uploading" | "success" | "error"
  message?: string
  result?: FileUploadResult
}

const ACCEPT_ATTR = (ALLOWED_MIME_TYPES as readonly string[]).join(",")

export function FileUpload({
  targetType,
  targetId,
  onUploaded,
  multiple = true,
  className,
}: FileUploadProps) {
  const inputRef = useRef<HTMLInputElement>(null)
  const [isDragging, setIsDragging] = useState(false)
  const [tasks, setTasks] = useState<UploadTask[]>([])

  const validateFile = (file: File): string | null => {
    if (file.size === 0) return "File is empty"
    if (file.size > MAX_FILE_SIZE) return "File size exceeds 10MB limit"
    if (!(ALLOWED_MIME_TYPES as readonly string[]).includes(file.type)) {
      return `File type ${file.type || "unknown"} is not allowed`
    }
    return null
  }

  const uploadOne = useCallback(
    async (file: File): Promise<void> => {
      const taskId = `${file.name}-${file.size}-${Date.now()}`
      setTasks((prev) => [
        ...prev,
        { file, progress: 0, status: "uploading" },
      ])

      const validationError = validateFile(file)
      if (validationError) {
        setTasks((prev) =>
          prev.map((t) =>
            t.file === file
              ? { ...t, status: "error", message: validationError, progress: 100 }
              : t
          )
        )
        toast.error(`${file.name}: ${validationError}`)
        return
      }

      try {
        const formData = new FormData()
        formData.append("file", file)
        if (targetType) formData.append("targetType", targetType)
        if (targetId) formData.append("targetId", targetId)

        const xhr = new XMLHttpRequest()
        const result = await new Promise<FileUploadResult>((resolve, reject) => {
          xhr.open("POST", "/api/upload")
          xhr.upload.onprogress = (e) => {
            if (e.lengthComputable) {
              const pct = Math.round((e.loaded / e.total) * 100)
              setTasks((prev) =>
                prev.map((t) =>
                  t.file === file ? { ...t, progress: pct } : t
                )
              )
            }
          }
          xhr.onload = () => {
            try {
              const body = JSON.parse(xhr.responseText)
              if (xhr.status >= 200 && xhr.status < 300 && body.success) {
                resolve(body as FileUploadResult)
              } else {
                reject(new Error(body.message || "Upload failed"))
              }
            } catch {
              reject(new Error("Invalid response"))
            }
          }
          xhr.onerror = () => reject(new Error("Network error"))
          xhr.send(formData)
        })

        setTasks((prev) =>
          prev.map((t) =>
            t.file === file
              ? { ...t, status: "success", progress: 100, result }
              : t
          )
        )
        onUploaded?.(result)
        toast.success(`${file.name} uploaded`)
        void taskId
      } catch (e) {
        const message = e instanceof Error ? e.message : "Upload failed"
        setTasks((prev) =>
          prev.map((t) =>
            t.file === file ? { ...t, status: "error", message } : t
          )
        )
        toast.error(`${file.name}: ${message}`)
      }
    },
    [targetType, targetId, onUploaded]
  )

  const handleFiles = useCallback(
    (fileList: FileList | null) => {
      if (!fileList || fileList.length === 0) return
      const files = Array.from(fileList)
      if (!multiple) {
        void uploadOne(files[0])
      } else {
        files.forEach((f) => void uploadOne(f))
      }
    },
    [uploadOne, multiple]
  )

  const handleDrop = useCallback(
    (e: React.DragEvent<HTMLDivElement>) => {
      e.preventDefault()
      setIsDragging(false)
      handleFiles(e.dataTransfer.files)
    },
    [handleFiles]
  )

  const removeTask = (task: UploadTask) => {
    setTasks((prev) => prev.filter((t) => t !== task))
  }

  return (
    <div className={cn("space-y-4", className)}>
      <div
        role="button"
        tabIndex={0}
        onClick={() => inputRef.current?.click()}
        onKeyDown={(e) => {
          if (e.key === "Enter" || e.key === " ") {
            e.preventDefault()
            inputRef.current?.click()
          }
        }}
        onDragOver={(e) => {
          e.preventDefault()
          setIsDragging(true)
        }}
        onDragLeave={() => setIsDragging(false)}
        onDrop={handleDrop}
        className={cn(
          "flex cursor-pointer flex-col items-center justify-center rounded-lg border-2 border-dashed p-8 text-center transition-colors",
          isDragging
            ? "border-primary bg-primary/5"
            : "border-input hover:border-primary/50 hover:bg-accent/50"
        )}
      >
        <UploadCloud className="h-10 w-10 text-muted-foreground" />
        <p className="mt-2 text-sm font-medium">
          Click to upload or drag and drop
        </p>
        <p className="mt-1 text-xs text-muted-foreground">
          Images, PDF, Word, Excel, PPT, Text, ZIP / RAR · up to {formatFileSize(MAX_FILE_SIZE)}
        </p>
        <input
          ref={inputRef}
          type="file"
          className="hidden"
          accept={ACCEPT_ATTR}
          multiple={multiple}
          onChange={(e) => {
            handleFiles(e.target.files)
            e.target.value = ""
          }}
        />
      </div>

      {tasks.length > 0 ? (
        <ul className="space-y-2">
          {tasks.map((task, idx) => (
            <li
              key={`${task.file.name}-${idx}`}
              className="flex items-center gap-3 rounded-md border p-3"
            >
              <FileIcon mimeType={task.file.type} />
              <div className="min-w-0 flex-1 space-y-1">
                <div className="flex items-center justify-between gap-2">
                  <span className="truncate text-sm font-medium">
                    {task.file.name}
                  </span>
                  <span className="shrink-0 text-xs text-muted-foreground">
                    {formatFileSize(task.file.size)}
                  </span>
                </div>
                {task.status === "uploading" ? (
                  <Progress value={task.progress} className="h-1.5" />
                ) : null}
                {task.status === "error" ? (
                  <p className="text-xs text-destructive">{task.message}</p>
                ) : null}
                {task.status === "success" ? (
                  <p className="text-xs text-green-600">Uploaded</p>
                ) : null}
              </div>
              <Button
                type="button"
                variant="ghost"
                size="icon"
                className="h-7 w-7"
                onClick={() => removeTask(task)}
                aria-label="Remove"
              >
                <X className="h-4 w-4" />
              </Button>
            </li>
          ))}
        </ul>
      ) : null}
    </div>
  )
}
