const Db = require('@cloudbase/database').Db
const storage = require('./src/storage')
const functions = require('./src/functions')
const auth = require('./src/auth')
const wx = require('./src/wx')
const Request = require('./src/utils/dbRequest')
const logger = require('./src/log')
const { SYMBOL_CURRENT_ENV } = require('./src/const/symbol')
const { getCurrentEnv } = require('./src/utils/utils')

const ExtRequest = require('./src/utils/extRequest')

function Tcb(config) {
  this.config = config ? config : this.config
  this.requestClient = new ExtRequest()
  this.SYMBOL_CURRENT_ENV = SYMBOL_CURRENT_ENV
}

Tcb.prototype.init = function({
  secretId,
  secretKey,
  sessionToken,
  debug,
  env,
  proxy,
  timeout,
  serviceUrl,
  version,
  headers = {},
  credentials,
  timingsMeasurer,
  isHttp,
  signMethod = 'v2',
  isUpdateSelfConfig = true,
  forever = false
} = {}) {
  if ((secretId && !secretKey) || (!secretId && secretKey)) {
    throw Error('secretId and secretKey must be a pair')
  }

  const config = {
    get secretId() {
      return this._secretId ? this._secretId : process.env.TENCENTCLOUD_SECRETID
    },
    set secretId(id) {
      this._secretId = id
    },
    get secretKey() {
      return this._secretKey
        ? this._secretKey
        : process.env.TENCENTCLOUD_SECRETKEY
    },
    set secretKey(key) {
      this._secretKey = key
    },
    get sessionToken() {
      if (this._sessionToken === undefined) {
        //默认临时密钥
        return process.env.TENCENTCLOUD_SESSIONTOKEN
      } else if (this._sessionToken === false) {
        //固定秘钥
        return undefined
      } else {
        //传入的临时密钥
        return this._sessionToken
      }
    },
    set sessionToken(token) {
      this._sessionToken = token
    },
    envName: env,
    proxy: proxy,
    isHttp: isHttp,
    headers: Object.assign({}, headers)
  }

  config.debug = debug
  config.forever = forever
  config.signMethod = signMethod
  config.timingsMeasurer = timingsMeasurer
  config.secretId = secretId
  config.secretKey = secretKey
  config.timeout = timeout || 15000
  config.serviceUrl = serviceUrl
  config.credentials = credentials
  config.sessionToken = sessionToken
    ? sessionToken
    : secretId && secretKey
    ? false
    : undefined

  if (version) {
    config.headers['x-sdk-version'] = version
  }

  // 这里的目的是创建新实例时可以避免更新当前实例
  if (isUpdateSelfConfig) {
    this.config = config
  }

  return new Tcb(config)
}

Tcb.prototype.database = function(dbConfig = {}) {
  Db.reqClass = Request
  if (Object.prototype.toString.call(dbConfig).slice(8, -1) !== 'Object') {
    throw Error('dbConfig must be an object')
  }

  if (dbConfig && dbConfig.env) {
    // env变量名转换
    dbConfig.envName = dbConfig.env
    delete dbConfig.env
  }
  this.config = Object.assign(this.config, dbConfig)
  return new Db({ ...this })
}

/**
 * @returns string
 */
Tcb.prototype.getCurrentEnv = function() {
  return getCurrentEnv()
}

const extensionMap = {}
/**
 * 注册扩展
 */
Tcb.prototype.registerExtension = function(ext) {
  extensionMap[ext.name] = ext
}

Tcb.prototype.invokeExtension = async function(name, opts) {
  const ext = extensionMap[name]
  if (!ext) {
    throw Error(`扩展${name} 必须先注册`)
  }

  return await ext.invoke(opts, this)
}

Tcb.prototype.parseContext = function(context) {
  if (typeof context !== 'object') {
    throw Error('context 必须为对象类型')
  }
  let {
    memory_limit_in_mb,
    time_limit_in_ms,
    request_id,
    environ = '',
    function_version,
    namespace,
    function_name,
    environment
  } = context
  let parseResult = {}

  try {
    parseResult.memoryLimitInMb = memory_limit_in_mb
    parseResult.timeLimitIns = time_limit_in_ms
    parseResult.requestId = request_id
    parseResult.functionVersion = function_version
    parseResult.namespace = namespace
    parseResult.functionName = function_name

    // 存在environment 为新架构 上新字段 JSON序列化字符串
    if (environment) {
      parseResult.environment = JSON.parse(environment)
      return parseResult
    }

    // 不存在environment 则为老字段,老架构上存在bug,无法识别value含特殊字符(若允许特殊字符,影响解析,这里特殊处理)

    const parseEnviron = environ.split(';')
    let parseEnvironObj = {}
    for (let i in parseEnviron) {
      const equalIndex = parseEnviron[i].indexOf('=')
      if (equalIndex < 0) {
        // value含分号影响切割,未找到= 均忽略
        continue
      }
      const key = parseEnviron[i].slice(0, equalIndex)
      let value = parseEnviron[i].slice(equalIndex + 1)

      // value 含, 为数组
      if (value.indexOf(',') >= 0) {
        value = value.split(',')
      }
      parseEnvironObj[key] = value
    }

    parseResult.environ = parseEnvironObj
  } catch (err) {
    throw Error('无效的context对象')
  }
  return parseResult
}

function each(obj, fn) {
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      fn(obj[i], i)
    }
  }
}

function extend(target, source) {
  each(source, function(val, key) {
    target[key] = source[key]
  })
  return target
}

extend(Tcb.prototype, functions)
extend(Tcb.prototype, storage)
extend(Tcb.prototype, wx)
extend(Tcb.prototype, auth)
extend(Tcb.prototype, logger)

module.exports = new Tcb()