/**
 * oss操作在这个文件维护
 */
import OSS from 'ali-oss'
import path from 'path'
import fs from 'fs'
import chalk from 'chalk'
import inquirer from 'inquirer'
import dotenv from 'dotenv'
const fsEx = require('fs-extra')

export const OSS_ENV_PATH = path.resolve(__dirname, '../temp/.env.oss') // oss配置
export interface OssConfig {
  region: string
  accessKeyId: string
  accessKeySecret: string
  bucket: string
}

// 加载oss配置到环境变量里，方便全局使用
function loadEnv(path: string) {
  dotenv.config({
    path: path,
  })
}

// 从命令行交互输入获取oss配置
async function getEnterInfo(): Promise<OssConfig> {
  const accessKeyId = (
    await inquirer.prompt({
      type: 'input',
      name: 'accessKeyId',
      message: '请输入阿里云accessKeyId',
    })
  ).accessKeyId

  const accessKeySecret = (
    await inquirer.prompt({
      type: 'input',
      name: 'accessKeySecret',
      message: '请输入阿里云accessKeySecret',
    })
  ).accessKeySecret

  const region = (
    await inquirer.prompt({
      type: 'input',
      name: 'region',
      message: '请输入阿里云oss的region',
    })
  ).region

  const bucket = (
    await inquirer.prompt({
      type: 'input',
      name: 'bucket',
      message: '请输入阿里云oss的bucket',
    })
  ).bucket

  const ossConfig = { accessKeyId, accessKeySecret, region, bucket }
  saveOssConfig(ossConfig)
  loadEnv(OSS_ENV_PATH)
  return ossConfig
}

// 保存oss配置
function saveOssConfig(ossConfig: OssConfig) {
  fsEx.ensureFileSync(OSS_ENV_PATH)
  fs.writeFileSync(
    OSS_ENV_PATH,
    `accessKeyId=${ossConfig.accessKeyId}
  accessKeySecret=${ossConfig.accessKeySecret}
  region=${ossConfig.region}
  bucket=${ossConfig.bucket}
    `
  )
}

// 从本地缓存获取oss配置
function getOssConfig(): OssConfig {
  if (fsEx.pathExistsSync(OSS_ENV_PATH)) {
    loadEnv(OSS_ENV_PATH)
    const {
      accessKeyId = '',
      accessKeySecret = '',
      region = '',
      bucket = '',
    } = process.env
    const config: OssConfig = { accessKeyId, accessKeySecret, region, bucket }
    return config
  } else {
    return {
      accessKeyId: '',
      accessKeySecret: '',
      region: '',
      bucket: '',
    }
  }
}

// 判断oss配置是否存在
function existsOssConfig(ossConfig: OssConfig): boolean {
  if (
    !ossConfig.accessKeyId ||
    !ossConfig.accessKeySecret ||
    !ossConfig.region ||
    !ossConfig.bucket
  ) {
    return false
  } else {
    return true
  }
}

class AliOSS {
  accessKeyId: string
  accessKeySecret: string
  region: string
  bucket: string
  client: any

  constructor() {
    this.accessKeyId = ''
    this.accessKeySecret = ''
    this.region = ''
    this.bucket = ''
  }

  async init() {
    let ossConfig: OssConfig = getOssConfig()
    if (existsOssConfig(ossConfig)) {
      Object.assign(this, ossConfig)
    } else {
      ossConfig = await getEnterInfo()
      Object.assign(this, ossConfig)
    }

    // 填写OSS文件完整路径和本地文件的完整路径。OSS文件完整路径中不能包含Bucket名称。
    // 如果本地文件的完整路径中未指定本地路径，则默认从示例程序所属项目对应本地路径中上传文件。
    this.client = new OSS({
      // 填写Bucket所在地域。以华东1（杭州）为例，Region填写为oss-cn-hangzhou。
      region: ossConfig.region,
      // 阿里云账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维，请登录RAM控制台创建RAM用户。
      accessKeyId: ossConfig.accessKeyId,
      accessKeySecret: ossConfig.accessKeySecret,
      // 填写Bucket名称。
      bucket: ossConfig.bucket,
    })
  }

  // 上传单个oss文件
  async uploadFiles(ossPath: string, localPath: string) {
    const IS_DEBUG = process.env.debug
    try {
      // 调试
      if (IS_DEBUG) {
        console.log(chalk.yellow(`debug: 上传文件:${localPath}`))
      }

      const result = await this.client.put(ossPath, path.normalize(localPath))

      // 调试
      if (result.res.status === 200) {
        IS_DEBUG &&
          console.log(chalk.yellow(`debug: ${localPath} 文件上传成功`))
        return true
      } else {
        IS_DEBUG && console.log(chalk.yellow(`debug: 文件上传失败:`))
        console.log(result)
        return false
      }
    } catch (e) {
      console.log(e)
      return false
    }
  }

  /**
   * 多oss文件上传
   * @param {*} directory 上传到oss的文件夹名称
   * @param {*} directory 本地上传的文件夹名称
   */
  async multipleFilesUpload(ossDirectory?: string, directory?: string) {
    if (!ossDirectory) {
      console.log(chalk.red(`上传oss失败，缺失oss路径`))
      return
    }
    if (!directory) {
      console.log(chalk.red(`上传oss失败，缺失本地文件夹名称`))
      return
    }
    let stat // 文档读取状态
    let uploadStatus = false // 是否上传成功
    const IS_DEBUG = process.env.debug
    try {
      stat = fs.statSync(directory)
    } catch (e) {
      console.log(chalk.red(e))
      return
    }

    // 判断是否是一个文件夹
    if (stat.isDirectory()) {
      const filesList = fs.readdirSync(directory)
      for (let i = 0; i < filesList.length; i++) {
        const file = filesList[i]
        const parentDirectory = directory // 父级目录
        const currentFileStat = fs.statSync(`${parentDirectory}/${file}`)
        const filePath = `${parentDirectory}/${file}`
        const ossPath = `${ossDirectory}/${file}`

        // 如果是目录，则继续遍历。如果是文件，直接上传oss
        if (currentFileStat.isDirectory()) {
          // 调试
          if (IS_DEBUG) {
            console.log(chalk.yellow(`debug: 递归目录:${filePath}`))
          }
          await this.multipleFilesUpload(ossPath, filePath)
        } else {
          const uploadResult = await this.uploadFiles(
            ossPath,
            path.normalize(path.resolve(process.cwd(), filePath))
          )
          if (!uploadResult) {
            uploadStatus = false
            return uploadStatus
          }
        }
      }
      uploadStatus = true
      return uploadStatus
    } else {
      console.log(
        chalk.red(
          `命令执行失败，请确认: 1.是否在文档模板根目录执行命令 2.目录下是否存在文件名为storybook-static的目录`
        )
      )
      return uploadStatus
    }
  }

  /**
   * 判断oss路径是否存在
   *  @param {*} name 填写不包含Bucket名称在内的Object的完整路径，例如example/test.txt
   */
  async isExistOssPath(name: string) {
    const path = name + '/'

    try {
      await this.client.head(path)
      return true
    } catch (error: any) {
      if (error.code === 'NoSuchKey') {
        return false
      }
    }
  }
}

const OssInstance = new AliOSS()
export default OssInstance
