/* eslint-disable prefer-destructuring */
/* eslint-disable @typescript-eslint/naming-convention */
import {
  SDKAdapterInterface,
  StorageInterface,
  StorageType,
  WebSocketContructor as WebSocketConstructor,
} from '@cloudbase/adapter-interface'

import { NodeRequest } from './request'
import { nodeTool } from './tool'
import { getSecretInfo } from './context'
import { parseQueryString } from './utils'
import { ICloudbaseConfig } from '@cloudbase/types'

// ws 延迟加载，避免非 Node 环境打包时引入
let _WS: any = null
function getWS() {
  if (!_WS) {
    try {
      // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
      _WS = require('ws')
    } catch (e) {
      console.error('缺少依赖 ws，请执行以下命令安装：\n\n'
        + '  npm install ws\n\n'
        + '该依赖用于 Node 环境下的 WebSocket 连接。',)
    }
  }
  return _WS
}

// Re-export for external consumers
export { NodeRequest } from './request'
export { parseQueryString, createWebStreamFromNodeReadableStream } from './utils'
export type {
  ICreateTicketOpts,
  IGetUserInfoResult,
  IGetEndUserInfoResult,
  IUserInfoQuery,
  IContextParam,
  ICompleteCloudbaseContext,
} from './types'

/**
 * 环境匹配检测：判断当前运行环境是否为 Node.js
 * SDK 通过此方法决定是否使用当前 adapter
 */
function isMatch(): boolean {
  // eslint-disable-next-line eqeqeq
  return typeof process !== 'undefined' && process.versions != null && process.versions.node != null
}

/**
 * 基于 Map 的内存存储实现
 * Node.js 环境下无浏览器 Storage API，使用内存 Map 模拟
 */
const storage: StorageInterface = (() => {
  const db = new Map()
  return {
    mode: 'sync',
    setItem(key, value) {
      db.set(key, value)
    },
    getItem(key) {
      return db.get(key)
    },
    removeItem(key) {
      db.delete(key)
    },
    clear() {
      db.clear()
    },
  }
})()

/**
 * 生成 Node.js 环境的 SDK Adapter
 * 组装请求类、存储、WebSocket、认证等能力
 *
 * @param options - 配置项，包含 EventBus（用于验证码回调）
 * @returns SDK Adapter 实例
 */
function genAdapter(options: any) {
  const adapter: SDKAdapterInterface & {
    getSecretInfo: (config?: ICloudbaseConfig) => {
      env: string
      secretId: string
      secretKey: string
      sessionToken: string
      accessKey: string
      secretType: string
    }
    nodeTool: (app: any, config: ICloudbaseConfig) => void
  } = {
    nodeTool,
    getSecretInfo,
    root: { globalThis: {} },
    reqClass: NodeRequest,
    wsClass: getWS() as WebSocketConstructor,
    sessionStorage: storage,
    localStorage: storage,
    primaryStorage: StorageType.session,
    captchaOptions: {
      /**
       * 验证码处理回调
       * 解析 data: URI 中的验证码参数，通过 EventBus 通知业务层
       * 等待业务层返回验证结果后 resolve
       */
      openURIWithCallback: (_url: string) => {
        const { EventBus } = options
        let queryObj: Record<string, string | string[]> = {}
        let url = _url

        // 从 data: URI 中提取查询参数
        const matched = _url.match(/^(data:.*?)(\?[^#\s]*)?$/)
        if (matched) {
          url = matched[1]
          const search = matched[2]
          if (search) {
            queryObj = parseQueryString(search)
          }
        }

        const { token, ...restQueryObj } = queryObj

        // data: 协议但无 token，视为无效验证码数据
        if (/^data:/.test(url) && !token) {
          return Promise.reject({
            error: 'invalid_argument',
            error_description: `invalie captcha data: ${_url}`,
          })
        }
        if (!token) {
          return Promise.reject({
            error: 'unimplemented',
            error_description: 'need to impl captcha data',
          })
        }

        return new Promise((resolve) => {
          // 通知业务层展示验证码
          EventBus.$emit('CAPTCHA_DATA_CHANGE', {
            ...restQueryObj,
            token,
            url,
          })

          // 等待业务层返回验证结果
          EventBus.$once('RESOLVE_CAPTCHA_DATA', (res: { captcha_token: string; expires_in: number }) => {
            resolve(res)
          })
        })
      },
    },
  }
  return adapter
}

/** Node.js Adapter 入口导出 */
const adapter = {
  genAdapter,
  isMatch,
  runtime: 'node-adapter',
}

export default adapter
