"use client"

import { useCallback, useRef, useState } from "react"
import { toast } from "sonner"

import {
  ALLOWED_MIME_TYPES,
  formatFileSize,
  MAX_FILE_SIZE,
} from "@/shared/lib/file-storage"
import { useTranslations } from "next-intl"

import type { FileTargetType, FileUploadResult } from "../types"

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

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

export interface UseFileUploadReturn {
  tasks: UploadTask[]
  isDragging: boolean
  inputRef: React.RefObject<HTMLInputElement | null>
  setIsDragging: (v: boolean) => void
  handleFiles: (fileList: FileList | null) => void
  removeTask: (task: UploadTask) => void
  acceptAttr: string
  maxFileSize: number
}

/**
 * 文件上传 hook：封装 XHR 上传、进度、状态机与校验。
 *
 * 与 UI 解耦，便于在 `FileUpload`、`AvatarUpload` 等组件中复用，
 * 也可独立测试（无 DOM 依赖的逻辑部分）。
 */
export function useFileUpload({
  targetType,
  targetId,
  multiple = true,
  onUploaded,
}: UseFileUploadOptions): UseFileUploadReturn {
  const t = useTranslations("files.upload")
  const inputRef = useRef<HTMLInputElement | null>(null)
  const [isDragging, setIsDragging] = useState(false)
  const [tasks, setTasks] = useState<UploadTask[]>([])

  const validateFile = useCallback(
    (file: File): string | null => {
      if (file.size === 0) return t("empty")
      if (file.size > MAX_FILE_SIZE) return t("tooLarge", { limit: formatFileSize(MAX_FILE_SIZE) })
      if (!(ALLOWED_MIME_TYPES as readonly string[]).includes(file.type)) {
        return t("invalidType", { type: file.type || "unknown" })
      }
      return null
    },
    [t]
  )

  const uploadOne = useCallback(
    async (file: File): Promise<void> => {
      setTasks((prev) => [
        ...prev,
        { file, progress: 0, status: "uploading" },
      ])

      const validationError = validateFile(file)
      if (validationError) {
        setTasks((prev) =>
          prev.map((tk) =>
            tk.file === file
              ? { ...tk, status: "error", message: validationError, progress: 100 }
              : tk
          )
        )
        toast.error(t("error", { name: file.name, message: 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((tk) =>
                  tk.file === file ? { ...tk, progress: pct } : tk
                )
              )
            }
          }
          xhr.onload = () => {
            try {
              const body = JSON.parse(xhr.responseText) as { success?: boolean; message?: string } & Partial<FileUploadResult>
              if (xhr.status >= 200 && xhr.status < 300 && body.success) {
                resolve({
                  id: body.id ?? "",
                  url: body.url ?? "",
                  filename: body.filename ?? file.name,
                  originalName: body.originalName ?? file.name,
                  size: body.size ?? file.size,
                  mimeType: body.mimeType ?? file.type,
                })
              } else {
                reject(new Error(body.message || "Upload failed"))
              }
            } catch {
              reject(new Error(t("invalidResponse")))
            }
          }
          xhr.onerror = () => reject(new Error(t("networkError")))
          xhr.send(formData)
        })

        setTasks((prev) =>
          prev.map((tk) =>
            tk.file === file
              ? { ...tk, status: "success", progress: 100, result }
              : tk
          )
        )
        onUploaded?.(result)
        toast.success(t("success", { name: file.name }))
      } catch (e) {
        const message = e instanceof Error ? e.message : t("networkError")
        setTasks((prev) =>
          prev.map((tk) =>
            tk.file === file ? { ...tk, status: "error", message } : tk
          )
        )
        toast.error(t("error", { name: file.name, message }))
      }
    },
    [targetType, targetId, onUploaded, t, validateFile]
  )

  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 removeTask = useCallback((task: UploadTask) => {
    setTasks((prev) => prev.filter((tk) => tk !== task))
  }, [])

  return {
    tasks,
    isDragging,
    inputRef,
    setIsDragging,
    handleFiles,
    removeTask,
    acceptAttr: (ALLOWED_MIME_TYPES as readonly string[]).join(","),
    maxFileSize: MAX_FILE_SIZE,
  }
}
