import React, { useCallback, useRef, useState } from 'react'
import styles from './index.module.scss'
import { FileImgListType } from '@/types'
import { API_upFile } from '@/store/action/layout'
import { MessageFu } from '@/utils/message'
import { fileDomInitialFu } from '@/utils/domShow'
import { forwardRef, useImperativeHandle } from 'react'
import { Button, Popconfirm } from 'antd'
import {
  UploadOutlined,
  CloseOutlined,
  DownloadOutlined,
  EyeOutlined,
  MenuOutlined
} from '@ant-design/icons'
import classNames from 'classnames'
import { baseURL } from '@/utils/http'
import { authFilesLookFu } from './data'
import { DndContext, DragEndEvent, DragOverlay, DragStartEvent, closestCenter } from '@dnd-kit/core'
import {
  SortableContext,
  useSortable,
  verticalListSortingStrategy,
  arrayMove
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'

type Props = {
  isLook: boolean // 是否是查看
  ref: any // 当前自己的ref，给父组件调用
  fileCheck: boolean
  dirCode: string // 文件的code码
  myUrl: string
  fromData?: any
  accept?: string
  // result:成果 | list:清单
  type?: string
  tips?: string
  // 文件大小
  size?: number
  // 最大文件数量
  maxCount?: number
}

// 可排序的文件项组件
interface SortableFileItemProps {
  file: FileImgListType
  onDelete: (id: number) => void
  isLook: boolean
  index: number
}

const SortableFileItem = ({ file, onDelete, isLook, index }: SortableFileItemProps) => {
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
    id: file.id
  })

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.5 : 1
  }

  return (
    <div
      ref={setNodeRef}
      style={style}
      className={classNames(styles.Z3filesRow, { [styles.dragging]: isDragging })}
    >
      {!isLook && (
        <div className={styles.dragHandle} {...attributes} {...listeners}>
          <MenuOutlined rev={undefined} />
        </div>
      )}
      <div className={styles.Z3files1} title={file.fileName}>
        {file.fileName}
      </div>
      <div className={styles.Z3files2}>
        {authFilesLookFu(file.fileName, '') ? (
          <>
            <EyeOutlined
              rev={undefined}
              title='查看'
              onClick={() => authFilesLookFu(file.fileName, file.filePath)}
            />
            &emsp;
          </>
        ) : null}
        <a
          title='下载'
          href={baseURL + file.filePath}
          download={file.fileName}
          target='_blank'
          rel='noreferrer'
        >
          <DownloadOutlined rev={undefined} />
        </a>
        &emsp;
        {!isLook && (
          <Popconfirm
            title='删除后无法恢复，是否删除?'
            okText='删除'
            cancelText='取消'
            onConfirm={() => onDelete(file.id)}
            okButtonProps={{ loading: false }}
          >
            <CloseOutlined rev={undefined} title='删除' />
          </Popconfirm>
        )}
      </div>
    </div>
  )
}

function Z3upFiles(
  {
    isLook,
    type = 'doc',
    fileCheck,
    dirCode,
    myUrl,
    fromData,
    accept = '.zip',
    tips = '单个附件不得超过500M',
    size
  }: Props,
  ref: any
) {
  const [fileList, setFileList] = useState<FileImgListType[]>([])
  const [activeId, setActiveId] = useState<number | null>(null)
  const myInput = useRef<HTMLInputElement>(null)

  // 给父组件调用 回显
  const showList = useCallback((list: FileImgListType[]) => {
    setFileList(list)
  }, [])

  // 上传多个文件
  const handeUpPhoto = useCallback(
    async (e: React.ChangeEvent<HTMLInputElement>) => {
      if (!e.target.files || e.target.files.length === 0) return

      const files = Array.from(e.target.files)

      const uploadedFiles: FileImgListType[] = []
      let hasError = false

      // 逐个上传文件
      for (const file of files) {
        // 校验大小
        if (size && file.size > size * 1024 * 1024) {
          MessageFu.warning(`文件"${file.name}"超过${size}M限制!`)
          hasError = true
          continue
        }

        // 创建FormData对象
        const fd = new FormData()
        fd.append('type', type)
        fd.append('dirCode', dirCode)
        fd.append('isCompress', 'true')
        fd.append('isDb', 'true')
        fd.append('file', file)

        if (fromData) {
          for (const k in fromData) {
            if (fromData[k]) fd.append(k, fromData[k])
          }
        }

        try {
          const res = await API_upFile(fd, myUrl)
          if (res.code === 0) {
            uploadedFiles.push(res.data)
          } else {
            MessageFu.error(`文件"${file.name}"上传失败`)
            hasError = true
          }
        } catch (error) {
          MessageFu.error(`文件"${file.name}"上传失败`)
          hasError = true
        }
      }

      // 清空input值
      e.target.value = ''

      if (uploadedFiles.length > 0) {
        setFileList(prev => [...prev, ...uploadedFiles])
        MessageFu.success(`成功上传${uploadedFiles.length}个文件`)
      }

      if (!hasError && uploadedFiles.length === files.length) {
        fileDomInitialFu()
      }
    },
    [dirCode, fromData, myUrl, size, type]
  )

  // 拖拽开始
  const handleDragStart = useCallback((event: DragStartEvent) => {
    setActiveId(Number(event.active.id))
  }, [])

  // 拖拽结束
  const handleDragEnd = useCallback((event: DragEndEvent) => {
    const { active, over } = event
    setActiveId(null)

    if (over && active.id !== over.id) {
      setFileList(items => {
        const oldIndex = items.findIndex(item => item.id === active.id)
        const newIndex = items.findIndex(item => item.id === over.id)

        return arrayMove(items, oldIndex, newIndex)
      })
    }
  }, [])

  // 列表删除某一个文件
  const delImgListFu = useCallback((id: number) => {
    setFileList(prev => prev.filter(v => v.id !== id))
  }, [])

  // 让父组件调用，拿到附件信息
  const filesRes = useCallback(() => {
    return fileList || []
  }, [fileList])

  // 获取当前拖拽的文件
  const activeFile = activeId ? fileList.find(file => file.id === activeId) : null

  // 可以让父组件调用子组件的方法
  useImperativeHandle(ref, () => ({
    filesRes,
    showList
  }))

  return (
    <div className={styles.Z3upFiles}>
      <input
        id='upInput'
        type='file'
        ref={myInput}
        onChange={handeUpPhoto}
        multiple // 支持多选
      />
      <div className={styles.Z3Btn}>
        {!isLook && (
          <Button
            onClick={() => myInput.current?.click()}
            icon={<UploadOutlined rev={undefined} />}
          >
            上传文件
          </Button>
        )}

        <div className={styles.Z3files} style={{ marginTop: isLook ? '0' : '16px' }}>
          <DndContext
            collisionDetection={closestCenter}
            onDragStart={handleDragStart}
            onDragEnd={handleDragEnd}
          >
            <SortableContext items={fileList.map(f => f.id)} strategy={verticalListSortingStrategy}>
              {fileList.map((file, index) => (
                <SortableFileItem
                  key={file.id}
                  file={file}
                  onDelete={delImgListFu}
                  isLook={isLook}
                  index={index}
                />
              ))}
            </SortableContext>
            <DragOverlay>
              {activeFile ? (
                <div className={classNames(styles.Z3filesRow, styles.dragOverlay)}>
                  <div className={styles.dragHandle}>
                    <MenuOutlined rev={undefined} />
                  </div>
                  <div className={styles.Z3files1} title={activeFile.fileName}>
                    {activeFile.fileName}
                  </div>
                  <div className={styles.Z3files2}>{/* 拖拽时隐藏操作按钮 */}</div>
                </div>
              ) : null}
            </DragOverlay>
          </DndContext>
        </div>

        <div className={styles.fileTit} hidden={isLook}>
          {tips}；支持按住Ctrl键选择多个文件；拖动附件左侧图标可调整顺序
          <div
            className={classNames(
              styles.noUpThumb,
              fileList.length <= 0 && fileCheck ? styles.noUpThumbAc : ''
            )}
          >
            请上传文件!
          </div>
        </div>
      </div>
      {isLook && fileList.length <= 0 ? (
        <div style={{ height: 32, lineHeight: '32px' }}>（空）</div>
      ) : null}
    </div>
  )
}

export default forwardRef(Z3upFiles)
