import { CloudUploadOutlined, PlusOutlined } from '@ant-design/icons'
import { App, Typography, Upload } from 'antd'
import type { UploadFile, UploadProps } from 'antd'
import { useEffect, useRef, useState } from 'react'
import type { ClipboardEvent, DragEvent, FocusEvent, MouseEvent } from 'react'

import { uploadFile } from '@/services/files'
import type { UploadedFile } from '@/types/worker-platform'

type ImageUploadProps = {
  value?: UploadedFile[]
  onChange?: (files: UploadedFile[]) => void
  scene: string
  scope: 'admin' | 'worker' | 'collect'
  maxCount?: number
  showPasteHint?: boolean
  /** 开启后，页面任意位置粘贴剪贴板截图都会上传到本组件（多实例时以最近点击/挂载的为准） */
  captureGlobalPaste?: boolean
}

type GlobalPasteTarget = {
  id: number
  uploadImages: (files: File[]) => void
}

type UploadSource = 'default' | 'drag'

let nextTargetId = 1
const globalPasteTargets: GlobalPasteTarget[] = []
let globalPasteListenerAttached = false

function handleDocumentPaste(event: DocumentEventMap['paste']) {
  const files = Array.from(event.clipboardData?.files || [])
  const images = files.filter((file) => file.type.startsWith('image/'))
  if (images.length === 0) return
  const target = globalPasteTargets[globalPasteTargets.length - 1]
  if (!target || event.defaultPrevented) return
  event.preventDefault()
  target.uploadImages(images)
}

function ensureGlobalPasteListener() {
  if (globalPasteListenerAttached) return
  globalPasteListenerAttached = true
  document.addEventListener('paste', handleDocumentPaste)
}

function registerGlobalPasteTarget(target: GlobalPasteTarget): () => void {
  globalPasteTargets.push(target)
  ensureGlobalPasteListener()
  return () => {
    const index = globalPasteTargets.findIndex((item) => item.id === target.id)
    if (index >= 0) globalPasteTargets.splice(index, 1)
  }
}

export default function ImageUpload({
  value = [],
  onChange,
  scene,
  scope,
  maxCount = 6,
  showPasteHint = false,
  captureGlobalPaste = false,
}: ImageUploadProps) {
  const { message } = App.useApp()
  const [isDragging, setIsDragging] = useState(false)
  const dragCounterRef = useRef(0)
  const targetIdRef = useRef(0)
  const valueRef = useRef(value)
  valueRef.current = value

  const fileList: UploadFile[] = value.map((file) => ({
    uid: file.objectKey || file.url,
    name: file.filename || '图片',
    status: 'done',
    url: file.thumbnailUrl || file.url,
    thumbUrl: file.thumbnailUrl || file.url,
  }))

  async function uploadImages(images: File[], source: UploadSource = 'default') {
    const remaining = maxCount - valueRef.current.length
    if (remaining <= 0) {
      message.warning(`最多上传 ${maxCount} 张图片`)
      return
    }
    const next = [...valueRef.current]
    for (const file of images.slice(0, remaining)) {
      try {
        const uploaded = await uploadFile(file, scene, scope)
        next.push(uploaded)
      } catch (error) {
        message.error(resolveUploadErrorMessage(error, source))
      }
    }
    if (next.length > valueRef.current.length) {
      valueRef.current = next
      onChange?.(next)
    }
  }

  async function uploadFiles(files: File[], source: UploadSource = 'default') {
    const images = files.filter((file) => file.type.startsWith('image/'))
    if (images.length === 0) return
    await uploadImages(images, source)
  }

  function handlePaste(event: ClipboardEvent<HTMLDivElement>) {
    const files = Array.from(event.clipboardData?.files || [])
    if (files.length === 0) return
    event.preventDefault()
    void uploadFiles(files)
  }

  function handleDragEnter(event: DragEvent<HTMLDivElement>) {
    event.preventDefault()
    event.stopPropagation()
    dragCounterRef.current += 1
    if (event.dataTransfer?.types?.includes('Files')) {
      setIsDragging(true)
    }
  }

  function handleDragOver(event: DragEvent<HTMLDivElement>) {
    event.preventDefault()
    event.stopPropagation()
    if (event.dataTransfer) {
      event.dataTransfer.dropEffect = 'copy'
    }
  }

  function handleDragLeave(event: DragEvent<HTMLDivElement>) {
    event.preventDefault()
    event.stopPropagation()
    dragCounterRef.current -= 1
    if (dragCounterRef.current <= 0) {
      dragCounterRef.current = 0
      setIsDragging(false)
    }
  }

  function handleDrop(event: DragEvent<HTMLDivElement>) {
    event.preventDefault()
    event.stopPropagation()
    dragCounterRef.current = 0
    setIsDragging(false)
    const files = Array.from(event.dataTransfer?.files || [])
    if (files.length > 0) {
      void uploadFiles(files, 'drag')
    }
  }

  function activateGlobalPasteTarget() {
    const index = globalPasteTargets.findIndex((item) => item.id === targetIdRef.current)
    if (index < 0) return
    const [target] = globalPasteTargets.splice(index, 1)
    globalPasteTargets.push(target)
  }

  useEffect(() => {
    if (!captureGlobalPaste) return
    const targetId = nextTargetId++
    targetIdRef.current = targetId
    const unregister = registerGlobalPasteTarget({
      id: targetId,
      uploadImages: (files) => void uploadImages(files),
    })
    return unregister
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [captureGlobalPaste])

  function handleWrapperFocus(_event: FocusEvent<HTMLDivElement>) {
    if (captureGlobalPaste) activateGlobalPasteTarget()
  }

  function handleWrapperClick(_event: MouseEvent<HTMLDivElement>) {
    if (captureGlobalPaste) activateGlobalPasteTarget()
  }

  async function uploadFromRequest(
    options: Parameters<NonNullable<UploadProps['customRequest']>>[0],
  ) {
    try {
      const uploaded = await uploadFile(options.file as File, scene, scope)
      // 多文件选择会并发执行 customRequest，必须以 ref 中的最新列表累加，避免后完成的上传覆盖前面的图片。
      const next = [...valueRef.current, uploaded].slice(0, maxCount)
      valueRef.current = next
      onChange?.(next)
      options.onSuccess?.(uploaded)
    } catch (error) {
      message.error(error instanceof Error ? error.message : '上传失败')
      options.onError?.(error instanceof Error ? error : new Error('上传失败'))
    }
  }

  const customRequest: UploadProps['customRequest'] = (options) => {
    void uploadFromRequest(options)
  }

  return (
    <div
      className={`image-upload-droppable ${isDragging ? 'is-dragging' : ''}`}
      onPaste={handlePaste}
      onDropCapture={handleDrop}
      onDragEnterCapture={handleDragEnter}
      onDragOverCapture={handleDragOver}
      onDragLeaveCapture={handleDragLeave}
      onFocusCapture={handleWrapperFocus}
      onClickCapture={handleWrapperClick}
    >
      {isDragging && (
        <div className="image-upload-drag-overlay">
          <CloudUploadOutlined className="image-upload-drag-icon" />
          <span className="image-upload-drag-text">松开鼠标即可上传图片</span>
          <span className="image-upload-drag-subtext">支持单张或批量拖入多张图片</span>
        </div>
      )}
      <Upload
        accept="image/jpeg,image/png,image/webp"
        multiple
        listType="picture-card"
        fileList={fileList}
        maxCount={maxCount}
        customRequest={customRequest}
        onRemove={(file) => {
          const next = valueRef.current.filter((item) => (item.objectKey || item.url) !== file.uid)
          valueRef.current = next
          onChange?.(next)
          return true
        }}
        showUploadList={{ showPreviewIcon: false }}
      >
        {value.length >= maxCount ? null : (
          <button className="image-upload-trigger" type="button">
            <PlusOutlined />
            <span>上传</span>
          </button>
        )}
      </Upload>
      {showPasteHint ? (
        <Typography.Text type="secondary" className="image-upload-hint">
          点击选择图片上传；截图后可直接按 Ctrl+V / ⌘V 粘贴，或拖拽图片到此处
        </Typography.Text>
      ) : null}
    </div>
  )
}

function resolveUploadErrorMessage(error: unknown, source: UploadSource) {
  const errorMessage = error instanceof Error ? error.message : '上传失败'
  if (source === 'drag' && /网络连接错误|Network Error/i.test(errorMessage)) {
    return '拖拽图片读取失败，请复制图片后粘贴，或保存到本地后点击上传'
  }
  return errorMessage
}
