import $api from '@/api'
import client from '@/assets/oss'
import ossConfig from '@/assets/ossConfig.js'
import ossPaths from '@/assets/ossPaths.js'
import { base64ToFile } from '@/assets/utils/formatFile'
import { isObject, isString } from '@/assets/utils/types'
import { encode } from '@/assets/utils/code.js'
import { getDate } from '@/assets/utils/date.js'

// 默认配置
let config = {
    bucket: ossConfig.bussinessFileBucket,
    path: ossPaths.default
}

// OSS 客户端
let ossClient = null

// 获取 OSS 秘钥
const ossInit = call => {
    ossClient ? call() : $api.aliOssApi.getToken().then(res => {
        const { Status: status, Data: data } = res.data
        if (status) {
            // 初始化 OSS 客户端
            ossClient = client({
                accessKeyId: data.AccessKeyId,
                accessKeySecret: data.AccessKeySecret,
                stsToken: data.SecurityToken,
                endpoint: config.bucket.endpoint,
                bucket: config.bucket.bucket,
                region: config.bucket.region
            })
            call()
        }
    })
}

// 格式化 OSS 上传参数
const add0 = n => n > 9 ? n : '0' + n
const formatParams = (file, realname) => {
    const date = new Date()
    const today = [date.getFullYear(), date.getMonth() + 1, date.getDate()].map(add0).join('')
    const point = file.name.lastIndexOf('.')
    file.fileName = file.name.slice(0, point)
    file.suffix = file.name.slice(point)
    let randomNumber = ''
    for (var i = 0; i < 10; i++) randomNumber += Math.floor(Math.random() * 10)
    let storeAs = `${config.path}/${today}/wx_file_${randomNumber}_${date.getTime()}`
    storeAs += !realname ? file.suffix : `/${file.name}`
    return { storeAs, file }
}

// 上传文件
const uploadFileStart = (fileObj, call) => {
    const { storeAs, file } = formatParams(fileObj)
    ossClient.multipartUpload(storeAs, file).then(result => {
        if (result.res && result.res.status === 200) {
            call({
                url: `/${result.name}`,
                objectKey: result.name,
                name: file.fileName,
                suffix: file.suffix,
                fullName: file.name,
                type: file.type,
                size: file.size
            })
        }
    })
}
// 上传文件，参数是文件对象，可传入文件对象数组表示上传多个
const uploadFile = (fileConfig, filePath, fileObj, call) => {
    if (fileConfig) config.bucket = fileConfig
    else throw new Error('ossTool.js > uploadFile 方法的参数 fileConfig 不能为空')
    if (filePath) config.path = filePath
    else throw new Error('ossTool.js > uploadFile 方法的参数 filePath 不能为空')
    if (fileObj) {
        ossInit(() => {
            if (Array.isArray(fileObj)) {
                let fileRes = []
                let index = 0
                const loopUpload = () => {
                    if (index === fileObj.length) {
                        call && call(fileRes)
                    } else {
                        uploadFileStart(fileObj[index], res => {
                            fileRes.push(res)
                            index++
                            loopUpload()
                        })
                    }
                }
                loopUpload()
            } else {
                uploadFileStart(fileObj, res => call && call(res))
            }
        })
    } else {
        throw new Error('ossTool.js > uploadFile 方法的参数 fileObj 不能为空')
    }
}

// 获取图片水印
export const watermark = wordOrParams => {
    const params = !wordOrParams ? {} : isString(wordOrParams) ? { word: wordOrParams } : wordOrParams
    params.word = params.word || getDate({ format: 'yyyy/MM/dd HH:mm:ss' })
    const config = {
        color: 'FFFFFF',
        size: 16,
        shadow: 50,
        g: 'se',
        x: 20,
        y: 15,
        ...params
    }

    return [
        '?x-oss-process=image/watermark',
        'text_' + encode(config.word),
        'color_' + config.color,
        'size_' + config.size,
        'shadow_' + config.shadow,
        'g_' + config.g,
        'x_' + config.x,
        'y_' + config.y
    ].join()
}

// 上传到 OSS
const uploadToOss = (fileObj, index, params) => {
    const { realname, progress, success } = params
    return new Promise(resolve => {
        const { storeAs, file } = formatParams(fileObj, realname)
        ossClient.multipartUpload(storeAs, file, {
            progress: percent => {
                if (progress) progress(percent * 100, index)
            }
        }).then(result => {
            if (result.res && result.res.status === 200) {
                const res = {
                    url: `/${result.name}`,
                    objectKey: result.name,
                    name: file.fileName,
                    suffix: file.suffix,
                    fullName: file.name,
                    type: file.type,
                    size: file.size
                }
                if (params.watermark && /^\.(png|jpe?g|webp)$/.test(res.suffix)) {
                    res.url += params.watermark === true ? watermark()
                        : isString(params.watermark) ? params.watermark
                            : params.watermark[index]
                }
                if (success) success(res, index)
                resolve(res)
            }
        })
    })
}
// 上传文件 promise 版
/*
params: {
    bucket {Object}：oss 配置
    path {String}：文件保存路径
    file {File/FileArray}：文件或文件数组
    watermark {Boolean/String/StringArray}：图片水印
    realname {Boolean}：是否以真实文件名保存，上传附件时使用，便于下载和预览时使用实际的文件名
    progress {Function}：上传进度回调，参数为上传的进度（范围 0~100），如果是上传文件数组则会有第二个参数当前上传文件的 index
    success {Function}：上传文件数组时传入，每次文件上传成功的回调，参数分别为结果对象和当前 index（then() 只在所有文件上传完才触发）
}
*/
export const ossUpload = (params = {}) => {
    return new Promise((resolve, reject) => {
        if (!params.file) return reject(new Error('upload 方法文件对象不能为空'))
        if (!isObject(params)) return reject(new Error('upload 方法只接受对象类型参数'))
        config = { ...config, ...params }
        ossInit(() => {
            const isArray = Array.isArray(params.file)
            const list = (isArray ? params.file : [params.file])
                .map(item => isString(item) ? base64ToFile(item) : item)
                .map((file, index) => uploadToOss(file, index, params))
            Promise.all(list).then(res => resolve(isArray ? res : res[0]))
        })
    })
}

// 删除文件，参数是文件的 objectKey 数组，可传入字符串表示删除单个
const deleteFile = (fileConfig, filePath, objectKeys, call) => {
    if (fileConfig) config.bucket = fileConfig
    else throw new Error('ossTool.js > uploadFile 方法的参数 fileConfig 不能为空')
    if (filePath) config.path = filePath
    else throw new Error('ossTool.js > uploadFile 方法的参数 filePath 不能为空')
    if (objectKeys) {
        if (typeof objectKeys === 'string') objectKeys = [objectKeys]
        if (Array.isArray(objectKeys)) {
            ossInit(() => {
                ossClient.deleteMulti(objectKeys).then(result => {
                    if (result.res.status === 200) call && call()
                })
            })
        } else {
            throw new Error('ossTool.js > deleteFile 方法的参数类型只接受数组或字符串')
        }
    } else {
        throw new Error('ossTool.js > deleteFile 方法的参数不能为空')
    }
}

// 删除文件 promise 版
/*
params: {
    bucket {Object}：oss 配置
    path {String}：文件保存路径
    key {String/StringArray}：objectKey 或 objectKey 数组
}
*/
export const ossDelete = (params = {}) => {
    return new Promise((resolve, reject) => {
        if (!params.key) return reject(new Error('del 方法 key 不能为空'))
        if (!isObject(params)) return reject(new Error('del 方法只接受对象类型参数'))
        config = {...config, ...params}
        ossInit(() => {
            const key = Array.isArray(params.key) ? params.key : [params.key]
            ossClient.deleteMulti(key).then(result => {
                if (result.res.status === 200) resolve()
            })
        })
    })
}

export default { uploadFile, deleteFile }
