import * as qiniu from 'qiniu';
import * as fs from 'fs';
import { Logger } from '@nestjs/common';
import { v4 as uuidv4 } from 'uuid';
import * as COS from 'cos-nodejs-sdk-v5';
import * as STS from 'qcloud-cos-sts';

const getConfig = () => {
  return {
    secretId: process.env.OSS_SECRET_ID, // 固定密钥
    secretKey: process.env.OSS_SECRET_KEY, // 固定密钥
    durationSeconds: 1800,
    // host: 'sts.tencentcloudapi.com', // 域名，非必须，默认为 sts.tencentcloudapi.com
    endpoint: 'sts.tencentcloudapi.com', // 域名，非必须，与host二选一，默认为 sts.tencentcloudapi.com

    // 放行判断相关参数
    bucket: process.env.OSS_BUCKET,
    region: process.env.OSS_REGION,
    allowPrefix: 'image_resources/custom/*', // 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，例子： a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
    // 简单上传和分片，需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
    allowActions: [
      // 简单上传
      'name/cos:PutObject',
      'name/cos:PostObject',
    ],
  };
};

/**
 * getCos
 * @returns
 */
let cos;
export function getCos() {
  if (cos) {
    return cos;
  }
  cos = new COS({
    SecretId: process.env.OSS_SECRET_ID,
    SecretKey: process.env.OSS_SECRET_KEY,
  });
  return cos;
}

let qiniuOssManager: qiniu.rs.BucketManager;
export function getQiniuOssManager() {
  if (qiniuOssManager) {
    return qiniuOssManager;
  }
  const mac = new qiniu.auth.digest.Mac(
    process.env.QINIU_ACCESS_KEY,
    process.env.QINIU_ACCESS_SECRET,
  );
  const config = new qiniu.conf.Config();
  qiniuOssManager = new qiniu.rs.BucketManager(mac, config);
  return qiniuOssManager;
}

/**
 * 生成七牛云临时秘钥
 */
export function generateQiniuTempToken(key) {
  qiniu.conf.ACCESS_KEY = process.env.QINIU_ACCESS_KEY;
  qiniu.conf.SECRET_KEY = process.env.QINIU_ACCESS_SECRET;
  const putPolicy = new qiniu.rs.PutPolicy({
    scope: `${process.env.QINIU_BUCKET}:${key}`,
    expires: 20,
  });
  return putPolicy.uploadToken();
}

export function removeQiniuFile(key) {
  qiniu.conf.ACCESS_KEY = process.env.QINIU_ACCESS_KEY;
  qiniu.conf.SECRET_KEY = process.env.QINIU_ACCESS_SECRET;
  const ossManager = getQiniuOssManager();
  return new Promise((resolve) => {
    ossManager.delete(process.env.QINIU_BUCKET, key, (err) => {
      resolve(!err);
    });
  });
}

export function uploadFile(token, key, filePath) {
  const extra = new qiniu.form_up.PutExtra();
  return new Promise((resolve, reject) => {
    new qiniu.form_up.FormUploader().putFile(
      token,
      key,
      filePath,
      extra,
      (err, ret) => {
        if (!err) {
          resolve(ret);
        } else {
          reject(err);
        }
      },
    );
  });
}

export function uploadTencentFile(key, filePath) {
  const cos = getCos();
  return cos.uploadFile({
    Bucket: process.env.OSS_BUCKET /* 填入您自己的存储桶，必须字段 */,
    Region:
      process.env.OSS_REGION /* 存储桶所在地域，例如 ap-beijing，必须字段 */,
    Key: key /* 存储在桶里的对象键（例如1.jpg，a/b/test.txt），必须字段 */,
    FilePath: filePath,
  });
}

export function uploadImageResource(
  path: string,
  initPath: string = '',
  callback: Function,
) {
  fs.readdir(path, (err, fileOrDirs) => {
    if (!fileOrDirs) {
      return;
    }
    fileOrDirs.forEach((fileOrDir) => {
      const filePath = `${path}/${fileOrDir}`;
      fs.stat(filePath, (err, file) => {
        if (file.isDirectory()) {
          uploadImageResource(filePath, initPath, callback);
        } else {
          const uniqueId = uuidv4();
          const fileRelativePath = filePath.replace(initPath, '');
          const fileName = fileRelativePath.slice(
            fileRelativePath.lastIndexOf('/') + 1,
            fileRelativePath.lastIndexOf('.'),
          );
          const replaceFileRelativePath = fileRelativePath.replace(
            fileName,
            uniqueId,
          );
          const key = `image_resources${replaceFileRelativePath}`;
          uploadTencentFile(key, filePath)
            .then(() => {
              Logger.log(`${fileRelativePath}上传成功`);
              fs.rmSync(filePath);
              const filePaths = fileRelativePath.split('/');
              const cName = filePaths[filePaths.length - 2];
              callback(key, key, fileName, cName);
            })
            .catch(() => {
              Logger.error(`${fileRelativePath}上传失败`);
            });
        }
      });
    });
  });
}

export function uploadSceneImage(
  path: string,
  initPath: string = '',
  callback: Function,
) {
  fs.readdir(path, (err, fileOrDirs) => {
    if (!fileOrDirs) {
      return;
    }
    fileOrDirs.forEach((fileOrDir) => {
      const filePath = `${path}/${fileOrDir}`;
      fs.stat(filePath, (err, file) => {
        if (file.isDirectory()) {
          uploadSceneImage(filePath, initPath, callback);
        } else {
          const uniqueId = uuidv4();
          const fileRelativePath = filePath.replace(initPath, '');
          const fileName = fileRelativePath.slice();
          const replaceFileRelativePath = fileRelativePath.replace(
            fileName,
            uniqueId,
          );
          const key = `image_resources/custom${replaceFileRelativePath}`;
          uploadTencentFile(key, filePath)
            .then(() => {
              Logger.log(`${fileRelativePath}上传成功`);
              fs.rmSync(filePath);
              const filePaths = fileRelativePath.split('/');
              const cName = filePaths[filePaths.length - 2];
              callback(key, key, fileName, cName);
            })
            .catch(() => {
              Logger.error(`${fileRelativePath}上传失败`);
            });
        }
      });
    });
  });
}

/**
 * 腾讯云对象存储生成临时秘钥
 * @returns
 */
export async function generateTempToken() {
  const config = getConfig();
  // 获取临时密钥
  const shortBucketName = config.bucket.substr(
    0,
    config.bucket.lastIndexOf('-'),
  );
  const appId = config.bucket.substr(1 + config.bucket.lastIndexOf('-'));
  const policy = {
    version: '2.0',
    statement: [
      {
        action: config.allowActions,
        effect: 'allow',
        principal: { qcs: ['*'] },
        resource: [
          'qcs::cos:' +
            config.region +
            ':uid/' +
            appId +
            ':prefix//' +
            appId +
            '/' +
            shortBucketName +
            '/' +
            config.allowPrefix,
        ],
      },
    ],
  };
  return new Promise((resolve, reject) => {
    STS.getCredential(
      {
        secretId: config.secretId,
        secretKey: config.secretKey,
        durationSeconds: config.durationSeconds,
        endpoint: config.endpoint,
        policy: policy,
      },
      function (err, tempKeys) {
        if (err) {
          reject(err);
        } else {
          resolve(tempKeys);
        }
      },
    );
  });
}
