const OSS = require('ali-oss')
const router = require('@/router');
const Minio = require('minio')
const stream = require('stream')
import Vue from 'vue'
import { customerHttp, OSSclose } from "@/utils/multipartUpload/oss"
import { exist, AWSclose } from "@/utils/multipartUpload/aws"
import { addOrUpdateFileUploadRecord } from '@/api/file'
const { GetObjectStoreToken } = require('../api/user.js')
const {
  S3Client,
} = require("@aws-sdk/client-s3");

Vue.prototype.OSSclientConfig = {
}


async function ossGenerateSTS() {
  let res = null;
  if (localStorage.getItem('stsToken')) {
    res = JSON.parse(localStorage.getItem('stsToken'))
  } else {
    res = await GetObjectStoreToken()
    localStorage.setItem('stsToken', JSON.stringify(res))
  }
  // res.Result.ObjectStoreUse = 'AWS';
  Vue.prototype.OSSclientConfig = { ...res.Result[res.Result.ObjectStoreUse] }
  Vue.prototype.OSSclientConfig.ObjectStoreUse = res.Result.ObjectStoreUse;
  Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint
  switch (res.Result.ObjectStoreUse) {
    case 'AliyunOSS':
      Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
      Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
      Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
      let OSSclient = new OSS(Vue.prototype.OSSclientConfig)
      Vue.prototype.OSSclient = {
        put: async function (objectName, object, fileInfo = {}) {

          OSSclient = await RefreshClient(OSSclient)
          return new Promise(async (resolve, reject) => {
            try {
              let _vm = router.default.app
              if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
                var objectItem = objectName.split('/')
                // objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
                objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
                  0,
                  objectItem[objectItem.length - 1].lastIndexOf('.')
                )}__${new Date().getTime()}${objectItem[objectItem.length - 1].substring(
                  objectItem[objectItem.length - 1].lastIndexOf('.')
                ).toLocaleLowerCase()}`
                objectName = objectItem.join('/')
              }
              let res = await OSSclient.put(objectName, object)
              if (res && res.url) {
                const urlParams = new URLSearchParams(window.location.search)
                const trialId = urlParams.get('trialId')
                if (Object.keys(fileInfo).length !== 0) {
                  fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
                  let params = Object.assign({path: objectName}, fileInfo)
                  addOrUpdateFileUploadRecord(params)
                } else if (trialId) {
                  const fileName = objectName.split('/').pop()
                  const fileType = fileName.includes('.') 
                  ? fileName.split('.').pop().toLowerCase() 
                  : ''
                  let params = { trialId, path: objectName, fileName, fileType }
                  addOrUpdateFileUploadRecord(params)
                }
                resolve({
                  name: objectName,
                  url: res.url
                })
              } else {
                reject()
              }
            } catch (e) {
              console.log(e)
              reject()
            }
          })
        },
        multipartUpload: async (data, progress, fileInfo = {}) => {
          OSSclient = await RefreshClient(OSSclient)
          return new Promise(async (resolve, reject) => {
            try {
              const { file, path } = data;
              if (!file || !path) return reject('file and path be required');
              let config = await getSTSToken(Vue.prototype.OSSclientConfig.expiration);
              if (config) {
                Vue.prototype.OSSclientConfig = { ...config.Result[config.Result.ObjectStoreUse] }
                Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
                Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
                Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
                Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
                Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
                OSSclient = new OSS(Vue.prototype.OSSclientConfig);
              }
              let _vm = router.default.app
              if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
                var objectItem = data.path.split('/')
                // objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
                objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
                  0,
                  objectItem[objectItem.length - 1].lastIndexOf('.')
                )}__${new Date().getTime()}${objectItem[objectItem.length - 1].substring(
                  objectItem[objectItem.length - 1].lastIndexOf('.')
                ).toLocaleLowerCase()}`
                data.path = objectItem.join('/')
              }
              let res = await customerHttp(OSSclient, data, progress);
              if (res) {
                const urlParams = new URLSearchParams(window.location.search)
                const trialId = urlParams.get('trialId')
                if (Object.keys(fileInfo).length !== 0) {
                  fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
                  let params = Object.assign({path: data.path}, fileInfo)
                  addOrUpdateFileUploadRecord(params)
                } else if (trialId) {
                  const fileName = data.path.split('/').pop()
                  const fileType = fileName.includes('.') 
                  ? fileName.split('.').pop().toLowerCase() 
                  : ''
                  let params = { trialId, path: data.path, fileName, fileType }
                  addOrUpdateFileUploadRecord(params)
                }
                
                resolve({
                  name: data.path,
                  url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name)
                })
              } else {
                reject()
              }
            } catch (err) {
              console.log(err)
              reject(err)
            }
          })
        },
        close: () => {
          OSSclose();
        }
      }
      break
    case 'MinIO':
      let minioClient = new Minio.Client(Vue.prototype.OSSclientConfig);
      Vue.prototype.OSSclient = {
        put: function (objectName, object) {
          return new Promise(async (resolve, reject) => {
            try {
              var name = objectName.split('/')[objectName.split('/').length - 1]
              let _vm = router.default.app
              if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
                var objectItem = objectName.split('/')
                objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
                objectName = objectItem.join('/')
              }
              const reader = new FileReader();
              reader.onload = (ex) => {
                const bufferStream = new stream.PassThrough()
                bufferStream.end(Buffer.from(ex.target.result))
                minioClient.putObject(Vue.prototype.OSSclientConfig.bucketName, objectName, bufferStream, function (err, etag) {
                  if (err) {
                    console.log(err)
                    reject()
                  } else {
                    resolve({
                      name: objectName,
                      url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(objectName)
                    })
                  }
                })
              };
              reader.readAsArrayBuffer(object);
            } catch (e) {
              console.log(e)
            }
          })
        },
        close: () => {
          return false
        }
      }
      break
    case 'AWS':
      Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
      let aws = new S3Client({
        endpoint: Vue.prototype.OSSclientConfig.viewEndpoint,
        region: Vue.prototype.OSSclientConfig.region,
        s3ForcePathStyle: true,
        signatureVersion: 'v4',
        forcePathStyle: true,
        // SessionToken: '',
        credentials: {
          accessKeyId: Vue.prototype.OSSclientConfig.accessKeyId,
          secretAccessKey: Vue.prototype.OSSclientConfig.secretAccessKey,
          sessionToken: Vue.prototype.OSSclientConfig.sessionToken
        }
      });
      Vue.prototype.OSSclient = {
        put: async function (objectName, object, fileInfo = {}) {
          let data = {
            file: object,
            path: objectName
          }
          aws = await RefreshClient(aws);
          return uploadAWS(aws, data, () => { }, fileInfo);
        },
        multipartUpload: async (data, progress, fileInfo = {}) => {
          aws = await RefreshClient(aws);
          return uploadAWS(aws, data, progress, fileInfo);
        },
        close: () => {
          AWSclose();
        }
      }
  }
  return
}
// AWS上传函数
function uploadAWS(aws, data, progress, fileInfo) {
  return new Promise(async (resolve, reject) => {
    try {
      const { file, path } = data;
      if (!file || !path) return reject('file and path be required');
      let _vm = router.default.app
      if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
        var objectItem = data.path.split('/')
        // objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
        objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
          0,
          objectItem[objectItem.length - 1].lastIndexOf('.')
        )}__${new Date().getTime()}${objectItem[objectItem.length - 1].substring(
          objectItem[objectItem.length - 1].lastIndexOf('.')
        ).toLocaleLowerCase()}`
        data.path = objectItem.join('/')
      }
      let bucketName = data.path.split("/")[1] || Vue.prototype.OSSclientConfig.bucket
      let curPath = data.path;
      data.path = data.path.replace(`/${bucketName}/`, '');
      await exist(aws, bucketName, data, progress, (path, status) => {
        if (status === 'success') {
          const urlParams = new URLSearchParams(window.location.search)

          const trialId = urlParams.get('trialId')
          if (Object.keys(fileInfo).length !== 0) {
            fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
            let params = Object.assign({path: decodeUtf8(curPath)}, fileInfo)
            addOrUpdateFileUploadRecord(params)
          } else if (trialId) {
            const fileName = decodeUtf8(curPath).split('/').pop()
            const fileType = fileName.includes('.') 
            ? fileName.split('.').pop().toLowerCase() 
            : ''
            let params = { trialId, path: decodeUtf8(curPath), fileName, fileType }
            addOrUpdateFileUploadRecord(params)
          }
          resolve({
            name: decodeUtf8(curPath),
            url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath)
          })
        } else {
          reject()
        }
      });

    } catch (err) {
      console.log(err)
      reject(err)
    }
  })
}
// client过期刷新
async function RefreshClient(client) {
  let config = await getSTSToken(Vue.prototype.OSSclientConfig.expiration);
  if (config) {
    // config.Result.ObjectStoreUse = 'AWS'
    switch (config.Result.ObjectStoreUse) {
      case 'AliyunOSS': {
        Vue.prototype.OSSclientConfig = { ...config.Result[config.Result.ObjectStoreUse] }
        Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
        Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
        Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
        Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
        Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
        return new OSS(Vue.prototype.OSSclientConfig);
      }
      case "MinIO": {
        return client;
      }
      case "AWS": {
        Vue.prototype.OSSclientConfig = { ...config.Result[config.Result.ObjectStoreUse] }
        Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
        Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
        Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
        return new S3Client({
          endpoint: Vue.prototype.OSSclientConfig.viewEndpoint,
          region: Vue.prototype.OSSclientConfig.region,
          s3ForcePathStyle: true,
          signatureVersion: 'v4',
          forcePathStyle: true,
          credentials: {
            accessKeyId: Vue.prototype.OSSclientConfig.accessKeyId,
            secretAccessKey: Vue.prototype.OSSclientConfig.secretAccessKey,
            sessionToken: Vue.prototype.OSSclientConfig.sessionToken
          }
        });
      }
    }

  } else {
    return client;
  }
}
function decodeUtf8(bytes) {
  let str = bytes.split('?');
  let str2 = str[0].split('/');
  let name = str2[str2.length - 1];
  name = encodeURIComponent(name);
  str.shift();
  str2.pop();
  return str2.join("/") + '/' + name;
}
const queue = []
let loading = false;
// 获取凭证
function getSTSToken(credentials) {
  return new Promise(async (resolve, reject) => {
    let isExpired = isCredentialsExpired(credentials);
    if (isExpired) {
      if (loading) {
        queue.push({ resolve, reject })
      }
      if (!loading) {
        loading = true;
        let res = await GetObjectStoreToken();
        loading = false;
        localStorage.setItem('stsToken', JSON.stringify(res))
        resolve(res)
        let p = queue.shift();
        while (p) {
          p.resolve(res)
          p = queue.shift();
        }
      }

    } else {
      // console.log("凭证未过期");
      resolve(false)
    }

  })
}
/**
* oss判断临时凭证是否到期。
**/
function isCredentialsExpired(credentials) {
  if (!credentials) {
    return true;
  }
  const expireDate = new Date(credentials);
  const now = new Date();
  // 如果有效期不足五分钟，视为过期。
  return expireDate.getTime() - now.getTime() <= 300000;

}
function mimeTypeToExt(mimeType) {
  const map = {
    // 图片
    'image/jpeg': 'jpg',
    'image/jpg': 'jpg',
    'image/png': 'png',
    'image/gif': 'gif',
    'image/webp': 'webp',
    'image/svg+xml': 'svg',
    'image/bmp': 'bmp',

    // 文档
    'application/dicom': 'dcm',
    'application/pdf': 'pdf',
    'application/msword': 'doc',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
    'application/vnd.ms-excel': 'xls',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
    'application/vnd.ms-powerpoint': 'ppt',
    'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
    'text/plain': 'txt',
    // 音频/视频
    'audio/mpeg': 'mp3',
    'audio/wav': 'wav',
    'video/mp4': 'mp4',
    'video/mpeg': 'mpeg',
    // 压缩包
    'application/zip': 'zip',
    'application/x-rar-compressed': 'rar',
    'application/x-7z-compressed': '7z',
  };
  return map[mimeType] || ''; // 找不到返回空字符串
}
export const OSSclient = ossGenerateSTS
