import { adapters, constants, utils, helpers } from '@cloudbase/utilities'
import { SDKAdapterInterface, CloudbaseAdapter, IRequestConfig } from '@cloudbase/adapter-interface'
import {
  ICloudbaseConfig,
  ICloudbaseUpgradedConfig,
  ICloudbase,
  ICloudbaseExtension,
  KV,
  ICloudbasePlatformInfo,
} from '@cloudbase/types'
import { ICloudbaseAuth } from '@cloudbase/types/auth'
import adapterForWxMp from '@cloudbase/adapter-wx_mp'
import { registerComponent, registerHook } from './libs/component'
import { Platform } from './libs/adapter'
import { ICloudbaseComponent, ICloudbaseHook } from '@cloudbase/types/component'
import { ICloudbaseCache } from '@cloudbase/types/cache'
import { initCache, getCacheByEnvId, getLocalCache } from './libs/cache'
import { ICloudbaseRequest } from '@cloudbase/types/request'
import { initRequest, getRequestByEnvId } from './libs/request'
import {
  getSdkName,
  setSdkVersion,
  setRegionLevelEndpoint,
  setSdkName,
  setGatewayEndPointWithEnv,
  type EndPointKey,
  type ISetEndPointWithKey,
  setEndPointInfo,
  getEndPointInfo,
  getSdkVersion,
} from './constants/common'
export { getBaseEndPoint } from './constants/common'
const { useAdapters, useDefaultAdapter } = adapters
const { ERRORS, COMMUNITY_SITE_URL } = constants
const { printWarn } = utils
const { catchErrorsDecorator } = helpers

/**
 * @constant 默认配置
 */
const DEFAULT_INIT_CONFIG: Partial<ICloudbaseConfig> = {
  timeout: 15000,
  persistence: 'local',
}

// timeout上限10分钟
const MAX_TIMEOUT = 1000 * 60 * 10
// timeout下限100ms
const MIN_TIMEOUT = 100

const extensionMap: KV<ICloudbaseExtension> = {}

class Cloudbase implements ICloudbase {
  public authInstance: ICloudbaseAuth
  public oauthInstance: any
  public requestClient: any
  public oauthClient: any
  public version: string
  private cloudbaseConfig: ICloudbaseConfig

  constructor(config?: ICloudbaseConfig) {
    this.cloudbaseConfig = config ? config : this.cloudbaseConfig
    this.authInstance = null
    this.oauthInstance = null
    this.version = getSdkVersion()
  }

  get config() {
    return this.cloudbaseConfig
  }

  get platform(): ICloudbasePlatformInfo {
    return Platform
  }

  get cache(): ICloudbaseCache {
    return getCacheByEnvId(this.cloudbaseConfig.env)
  }

  get localCache(): ICloudbaseCache {
    return getLocalCache(this.cloudbaseConfig.env)
  }

  get request(): ICloudbaseRequest {
    return getRequestByEnvId(this.cloudbaseConfig.env)
  }

  @catchErrorsDecorator({
    mode: 'sync',
    title: 'Cloudbase 初始化失败',
    messages: [
      '请确认以下各项：',
      '  1 - 调用 cloudbase.init() 的语法或参数是否正确',
      '  2 - 如果是非浏览器环境，是否配置了安全应用来源（https://docs.cloudbase.net/api-reference/webv3/adapter#%E7%AC%AC-2-%E6%AD%A5%E9%85%8D%E7%BD%AE%E5%AE%89%E5%85%A8%E5%BA%94%E7%94%A8%E6%9D%A5%E6%BA%90）',
      `如果问题依然存在，建议到官方问答社区提问或寻找帮助：${COMMUNITY_SITE_URL}`,
    ],
  })
  public init(config: ICloudbaseConfig): Cloudbase {
    if (!config.env) {
      throw new Error(JSON.stringify({
        code: ERRORS.INVALID_PARAMS,
        msg: 'env must not be specified',
      }),)
    }
    // 初始化时若未兼容平台，则使用默认adapter
    if (!Platform.adapter) {
      this.useDefaultAdapter()
    }

    const reqConfig: IRequestConfig = {
      timeout: config.timeout || 5000,
      timeoutMsg: `[${getSdkName()}][REQUEST TIMEOUT] request had been abort since didn't finished within${
        config.timeout / 1000
      }s`,
    }

    this.requestClient = new Platform.adapter.reqClass(reqConfig)
    this.cloudbaseConfig = {
      ...DEFAULT_INIT_CONFIG,
      ...config,
    }
    // 修正timeout取值
    this.cloudbaseConfig.timeout = this.formatTimeout(this.cloudbaseConfig.timeout)
    // 初始化cache和request
    const { env, persistence, debug, timeout, oauthClient } = this.cloudbaseConfig
    initCache({ env, persistence, debug, platformInfo: this.platform })

    setRegionLevelEndpoint(env, config.region || '')
    setGatewayEndPointWithEnv(env)

    const app = new Cloudbase(this.cloudbaseConfig)
    initRequest({ env, region: config.region || '', timeout, oauthClient, _fromApp: app })
    app.requestClient = this.requestClient
    ;(this as any)?.fire?.('cloudbase_init', app)
    return app
  }

  public updateConfig(config: ICloudbaseUpgradedConfig) {
    const { persistence, debug } = config
    this.cloudbaseConfig.persistence = persistence
    this.cloudbaseConfig.debug = debug
    // persistence改动影响cache
    initCache({ env: this.cloudbaseConfig.env, persistence, debug, platformInfo: this.platform })
  }

  public registerExtension(ext: ICloudbaseExtension) {
    extensionMap[ext.name] = ext
  }
  @catchErrorsDecorator({
    title: '调用扩展能力失败',
    messages: [
      '请确认以下各项：',
      '  1 - 调用 invokeExtension() 的语法或参数是否正确',
      '  2 - 被调用的扩展能力是否已经安装并通过 registerExtension() 注册',
      `如果问题依然存在，建议到官方问答社区提问或寻找帮助：${COMMUNITY_SITE_URL}`,
    ],
  })
  public async invokeExtension(name: string, opts: any) {
    const ext = extensionMap[name]
    if (!ext) {
      throw new Error(JSON.stringify({
        code: ERRORS.INVALID_PARAMS,
        msg: `extension:${name} must be registered before invoke`,
      }),)
    }

    return await ext.invoke(opts, this)
  }

  public useAdapters(adapters: CloudbaseAdapter | CloudbaseAdapter[]) {
    const { adapter, runtime } = useAdapters(adapters) || {}
    adapter && (Platform.adapter = adapter as SDKAdapterInterface)
    runtime && (Platform.runtime = runtime as string)
  }

  public registerHook(hook: ICloudbaseHook) {
    registerHook(Cloudbase, hook)
  }

  public registerComponent(component: ICloudbaseComponent) {
    registerComponent(Cloudbase, component)
  }

  public registerVersion(version: string) {
    setSdkVersion(version)
    this.version = version
  }

  public registerSdkName(name: string) {
    setSdkName(name)
  }

  /** 设置 tcb api 的 endpoint */
  public registerEndPoint(url: string, protocol?: 'http' | 'https') {
    setEndPointInfo({ baseUrl: url, protocol, env: this.config.env, endPointKey: 'CLOUD_API' })
  }

  /** 设置网关/tcb api的 endPoint，通过 key 指定 */
  public registerEndPointWithKey(props: ISetEndPointWithKey) {
    setEndPointInfo({
      env: this.config.env,
      endPointKey: props.key,
      baseUrl: props.url,
      protocol: props.protocol,
    })
  }

  /** 拿网关/tcb api的 endPoint，通过 key 指定 */
  public getEndPointWithKey(key: EndPointKey) {
    const info = getEndPointInfo(this.config.env, key)
    return {
      BASE_URL: info.baseUrl,
      PROTOCOL: info.protocol,
    }
  }

  private useDefaultAdapter() {
    const { adapter, runtime } = useDefaultAdapter()
    Platform.adapter = adapter as SDKAdapterInterface
    Platform.runtime = runtime as string
  }

  private formatTimeout(timeout: number) {
    switch (true) {
      case timeout > MAX_TIMEOUT:
        printWarn(ERRORS.INVALID_PARAMS, 'timeout is greater than maximum value[10min]')
        return MAX_TIMEOUT
      case timeout < MIN_TIMEOUT:
        printWarn(ERRORS.INVALID_PARAMS, 'timeout is less than maximum value[100ms]')
        return MIN_TIMEOUT
      default:
        return timeout
    }
  }
}

export const cloudbase: ICloudbase = new Cloudbase()
cloudbase.useAdapters(adapterForWxMp)

export default cloudbase
