const {
  getWxApi,
  isCancelError
} = require('./platform-utils')

function formatBytes(byteLength) {
  const length = Number(byteLength) || 0
  if (length >= 1024 && length % 1024 === 0) return `${length / 1024} KB`
  if (length >= 1024) return `${(length / 1024).toFixed(2)} KB`

  return `${length} bytes`
}

function formatExportStamp(date = new Date()) {
  const pad = (value, length = 2) => String(value).padStart(length, '0')

  return [
    date.getFullYear(),
    pad(date.getMonth() + 1),
    pad(date.getDate()),
    '-',
    pad(date.getHours()),
    pad(date.getMinutes()),
    pad(date.getSeconds())
  ].join('')
}

function normalizeExtensions(extensions = []) {
  return extensions
    .map((extension) => String(extension || '').trim().replace(/^\./, '').toLowerCase())
    .filter(Boolean)
}

function getFileName(file, fallback = '未命名文件') {
  return String(file && file.name ? file.name : fallback)
}

function getFilePath(file) {
  return file && (file.path || file.tempFilePath) ? (file.path || file.tempFilePath) : ''
}

function getFirstSelectedFile(result, fallbackName = '未命名文件') {
  const file = result && Array.isArray(result.tempFiles) ? result.tempFiles[0] : null
  if (!file) throw new Error('没有选择文件')

  const filePath = getFilePath(file)
  if (!filePath) throw new Error('无法读取所选文件路径')

  return {
    file,
    name: getFileName(file, fallbackName),
    path: filePath
  }
}

function assertFileExtension(fileInfo, extensions = [], message = '文件格式不符') {
  const normalizedExtensions = normalizeExtensions(extensions)
  if (!normalizedExtensions.length) return

  const nameText = `${fileInfo && fileInfo.name ? fileInfo.name : ''} ${fileInfo && fileInfo.path ? fileInfo.path : ''}`
  const matched = normalizedExtensions.some((extension) => (
    new RegExp(`\\.${extension}$`, 'i').test(nameText)
  ))

  if (!matched) throw new Error(message)
}

function chooseMessageFile(options = {}) {
  const wxApi = getWxApi()
  const extensions = normalizeExtensions(options.extensions || options.extension || [])

  return new Promise((resolve, reject) => {
    if (typeof wxApi.chooseMessageFile !== 'function') {
      reject(new Error('当前微信版本不支持从聊天记录选择文件'))
      return
    }

    const chooseOptions = {
      count: options.count || 1,
      type: options.type || 'file',
      success: resolve,
      fail: reject
    }

    if (extensions.length) chooseOptions.extension = extensions
    wxApi.chooseMessageFile(chooseOptions)
  })
}

function chooseLocalFile(options = {}) {
  const wxApi = getWxApi()
  const extensions = normalizeExtensions(options.extensions || options.extension || [])

  return new Promise((resolve, reject) => {
    if (typeof wxApi.chooseFile !== 'function') {
      reject(new Error(options.unsupportedMessage || '当前微信版本不支持打开本地文件，请从聊天记录选择'))
      return
    }

    const chooseOptions = {
      count: options.count || 1,
      type: options.type || 'file',
      success: resolve,
      fail: reject
    }

    if (extensions.length) chooseOptions.extension = extensions
    wxApi.chooseFile(chooseOptions)
  })
}

function chooseFile(source = 'message', options = {}) {
  return source === 'local'
    ? chooseLocalFile(options)
    : chooseMessageFile(options)
}

function readFile(filePath, options = {}) {
  const wxApi = getWxApi()

  return new Promise((resolve, reject) => {
    if (typeof wxApi.getFileSystemManager !== 'function') {
      reject(new Error('当前微信版本不支持读取文件'))
      return
    }

    const fs = wxApi.getFileSystemManager()
    const readOptions = {
      filePath,
      success: (res) => resolve(res.data),
      fail: reject
    }

    if (options.encoding) readOptions.encoding = options.encoding
    fs.readFile(readOptions)
  })
}

function toUint8Array(data) {
  if (data instanceof Uint8Array) return data
  if (data instanceof ArrayBuffer) return new Uint8Array(data)
  if (ArrayBuffer.isView(data)) {
    return new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
  }

  return new Uint8Array(data || new ArrayBuffer(0))
}

async function loadSelectedFile(source = 'message', options = {}) {
  const result = await chooseFile(source, options)
  const fileInfo = getFirstSelectedFile(result, options.fallbackName || '未命名文件')

  assertFileExtension(fileInfo, options.extensions || options.extension || [], options.extensionMessage || '文件格式不符')

  const data = await readFile(fileInfo.path, {
    encoding: options.encoding
  })
  const bytes = options.encoding ? null : toUint8Array(data)

  return {
    ...fileInfo,
    bytes,
    data,
    size: bytes ? bytes.length : String(data || '').length,
    sizeText: formatBytes(bytes ? bytes.length : String(data || '').length),
    text: options.encoding ? String(data || '') : ''
  }
}

function getUserDataFilePath(fileName) {
  const wxApi = getWxApi()
  const userDataPath = wxApi.env && wxApi.env.USER_DATA_PATH

  if (!userDataPath) throw new Error('当前微信版本不支持生成文件')

  return `${userDataPath}/${fileName}`
}

function writeTextFile(filePath, data, encoding = 'utf8') {
  const wxApi = getWxApi()
  if (typeof wxApi.getFileSystemManager !== 'function') {
    throw new Error('当前微信版本不支持生成文件')
  }

  const fs = wxApi.getFileSystemManager()
  if (typeof fs.writeFileSync !== 'function') {
    throw new Error('当前微信版本不支持同步生成文件')
  }

  fs.writeFileSync(filePath, data, encoding)
}

function shareFileToChat(filePath, fileName) {
  const wxApi = getWxApi()

  return new Promise((resolve, reject) => {
    if (typeof wxApi.shareFileMessage !== 'function') {
      reject(new Error('当前微信版本不支持发送文件到聊天'))
      return
    }

    wxApi.shareFileMessage({
      fileName,
      filePath,
      success: resolve,
      fail: reject
    })
  })
}

async function saveTextFileToChat(fileName, data) {
  const filePath = getUserDataFilePath(fileName)

  writeTextFile(filePath, data, 'utf8')
  await shareFileToChat(filePath, fileName)

  return filePath
}

module.exports = {
  assertFileExtension,
  chooseFile,
  chooseLocalFile,
  chooseMessageFile,
  formatBytes,
  formatExportStamp,
  getFirstSelectedFile,
  getUserDataFilePath,
  isCancelError,
  loadSelectedFile,
  readFile,
  saveTextFileToChat,
  shareFileToChat,
  toUint8Array,
  writeTextFile
}
