import { ERROR } from './constants'
import { getCloudbaseContext, parseContext } from './context'
import { validateUid } from './utils'
import type {
  ICreateTicketOpts,
  IGetUserInfoResult,
  IGetEndUserInfoResult,
  IUserInfoQuery,
  ITemplateNotifyReq,
} from './types'
import { ICloudbaseConfig } from '@cloudbase/types'

// 延迟加载，避免非 Node 环境打包时引入此包
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let jwtSign: ((...args: any[]) => any) | null = null
async function getJwtSign() {
  if (!jwtSign) {
    try {
      // @ts-ignore — 该包仅在 Node 运行时存在，开发环境可能未安装
      const mod = await import('jsonwebtoken')
      jwtSign = mod.default?.sign || mod.sign
    } catch (e) {
      console.error('缺少依赖 jsonwebtoken，请执行以下命令安装：\n\n'
        + '  npm install jsonwebtoken\n\n'
        + '该依赖用于 Node 环境下的自定义登录票据生成。')
    }
  }
  return jwtSign!
}

/**
 * 从云函数运行时上下文中获取当前请求的用户信息
 * 数据来源为环境变量（微信 openId、TCB uuid 等）
 */
function getDefaultUserInfo(): IGetUserInfoResult {
  const { WX_OPENID, WX_APPID, TCB_UUID, TCB_CUSTOM_USER_ID, TCB_ISANONYMOUS_USER } = getCloudbaseContext()

  return {
    openId: WX_OPENID || '',
    appId: WX_APPID || '',
    uid: TCB_UUID || '',
    customUserId: TCB_CUSTOM_USER_ID || '',
    isAnonymous: TCB_ISANONYMOUS_USER === 'true',
  }
}

/**
 * 调用后端管理接口查询用户信息
 * 统一封装 auth.getUserInfoForAdmin 的请求逻辑
 */
function sendUserInfoRequest(app: any, params: Record<string, any>): Promise<any> {
  return app?.request?.send?.('auth.getUserInfoForAdmin', params, {
    pathname: 'web',
    endPointMode: 'CLOUD_API',
  })
}

/**
 * 初始化 Node 端工具方法，挂载到 js-sdk app 实例上
 * 包含：auth 相关方法、模板消息推送、context 解析
 *
 * @param app - js-sdk cloudbase 实例
 * @param config - 配置信息，包含认证凭证和环境 ID
 */
export const nodeTool = (app: any, config: ICloudbaseConfig) => {
  // 仅当 app 已初始化 auth 模块时，才注入 auth 相关方法
  if (app.auth) {
    const auth = {
      /** 获取当前请求的用户信息（从环境变量读取，同步） */
      getUserInfo(): IGetUserInfoResult {
        return getDefaultUserInfo()
      },

      /**
       * 获取终端用户信息
       * 不传 uid 时返回当前请求用户信息，传 uid 时查询指定用户
       */
      async getEndUserInfo(uid?: string): Promise<IGetEndUserInfoResult> {
        const defaultUserInfo = getDefaultUserInfo()

        if (uid === undefined) {
          return { userInfo: defaultUserInfo }
        }
        validateUid(uid)

        return sendUserInfoRequest(app, {
          uuid: uid,
          envName: config.env,
        }).then((result: any) => {
          if (result.code) {
            return result
          }
          return {
            userInfo: { ...defaultUserInfo, ...result.data },
            requestId: result.requestId,
          }
        })
      },

      /**
       * 创建自定义登录 Ticket
       * 使用 RSA 私钥签发 JWT，客户端凭此 Ticket 换取登录态
       *
       * @param uid - 自定义用户 ID（4~32 位）
       * @param options - 刷新间隔和过期时间配置
       * @returns 格式为 "{private_key_id}/@@/{jwt_token}" 的 Ticket 字符串
       */
      async createTicket(uid: string, options: ICreateTicketOpts = {}): Promise<string> {
        validateUid(uid)

        const timestamp = new Date().getTime()
        const { credentials } = config.auth || {}
        const { env } = config

        if (!env) {
          throw { ...ERROR.INVALID_PARAM, message: 'no env in config' }
        }

        if (!credentials?.env_id) {
          throw {
            ...ERROR.INVALID_PARAM,
            message: '当前私钥未包含env_id 信息， 请前往腾讯云云开发控制台，获取自定义登录最新私钥',
          }
        }

        if (credentials.env_id !== env) {
          throw {
            ...ERROR.INVALID_PARAM,
            message: '当前私钥所属环境与 init 指定环境不一致！',
          }
        }

        const {
          refresh = 3600 * 1000, // 默认 1 小时刷新
          expire = timestamp + 7 * 24 * 60 * 60 * 1000, // 默认 7 天过期
        } = options
        const sign = await getJwtSign()
        const token = sign(
          {
            alg: 'RS256',
            env,
            iat: timestamp,
            exp: timestamp + 10 * 60 * 1000, // Ticket 本身 10 分钟有效
            uid,
            refresh,
            expire,
          },
          credentials.private_key,
          {
            allowInsecureKeySizes: true,
            algorithm: 'RS256',
          },
        )

        return `${credentials.private_key_id}/@@/${token}`
      },

      /**
       * 按条件查询用户信息（管理端接口）
       * 支持按 uid、platform、platformId 查询
       */
      async queryUserInfo(query: IUserInfoQuery): Promise<any> {
        const { uid, platform, platformId } = query
        return sendUserInfoRequest(app, {
          uuid: uid,
          platform,
          platformId,
          envName: config.env,
        }).then((result: any) => {
          if (result.code) {
            return result
          }
          return {
            userInfo: { ...result.data },
            requestId: result.requestId,
          }
        })
      },

      /** 获取客户端 IP 地址 */
      getClientIP(): string {
        const { TCB_SOURCE_IP } = getCloudbaseContext()
        return TCB_SOURCE_IP || ''
      },
    }

    // 将 auth 方法逐一挂载到 app.auth 上
    Object.keys(auth).forEach((key) => {
      app.auth[key] = (auth as Record<string, any>)[key]
    })
  }

  /**
   * 发送模板消息通知
   * 通过调用 lowcode-datasource 云函数间接调用微搭 API
   *
   * @param params - 通知参数（策略 ID、模板变量、跳转链接）
   * @param opts - 可选配置，如超时时间
   */
  app.sendTemplateNotification = async (params: ITemplateNotifyReq, opts?: { timeout?: number }) => await app?.callFunction?.(
    {
      name: 'lowcode-datasource',
      data: {
        methodName: 'callWedaApi',
        params: {
          action: 'PushNotifyMsg',
          data: {
            NotifyId: params.notifyId,
            Data: JSON.stringify(params.data),
            NotifyUsers: undefined,
            Url: params.url,
          },
        },
        mode: 'c',
      },
    },
    undefined,
    opts,
  )

  /** 挂载 context 解析工具，方便用户在云函数中使用 */
  app.parseContext = parseContext
}
