// @ts-nocheck
import Vue from 'vue'
import axios from '@/common/services/axios-instance'
import Dexie from 'dexie'
import cloneDeep from 'lodash.clonedeep'
import { Modal, Message } from 'ant-design-vue'
import ProgressBar from '@/common/services/progress-bar'
import pickValues from '@/common/services/pick-values'
import sdListPickerVue from '@/common/components/sd-list-picker.vue'
import download from '@/common/services/download'
import SdAttachment from '@/common/components/sd-attachment.vue'
import loginService from '@/login/login-service.js'
import FlowService from '@/webflow/flow-service'
import Resumable from './resumable.js'
import AttachmentService from './attachment-service.js'
import RangeDownloader from './range-downloader.js'
import Tips from './tips.vue'
import './batchButtonStyle.css'

const XmAttachmentCus = cloneDeep(SdAttachment)
const customRequest = XmAttachmentCus.methods.customRequest
const OpenMyFile = XmAttachmentCus.methods.OpenMyFile
const DelFile = SdAttachment.methods.DelFile
const getFileList = SdAttachment.methods.getFileList
const render = XmAttachmentCus.render
const created = XmAttachmentCus.created
const db = new Dexie('sjprojectattach')
db.version(1).stores({
  attachmentSharding: '&id,index,count,filesize,progress, timestap',
  attachmentShardingBlob: '&id,index,timestap,blob,key',
})
const config = {
  fileSize: 50 * 1024 * 1024, // 超过 100M 上传大附件
  maxFileSize: 3 * 1024 * 1024 * 1024, // 最大上传的文件大小3G
  maxBatchDownloadSize: '',
}

XmAttachmentCus.created = function() {
  // 获取groupId
  this.uploading = []
  this.mapR = new Map()
  created.call(this)
}

function cancelFile(item) {
  // 项目扩展事件
  console.log('取消附件：', item)
  const R = this.mapR.get(item.FileName)
  if (R) {
    const fileItem = R.r.files[0]
    fileItem.cancel()
  }

  if (item.downloading) {
    // 取消下载
    Modal.confirm({
      title: `是否取消下载`,
      okType: 'danger',
      onOk: () => {
        item.downloading = 0
        item.downloader.cancel()
        ProgressBar.done()
        this.attachments = [...this.attachments]
      },
    })
  } else {
    this.attachments = this.attachments.filter((_) => _.FileName !== item.FileName)
    if (item.Completed === 1) {
      AttachmentService.largeFileDeleteUpload(item.id).then((res) => {
        Message.success('取消成功')
      })
    } else {
      AttachmentService.largeFileCancelUpload(item.id).then((res) => {
        Message.success('取消成功')
      })
    }
  }
}
XmAttachmentCus.methods.customRequest = function(file) {
  config.groupId = this.groupId
  if (file.file.size > config.maxFileSize) {
    return Message.warn(
      `文件超过最大大小40GB` // maxFilesize传过来的单位是
    )
  }
  if (file.file.size < config.fileSize) {
    return customRequest.call(this, file)
  }

  // 大附件
  file.CatNum = '0' // 排序
  file.Completed = 0 // 正在上传
  file.renamable = 0 // 隐藏重命名
  file.FileName = file.file.name
  file.size = file.file.size
  file.largeFile = 1 // 大附件
  file.deletable = 1 // 显示删除按钮
  file.downloadBatch = 1
  this.attachments.push(file)

  const R = new ResumableUpload(
    (fileId) => {
      file.id = fileId // 对应的fileId
      file.Unid = fileId
    },
    (f) => {
      file.progress = f.progress()
      if (file.progress === 1) {
        file.downloading = 0
        file.pause = 0
      }
      this.attachments = [...this.attachments]
    },
    this.uploading
  )
  this.mapR.set(file.FileName, R)
  R.init().then(() => {
    // 初始化完成 添加文件
    R.r.addFile(file.file)
    console.time('上传时长')
  })

  // 上传完成
  R.exec()
    .then(({ status }) => {
      // 上传成功
      if (status === 200) {
        file.Completed = 1
        console.timeEnd('上传时长')
      }
    })
    .catch((err) => {
      // 上传失败
      console.log(err)
      cancelFile(file)
    })
}
function customOpenMyFile(fileName) {
  const file = this.attachments.find((_) => _.FileName === fileName)
  if (file && (file.downloading || file.uploading)) {
    // 正在下载
    Message.info('正在传输，请勿重复点击')
    return
  }
  file.downloading = 1
  if (!file.Completed) return
  if (!file.largeFile) {
    return OpenMyFile.call(this, fileName)
  }
  const that = this
  let errTryCount = 3
  let name = ''
  let code = ''
  let filename = ''
  let flag = true
  // url: 'api/framework/v1/large-file/download/' + file.id,
  let url = 'api/framework/v1/large-file/download/'
  code = file.groupCode
  filename = file.fileName
  name = file.groupCode + '_' + file.fileName
  if (!file.largeFile) {
    name = file.code + '_' + file.FileName
    code = file.code
    filename = file.FileName
    url = 'api/framework/v1/iam-task-form-process/download-attachment-chunk/'
  }
  function sum(arr) {
    return arr.reduce(function(total, value) {
      return total + value
    }, 0)
  }
  const downloader = new RangeDownloader({
    url: url + file.id,
    Authorization: loginService.getTokens().access_token,
    fileSize: file.size,
    db: db,
    name: name,
    onload(self) {
      const res = self.getResultAsBlob()
      const i = self.index
      const progress = self.downloadedSize - 10 / self.fileSize
      self.progress = Math.max(progress, self.progress || 0)
      file.progress = progress
      ProgressBar.set(progress)
      const ini = setInterval(() => {
        db.attachmentSharding.get(name).then((d) => {
          if (d.filesize === file.size) {
            // 合并
            const len = self.dbcount
            let array = []
            for (var j = 0; j < len + 1; j++) {
              db.attachmentShardingBlob.get(name + j).then((d) => {
                array.push(d)
                const lensize = sum(array.map((i) => i.blob.size))
                if (lensize === file.size && flag) {
                  clearInterval(ini)
                  array = fnpxarray(array)
                  const fileData = array.map((i) => i.blob)
                  const blob = new Blob(fileData)
                  flag = false
                  const ele = document.createElement('a')
                  ele.download = file.FileName
                  ele.href = URL.createObjectURL(blob)
                  document.body.append(ele)
                  ele.click()
                  URL.revokeObjectURL(res)
                  self.cancel()
                  const progress = self.downloadedSize / self.fileSize
                  self.progress = Math.max(progress, self.progress || 0)
                  file.progress = progress
                  ProgressBar.set(progress)
                  ProgressBar.done()
                  db.attachmentShardingBlob
                    .where('key')
                    .equals(name)
                    .delete()
                  db.attachmentSharding
                    .where('id')
                    .equals(name)
                    .delete()
                }
              })
            }
          }
        })
      }, 1000)
    },
    oncancle(self, e) {
      db.attachmentShardingBlob
        .where('key')
        .equals(name)
        .delete()
      db.attachmentSharding
        .where('id')
        .equals(name)
        .delete()
    },
    onprogress(self) {
      const progress = self.downloadedSize / self.fileSize
      self.progress = Math.max(progress, self.progress || 0)

      console.log('progress:', progress, self)
      file.progress = progress
      ProgressBar.set(progress)

      if (progress >= 1) {
        file.downloading = 0
      }
      that.attachments = [...that.attachments]
    },
    onerror(self, e) {
      if (e.message.indexOf('status: 400') > -1 && errTryCount--) {
        loginService.refreshToken().then(() => {
          self.Authorization = loginService.getTokens().access_token
          downloader.start()
        })
      } else {
        downloader.cancel()
        ProgressBar.done()
        Message.error('下载失败，请联系管理员')
      }
    },
  })
  file.downloader = downloader
  downloader.start()
  ProgressBar.start()
}

XmAttachmentCus.methods.OpenMyFile = function(fileName) {
  config.groupId = this.groupId
  const that = this
  customOpenMyFile.call(that, fileName)
  // const mixin = {
  //   methods: {
  //     handleOk() {
  //       customOpenMyFile.call(that, fileName)
  //       this.visible = false
  //     },
  //   },
  // }
  // const mixins = { mixins: [mixin] }
  // const c = { ...Tips, ...mixins }
  // const vm = new Vue(c).$mount()
  // document.body.appendChild(vm.$el)
}
XmAttachmentCus.methods.DelFile = function(file) {
  config.groupId = this.groupId
  if (!file.largeFile) {
    Modal.confirm({
      title: `确认删除附件《${file.FileName}》?`,
      okType: 'danger',
      onOk: () => {
        axios
          .delete(`api/framework/v1/attachment-extend/attachments-delete/${this.groupId}`, {
            params: {
              attachmentId: file.Unid,
              lockId: this.lockId,
              ...this.infoProperties,
            },
          })
          .then((_) => {
            if (typeof this.selectedKeys !== 'undefined') {
              // 如果是可选择附件，并且被选中的附件删除，则更新选中的key
              const keys = [...this.selectedKeys]
              if (keys.includes(file.Unid)) {
                keys.splice(keys.indexOf(file.Unid), 1)
              }
              if (typeof this.selectedKeys === 'undefined') {
                this.selectedKeysLocal = keys
              }
              this.$emit('update:selectedKeys', keys)
            }
            this.attachments = this.attachments.filter((item) => item.id !== file.id)
          })
          .catch((e) => {
            Message.info(
              e.response?.data?.code === 21001008
                ? `其他人正在处理该流程，不能执行当前操作`
                : '删除失败'
            )
          })
      },
    })
    return
  }

  Modal.confirm({
    title: `确认删除附件《${file.FileName}》?`,
    okType: 'danger',
    onOk: () => {
      AttachmentService.largeFileDeleteUpload(file.id)
        .then(() => {
          this.attachments = this.attachments.filter((item) => item.id !== file.id)
        })
        .catch(() => {
          Message.error('删除失败')
        })
    },
  })
}

XmAttachmentCus.methods.getFileList = function(
  noTriggerCheck = false,
  updateList = true,
  fromField = false
) {
  config.groupId = this.groupId
  console.log(this.groupId)
  console.log(!fromField || !updateList)
  if (!fromField || !updateList)
    return getFileList.call(this, noTriggerCheck, updateList, fromField)
  return getFileList.call(this, noTriggerCheck, updateList, fromField).then(() => {
    // 设置大附件
    config.groupId = this.groupId
    console.log('---大附件')
    AttachmentService.getlargeFiles(this.groupId).then((res) => {
      console.log(res)
      // 文件大小阈值
      config.fileSize = (res?.fileThreshold || 50) * 1024 * 1024
      config.maxFileSize = res?.maxFileSize * 1024 * 1024
      config.maxBatchDownloadSize = res?.maxBatchDownloadSize

      const largeAttachments = res.largeAttachments.map((item) => {
        return {
          ...item,
          Unid: item.id,
          FileName: item.fileName,
          Completed: 1,
          size: item.fileSize || item.size,
          CatNum: '0',
          deletable: !this.readOnly,
          renamable: 0,
          largeFile: 1,
        }
      })

      const completeds = []
      largeAttachments.forEach((item) => {
        // 未上传完成 刷新再次选择那个文件 则直接续传
        if (item.status === 0) {
          this.uploading.push(item)
        } else {
          completeds.push(item)
        }
      })

      this.attachments = this.attachments.concat(completeds)
    })
  })
}
// 给附件添加下载按钮
XmAttachmentCus.render = function(h) {
  this.$scopedSlots['file-progress'] = ({ item }) => {
    if (!item.Completed || item.downloading) {
      return (
        <a-progress
          percent={Math.max((item.progress || 0) * 100, 0).toFixed(2) * 1}
          showInfo={false}
          size='small'
          status='active'
        />
      )
    }
  }

  this.$scopedSlots['file-icon'] = ({ item }) => {
    if (!item.largeFile) return

    if (item.pause) {
      return (
        <span>
          <a-icon
            theme='filled'
            type={this.getIcon(item.FileName)}
            class={this.$style[this.getIcon(item.FileName)]}
          />
          (暂停)
        </span>
      )
    }

    if (item.downloading) {
      return (
        <span>
          <a-icon
            theme='filled'
            type={this.getIcon(item.FileName)}
            class={this.$style[this.getIcon(item.FileName)]}
          />
          (下载中: {Math.max((item.progress || 0) * 100, 0).toFixed(2)}%)
        </span>
      )
    }

    if (!item.Completed) {
      if (item.progress < 1) {
        return (
          <span>
            <a-icon
              theme='filled'
              type={this.getIcon(item.FileName)}
              class={this.$style[this.getIcon(item.FileName)]}
            />
            ({this.uploading.findIndex((_) => _.id === item.id) > -1 ? '续传中' : '上传中'}:
            {Math.max((item.progress || 0) * 100, 0).toFixed(2)}%)
          </span>
        )
      } else {
        return (
          <span>
            <a-icon
              theme='filled'
              type={this.getIcon(item.FileName)}
              class={this.$style[this.getIcon(item.FileName)]}
            />
            完成中...
          </span>
        )
      }
    }

    return (
      <a-icon
        theme='filled'
        type={this.getIcon(item.FileName)}
        class={this.$style[this.getIcon(item.FileName)]}
      />
    )
  }
  this.$scopedSlots.actions = ({ item }) => {
    if (!item.largeFile) return
    const result = []
    const pause = (
      <a-tooltip placement='top' title='暂停'>
        <a-button
          type='link'
          icon='pause'
          on={{
            click: () => {
              // 项目扩展事件
              console.log('暂停附件：', item)
              const R = this.mapR.get(item.FileName)
              const fileItem = R.r.files[0]
              fileItem.pause()
              item.pause = 1
              this.attachments = [...this.attachments]
            },
          }}
        />
      </a-tooltip>
    )
    const upload = (
      <a-tooltip placement='top' title='继续'>
        <a-button
          type='link'
          icon='sd-right'
          on={{
            click: () => {
              // 项目扩展事件
              console.log('继续附件：', item)
              const R = this.mapR.get(item.FileName)
              const fileItem = R.r.files[0]
              fileItem.pause()
              R.r.pause()
              R.r.upload()
              R.r.upload()
              item.pause = 0
              this.attachments = [...this.attachments]
            },
          }}
        />
      </a-tooltip>
    )
    const cancel = (
      <a-tooltip placement='top' title='取消'>
        <a-button
          type='link'
          icon='close'
          on={{
            click: () => {
              cancelFile.call(this, item)
              item.pause = 0
              this.attachments = [...this.attachments]
            },
          }}
        />
      </a-tooltip>
    )
    const downpause = (
      <a-tooltip placement='top' title='暂停'>
        <a-button
          type='link'
          icon='pause'
          on={{
            click: () => {
              // 项目扩展事件
              console.log(this.mapR)
              console.log('暂停附件：', item)
              item.downloader.pause()
              ProgressBar.done()
              // const R = this.mapR.get(item.FileName)
              // const fileItem = R.r.files[0]
              // fileItem.pause()
              item.pause = 1
              this.attachments = [...this.attachments]
            },
          }}
        />
      </a-tooltip>
    )
    const downloadjx = (
      <a-tooltip placement='top' title='继续'>
        <a-button
          type='link'
          icon='sd-right'
          on={{
            click: () => {
              // 项目扩展事件
              console.log('继续附件：', item)
              item.downloader.loaded = false
              item.downloader.start()
              item.pause = 0
              this.attachments = [...this.attachments]
            },
          }}
        />
      </a-tooltip>
    )
    const download = (
      <a-tooltip placement='top' title=''>
        {/* <a-button
          type='link'
          icon='sd-download'
          on={{
            click: () => {
              // 项目扩展事件
              console.log('下载附件：', item)
              customOpenMyFile.call(this, item.fileName)
              item.pause = 0
              this.attachments = [...this.attachments]
            },
          }}
        /> */}
      </a-tooltip>
    )
    if (item.downloading) {
      if (item.pause) {
        result.push(downloadjx)
      } else {
        result.push(downpause)
      }
      result.push(cancel)
    }
    if (!item.Completed) {
      if (item.pause) {
        result.push(upload)
      } else {
        result.push(pause)
      }
      result.push(cancel)
    } else {
      if (!item.downloading) {
        result.push(download)
      }
    }

    return result
  }
  const vNode = render.call(this, h)
  return vNode
}

XmAttachmentCus.methods.plsave = function() {
  config.groupId = this.groupId
  // 大附件兼容弹窗的多选列表name
  this.attachmentViewList.forEach((item) => {
    if (!item?.name) {
      item.name = item.fileName || item.FileName
    }
  })
  let p
  if (this.attachmentViewList.length <= 2) {
    p = Promise.resolve(this.attachmentViewList)
  } else {
    // 弹框下载
    const titles = []
    const filterFileSize = (list) => {
      const size = list.reduce((pre, cur) => pre + +(cur.size || cur.fileSize), 0)
      titles[0] = size ? Vue.filter('sdBytesFormat')(size) : ''
    }
    filterFileSize(this.attachmentViewList)

    p = pickValues(sdListPickerVue, {
      optionValue: 'Unid',
      singleColumn: true,
      loadListData: () => this.attachmentViewList,
      value: this.attachmentViewList,
      titles,
      render(item) {
        return `${item.name || item.fileName || item.FileName} (${Vue.filter('sdBytesFormat')(
          item.size || item.fileSize
        )})`
      },
      updateSelectedKeys: (keys) => {
        const list = this.attachmentViewList.filter(
          (item) => keys.includes(item.id) || keys.includes(item.code)
        )
        filterFileSize(list)
      },
    })
  }
  p.then((attachs) => {
    if (!attachs) return
    if (attachs.length === 0) return Message.error('请选择需要下载的文件')
    const filesSize = attachs.reduce((pre, cur) => pre + +cur.size, 0)
    // 单位M
    if (filesSize / 1024 / 1024 > config.maxFileSize) {
      Message.error('下载最大支持3G，请修改下载文件')
      return
    }
    const fileCodes = attachs.filter((item) => !item.largeFile)
    const largeFiles = attachs.filter((item) => item.largeFile)
    const fileName = attachs.map((item) => item.fileName || item.FileName).join(',')

    const mixin = {
      methods: {
        handleOk() {
          // 下载
          download(
            `api/framework/v1/large-file/batch-download?groupId=${
              config.groupId
            }&fileCodes=${fileCodes
              .map((item) => item.code)
              .join(',')}&largeFileIds=${largeFiles.map((item) => item.id).join(',')}`,
            `${fileName}等${attachs.length}个附件.zip`
          )
          this.visible = false
        },
      },
    }
    const mixins = { mixins: [mixin] }
    const c = { ...Tips, ...mixins }
    const vm = new Vue(c).$mount()
    document.body.appendChild(vm.$el)
  })
}

function fnpxarray(array) {
  for (let i = 0; i < array.length - 1; i++) {
    let minIndex = i // 用于存储最小值的下标，排序刚开始时，假设最小值的下标就是选中的基准数据下标
    for (let j = i + 1; j < array.length; j++) {
      if (array[j].index < array[minIndex].index) {
        minIndex = j
      }
    }
    ;[array[i], array[minIndex]] = [array[minIndex], array[i]]
  }
  return array
}

class ResumableUpload {
  r = null
  attachments = []

  resolve = null
  exec = null

  constructor(callback, uploadFile, uploading) {
    this.callback = callback
    this.uploadFile = uploadFile
    this.uploading = uploading
  }

  // 初始化
  init() {
    // 初始化配置
    return this.getUploadConfig().then((config) => {
      // 初始化插件
      this.r = new Resumable(config)

      // 初始化事件
      this.bindEvent()

      return this.exec
    })
  }

  exec = () => {
    return new Promise((resolve) => {
      this.resolve = resolve
    })
  }

  // 获取配置
  getUploadConfig() {
    const fileType =
      '.doc,.docx,.ofd,.pdf,.jpeg,.jpg,.png,.bmp,.xls,.xlsx,.gif,.ppt,.pptx,.zip,.rar,.txt,.log,.mht,.wps,.gz,.et,.jfif,.AVI,.mov,.rmvb,.rm,.FLV,.mp4,.3GP'

    return new Promise((resolve) => {
      resolve({
        target: () => 'api/framework/v1/large-file/upload',
        headers: () => ({
          Authorization: loginService.getTokens().access_token,
        }),
        chunkSize: 64 * 1024 * 1024, // 分片大小
        forceChunkSize: true, // 强制所有块小于或等于 chunkSize, 否则，最后一个块将大于或等于chunkSize
        maxChunkRetries: 20, // 一个chunk在上传失败前的最大重试次数
        permanentErrors: [404, 409, 415, 500, 501],
        chunkRetryInterval: 1000,
        // testChunks: false,
        testTarget: (params) => {
          let fileId = params.find((item) => item.includes('fileId'))

          if (!fileId) {
            const resumableFilename = params.find((item) => item.includes('resumableFilename'))
            for (let i = 0; i < this.attachments.length; i++) {
              const item = this.attachments[i]
              if (item.fileName === resumableFilename.split('=')[1]) {
                fileId = item.fileId
              }
            }
          }
          const chunkNumber = params.find((item) => item.includes('resumableChunkNumber'))
          const str = `api/framework/v1/large-file/check-chunk-upload/${fileId.split('=')[1]}/${
            chunkNumber.split('=')[1]
          }`
          return str
        },
        simultaneousUploads: 3,
        fileType: fileType.replaceAll('.', '').split(','), // 上传格式限制
        query: (ResumableFile, ResumableChunk) => {
          const file = this.attachments.find((item) => item.fileName === ResumableFile.fileName)
          return {
            fileId: file.id,
          }
        },
      })
    })
  }

  // 绑定事件
  bindEvent() {
    let errTryCount = 3
    // 添加文件
    this.r.on('fileAdded', this.fileAdded)
    // 重试上传
    this.r.on('fileRetry', () => {
      if (errTryCount--) {
        loginService.refreshToken()
      } else {
        Message.error('上传失败，请联系管理员')
      }
    })
    // 完成
    this.r.on('fileSuccess', this.fileSuccess)
    // 失败
    this.r.on('fileError', this.fileError)
    // 进度条变化
    this.r.on('fileProgress', this.fileProgress)
  }

  // 进度条变化
  fileProgress = (file) => {
    console.log('file:' + (file.progress() * 100).toFixed(2) + '%', Date.now())
    this.uploadFile(file)
  }

  // 上传
  fileAdded = (file, arraySkipped) => {
    // status 0 = 等待上传 1 = 正在上传 2 = 上传完毕

    this.attachments = [
      {
        ...file,
        id: null,
        retryNum: 100,
      },
    ]

    const index = this.uploading.findIndex(
      (_) => _.fileName === file.fileName && _.fileSize === file.size
    )

    // 之前没传完 直接续传
    if (this.uploading[index]) {
      this.attachments[0].id = this.uploading[index].id
      if (this.uploading?.[index]) {
        this.callback(this.uploading[index].id)
      }
      // this.uploading.splice(index, 1)
      this.r.upload()
    } else {
      // 新附件
      this.attachments.forEach((file, index) => {
        return AttachmentService.largeFileInfo({
          groupCode: config.groupId,
          fileName: file.fileName,
          fileSize: file.size,
          sortIndex: index,
        }).then((res) => {
          file.id = res.id
          this.callback(file.id)
          this.r.upload()
        })
      })
    }
  }

  // 成功
  fileSuccess = () => {
    const item = this.attachments[0]
    AttachmentService.largeFileUploadComplete(item.id)
      .then(() => {
        this.resolve({ status: 200, fileId: item.id })
      })
      .catch((err) => {
        this.resolve({ status: -1, err })
      })
  }

  // 失败
  fileError = (file) => {
    const item = this.attachments[0]

    if (item.retryNum > 0) {
      item.retryNum--
      file.bootstrap()
      setTimeout(() => {
        file.pause()
        file.upload()
      }, 2000)
    } else {
      item.pause()
    }
  }
}

export default XmAttachmentCus
