import { constants, utils, helpers } from '@cloudbase/utilities'
import { ICloudbase } from '@cloudbase/types'
import { ICloudbaseComponent } from '@cloudbase/types/component'
import {
  ICloudbaseFileMetaDataRes,
  ICloudbaseFileInfo,
  ICloudbaseUploadFileParams,
  ICloudbaseUploadFileResult,
  ICloudbaseGetUploadMetadataParams,
  ICloudbaseDeleteFileParams,
  ICloudbaseDeleteFileResult,
  ICloudbaseGetTempFileURLResult,
  ICloudbaseGetTempFileURLParams,
  ICloudbaseDownloadFileResult,
  ICloudbaseDownloadFileParams,
} from '@cloudbase/types/storage'

declare const cloudbase: ICloudbase

enum EUploadMethod {
  put = 'put',
  post = 'post'
}

const { getSdkName, ERRORS, COMMUNITY_SITE_URL } = constants
const { isArray, isString, isPalinObject, execCallback } = utils
const { catchErrorsDecorator } = helpers

const COMPONENT_NAME = 'storage'

class CloudbaseStorage {
  @catchErrorsDecorator({
    customInfo: {
      className: 'Cloudbase',
      methodName: 'uploadFile',
    },
    title: '上传文件失败',
    messages: [
      '请确认以下各项：',
      '  1 - 调用 uploadFile() 的语法或参数是否正确',
      '  2 - 当前域名是否在安全域名列表中：https://console.cloud.tencent.com/tcb/env/safety',
      '  3 - 云存储安全规则是否限制了当前登录状态访问',
      `如果问题依然存在，建议到官方问答社区提问或寻找帮助：${COMMUNITY_SITE_URL}`,
    ],
  })
  public async uploadFile(
    params: ICloudbaseUploadFileParams,
    callback?: Function
  ): Promise<ICloudbaseUploadFileResult> {
    const { cloudPath, filePath, onUploadProgress, method = 'put', headers = {} } = params
    if (!isString(cloudPath) || !filePath) {
      throw new Error(JSON.stringify({
        code: ERRORS.INVALID_PARAMS,
        msg: `[${COMPONENT_NAME}.uploadFile] invalid params`,
      }))
    }
    const uploadMethod = { put: EUploadMethod.put, post: EUploadMethod.post }[method.toLocaleLowerCase()] || EUploadMethod.put

    const action = 'storage.getUploadMetadata'
    // @ts-ignore
    const { request } = this
    const metaDataParam = {
      path: cloudPath,
      method: uploadMethod,
    }
    if (uploadMethod === EUploadMethod.put) {
      /* eslint-disable-next-line */
      metaDataParam["headers"] = headers
    }
    const metaData: ICloudbaseFileMetaDataRes = await request.send(action, metaDataParam)

    const {
      data: { url, authorization, token, fileId, cosFileId, download_url: downloadUrl },
      requestId,
    } = metaData

    const commonParams = {
      url,
      file: filePath,
      name: cloudPath,
      onUploadProgress,
    }

    const putParams = {
      ...commonParams,
      method: EUploadMethod.put,
      headers: {
        ...headers,
        authorization,
        'x-cos-meta-fileid': cosFileId,
        'x-cos-security-token': token,
      },
    }

    const postParams = {
      ...commonParams,
      method: EUploadMethod.post,
      data: {
        key: cloudPath,
        signature: authorization,
        'x-cos-meta-fileid': cosFileId,
        success_action_status: '201',
        'x-cos-security-token': token,
      },
    }

    const uploadConfig = {
      [EUploadMethod.put]: {
        params: putParams,
        isSuccess: (code: number) => code >= 200 && code < 300,
      },
      [EUploadMethod.post]: {
        params: postParams,
        isSuccess: (code: number) => code === 201,
      },
    }

    const res = await request.upload(uploadConfig[uploadMethod].params)

    if (uploadConfig[uploadMethod].isSuccess(res.statusCode)) {
      return execCallback(callback, null, {
        fileID: fileId,
        download_url: downloadUrl,
        requestId,
      })
    }
    return execCallback(callback, new Error(`[${getSdkName()}][${ERRORS.OPERATION_FAIL}][${COMPONENT_NAME}]:${res.data}`))
  }
  @catchErrorsDecorator({
    customInfo: {
      className: 'Cloudbase',
      methodName: 'getUploadMetadata',
    },
    title: '获取上传元信息失败',
    messages: [
      '请确认以下各项：',
      '  1 - 调用 getUploadMetadata() 的语法或参数是否正确',
      '  2 - 当前域名是否在安全域名列表中：https://console.cloud.tencent.com/tcb/env/safety',
      '  3 - 云存储安全规则是否限制了当前登录状态访问',
      `如果问题依然存在，建议到官方问答社区提问或寻找帮助：${COMMUNITY_SITE_URL}`,
    ],
  })
  public async getUploadMetadata(
    params: ICloudbaseGetUploadMetadataParams,
    callback?: Function
  ): Promise<any> {
    const { cloudPath } = params
    if (!isString(cloudPath)) {
      throw new Error(JSON.stringify({
        code: ERRORS.INVALID_PARAMS,
        msg: `[${COMPONENT_NAME}.getUploadMetadata] invalid cloudPath`,
      }))
    }
    // @ts-ignore
    const { request } = this
    const action = 'storage.getUploadMetadata'

    try {
      const metaData = await request.send(action, {
        path: cloudPath,
      })
      return execCallback(callback, null, metaData)
    } catch (err) {
      return execCallback(callback, err)
    }
  }
  @catchErrorsDecorator({
    customInfo: {
      className: 'Cloudbase',
      methodName: 'deleteFile',
    },
    title: '删除文件失败',
    messages: [
      '请确认以下各项：',
      '  1 - 调用 deleteFile() 的语法或参数是否正确',
      '  2 - 当前域名是否在安全域名列表中：https://console.cloud.tencent.com/tcb/env/safety',
      '  3 - 云存储安全规则是否限制了当前登录状态访问',
      `如果问题依然存在，建议到官方问答社区提问或寻找帮助：${COMMUNITY_SITE_URL}`,
    ],
  })
  public async deleteFile(
    params: ICloudbaseDeleteFileParams,
    callback?: Function
  ): Promise<ICloudbaseDeleteFileResult> {
    const { fileList } = params

    if (!fileList || !isArray(fileList) || fileList.length === 0) {
      throw new Error(JSON.stringify({
        code: ERRORS.INVALID_PARAMS,
        msg: `[${COMPONENT_NAME}.deleteFile] fileList must not be empty`,
      }))
    }

    for (const fileId of fileList) {
      if (!fileId || !isString(fileId)) {
        throw new Error(JSON.stringify({
          code: ERRORS.INVALID_PARAMS,
          msg: `[${COMPONENT_NAME}.deleteFile] fileID must be string`,
        }))
      }
    }

    const action = 'storage.batchDeleteFile'
    // @ts-ignore
    const { request } = this
    const res = await request.send(action, {
      fileid_list: fileList,
    })

    if (res.code) {
      return execCallback(callback, null, res)
    }
    const data = {
      fileList: res.data.delete_list,
      requestId: res.requestId,
    }
    return execCallback(callback, null, data)
  }
  @catchErrorsDecorator({
    customInfo: {
      className: 'Cloudbase',
      methodName: 'getTempFileURL',
    },
    title: '获取文件下载链接',
    messages: [
      '请确认以下各项：',
      '  1 - 调用 getTempFileURL() 的语法或参数是否正确',
      '  2 - 当前域名是否在安全域名列表中：https://console.cloud.tencent.com/tcb/env/safety',
      '  3 - 云存储安全规则是否限制了当前登录状态访问',
      `如果问题依然存在，建议到官方问答社区提问或寻找帮助：${COMMUNITY_SITE_URL}`,
    ],
  })
  public async getTempFileURL(
    params: ICloudbaseGetTempFileURLParams,
    callback?: Function
  ): Promise<ICloudbaseGetTempFileURLResult> {
    const { fileList } = params

    if (!fileList || !isArray(fileList) || fileList.length === 0) {
      throw new Error(JSON.stringify({
        code: ERRORS.INVALID_PARAMS,
        msg: `[${COMPONENT_NAME}.getTempFileURL] fileList must not be empty`,
      }))
    }

    const convertedFileList = []
    for (const file of fileList) {
      if (isPalinObject(file)) {
        if (!Object.prototype.hasOwnProperty.call(file, 'fileID')
          || !Object.prototype.hasOwnProperty.call(file, 'maxAge')) {
          throw new Error(JSON.stringify({
            code: ERRORS.INVALID_PARAMS,
            msg: `[${COMPONENT_NAME}.getTempFileURL] file info must include fileID and maxAge`,
          }))
        }

        convertedFileList.push({
          fileid: (file as ICloudbaseFileInfo).fileID,
          max_age: (file as ICloudbaseFileInfo).maxAge,
        })
      } else if (isString(file)) {
        convertedFileList.push({
          fileid: file,
        })
      } else {
        throw new Error(JSON.stringify({
          code: ERRORS.INVALID_PARAMS,
          msg: `[${COMPONENT_NAME}.getTempFileURL] invalid fileList`,
        }))
      }
    }

    const action = 'storage.batchGetDownloadUrl'
    // @ts-ignore
    const { request } = this

    const res = await request.send(action, { file_list: convertedFileList })

    if (res.code) {
      return execCallback(callback, null, res)
    }

    return execCallback(callback, null, {
      fileList: res.data.download_list,
      requestId: res.requestId,
    })
  }
  @catchErrorsDecorator({
    customInfo: {
      className: 'Cloudbase',
      methodName: 'downloadFile',
    },
    title: '下载文件失败',
    messages: [
      '请确认以下各项：',
      '  1 - 调用 downloadFile() 的语法或参数是否正确',
      '  2 - 当前域名是否在安全域名列表中：https://console.cloud.tencent.com/tcb/env/safety',
      '  3 - 云存储安全规则是否限制了当前登录状态访问',
      `如果问题依然存在，建议到官方问答社区提问或寻找帮助：${COMMUNITY_SITE_URL}`,
    ],
  })
  public async downloadFile(
    params: ICloudbaseDownloadFileParams,
    callback?: Function
  ): Promise<ICloudbaseDownloadFileResult> {
    const { fileID } = params
    if (!isString(fileID)) {
      throw new Error(JSON.stringify({
        code: ERRORS.INVALID_PARAMS,
        msg: `[${COMPONENT_NAME}.getTempFileURL] fileID must be string`,
      }))
    }

    const tmpUrlRes = await this.getTempFileURL.call(this, {
      fileList: [{
        fileID,
        maxAge: 600,
      }],
    })

    const res = tmpUrlRes.fileList[0]

    if (res.code !== 'SUCCESS') {
      return execCallback(callback, res)
    }
    // @ts-ignore
    const { request } = this

    const tmpUrl = encodeURI(res.download_url)

    const result = await request.download({ url: tmpUrl })
    return execCallback(callback, null, result)
  }
}

const cloudbaseStorage = new CloudbaseStorage()
const component: ICloudbaseComponent = {
  name: COMPONENT_NAME,
  entity: {
    uploadFile: cloudbaseStorage.uploadFile,
    deleteFile: cloudbaseStorage.deleteFile,
    getTempFileURL: cloudbaseStorage.getTempFileURL,
    downloadFile: cloudbaseStorage.downloadFile,
    getUploadMetadata: cloudbaseStorage.getUploadMetadata,
  },
}

try {
  cloudbase.registerComponent(component)
} catch (e) { }

export function registerStorage(app: Pick<ICloudbase, 'registerComponent'>) {
  try {
    app.registerComponent(component)
  } catch (e) {
    console.warn(e)
  }
}
