import mime from 'mime'
import { flatten } from 'lodash-es'
import { FileAppearanceTypeEnum } from './types'
import type { FileEntity } from './types'
import { upload } from '@/service/base'
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import type { FileResponse } from '@/types/workflow'
import { TransferMethod } from '@/types/app'

type FileUploadParams = {
  file: File
  onProgressCallback: (progress: number) => void
  onSuccessCallback: (res: { id: string }) => void
  onErrorCallback: () => void
}
type FileUpload = (v: FileUploadParams, isPublic?: boolean, url?: string) => void
export const fileUpload: FileUpload = ({
  file,
  onProgressCallback,
  onSuccessCallback,
  onErrorCallback,
}, isPublic, url) => {
  const formData = new FormData()
  formData.append('file', file)
  const onProgress = (e: ProgressEvent) => {
    if (e.lengthComputable) {
      const percent = Math.floor(e.loaded / e.total * 100)
      onProgressCallback(percent)
    }
  }

  upload({
    xhr: new XMLHttpRequest(),
    data: formData,
    onprogress: onProgress,
  }, isPublic, url)
    .then((res: { id: string }) => {
      onSuccessCallback(res)
    })
    .catch(() => {
      onErrorCallback()
    })
}

export const getFileExtension = (fileName: string, fileMimetype: string, isRemote?: boolean) => {
  if (fileMimetype)
    return mime.getExtension(fileMimetype) || ''

  if (isRemote)
    return ''

  if (fileName) {
    const fileNamePair = fileName.split('.')
    const fileNamePairLength = fileNamePair.length

    if (fileNamePairLength > 1)
      return fileNamePair[fileNamePairLength - 1]
  }

  return ''
}

export const getFileAppearanceType = (fileName: string, fileMimetype: string) => {
  const extension = getFileExtension(fileName, fileMimetype)

  if (extension === 'gif')
    return FileAppearanceTypeEnum.gif

  if (FILE_EXTS.image.includes(extension.toUpperCase()))
    return FileAppearanceTypeEnum.image

  if (FILE_EXTS.video.includes(extension.toUpperCase()))
    return FileAppearanceTypeEnum.video

  if (FILE_EXTS.audio.includes(extension.toUpperCase()))
    return FileAppearanceTypeEnum.audio

  if (extension === 'html')
    return FileAppearanceTypeEnum.code

  if (extension === 'pdf')
    return FileAppearanceTypeEnum.pdf

  if (extension === 'md' || extension === 'markdown')
    return FileAppearanceTypeEnum.markdown

  if (extension === 'xlsx' || extension === 'xls')
    return FileAppearanceTypeEnum.excel

  if (extension === 'docx' || extension === 'doc')
    return FileAppearanceTypeEnum.word

  if (extension === 'pptx' || extension === 'ppt')
    return FileAppearanceTypeEnum.ppt

  if (FILE_EXTS.document.includes(extension.toUpperCase()))
    return FileAppearanceTypeEnum.document

  return FileAppearanceTypeEnum.custom
}

export const getSupportFileType = (fileName: string, fileMimetype: string, isCustom?: boolean) => {
  if (isCustom)
    return SupportUploadFileTypes.custom

  const extension = getFileExtension(fileName, fileMimetype)
  for (const key in FILE_EXTS) {
    if ((FILE_EXTS[key]).includes(extension.toUpperCase()))
      return key
  }

  return ''
}

export const getProcessedFiles = (files: FileEntity[]) => {
  return files.filter(file => file.progress !== -1).map(fileItem => ({
    type: fileItem.supportFileType,
    transfer_method: fileItem.transferMethod,
    url: fileItem.url || '',
    upload_file_id: fileItem.uploadedId || '',
  }))
}

export const getProcessedFilesFromResponse = (files: FileResponse[]) => {
  return files.map((fileItem) => {
    return {
      id: fileItem.related_id,
      name: fileItem.filename,
      size: fileItem.size || 0,
      type: fileItem.mime_type,
      progress: 100,
      transferMethod: fileItem.transfer_method,
      supportFileType: fileItem.type,
      uploadedId: fileItem.related_id,
      url: fileItem.url,
    }
  })
}

export const getFileNameFromUrl = (url: string) => {
  const urlParts = url.split('/')
  return urlParts[urlParts.length - 1] || ''
}

export const getSupportFileExtensionList = (allowFileTypes: string[], allowFileExtensions: string[]) => {
  if (allowFileTypes.includes(SupportUploadFileTypes.custom))
    return allowFileExtensions.map(item => item.toUpperCase())

  return allowFileTypes.map(type => FILE_EXTS[type]).flat()
}

export const isAllowedFileExtension = (fileName: string, fileMimetype: string, allowFileTypes: string[], allowFileExtensions: string[]) => {
  return getSupportFileExtensionList(allowFileTypes, allowFileExtensions).includes(getFileExtension(fileName, fileMimetype).toUpperCase())
}

export const getFilesInLogs = (rawData: any) => {
  const originalFiles = flatten(Object.keys(rawData || {}).map((key) => {
    if (typeof rawData[key] === 'object' || Array.isArray(rawData[key]))
      return rawData[key]
    return undefined
  }).filter(Boolean)).filter(item => item?.model_identity === '__dify__file__')
  return getProcessedFilesFromResponse(originalFiles)
}

export const fileIsUploaded = (file: FileEntity) => {
  if (file.uploadedId)
    return true

  if (file.transferMethod === TransferMethod.remote_url && file.progress === 100)
    return true
}

export const downloadFile = (url: string, filename: string) => {
  const anchor = document.createElement('a')
  anchor.href = url
  anchor.download = filename
  anchor.style.display = 'none'
  anchor.target = '_blank'
  anchor.title = filename
  document.body.appendChild(anchor)
  anchor.click()
  document.body.removeChild(anchor)
}