import * as jwt from 'jsonwebtoken';
import * as request from 'request';
import { URLSearchParams } from 'url';

export type TaiHuTokenInfo = {
  access_token: string;
  id_token: string;
  expires_in: number;
  token_type: 'Bearer';
  scope: string;
};

export type TaiHuUserInfo = {
  user_id: string;
  sub: string;
  name: string;
  preferred_username: string;
  email: string;
  email_verified: boolean;
  phone_number_verified: boolean;
};

export type GAdminUserInfo = Pick<
  TaiHuUserInfo,
  'user_id' | 'sub' | 'name' | 'email'
>;
export const GAdminTokenName = 'gadmin-token';

// see https://docs.qq.com/doc/DSEN0SlhZWWFJSHR4
export default class TaiHuId {
  static config = {
    clientId: process.env.TAIHU_ODC_CLIENT_ID,
    clientSecret: process.env.TAIHU_ODC_APP_TOKEN || 'gadmin-default-secret',
    apiBase: process.env.TAIHU_BASE,
    callback: process.env.TAIHU_CALLBACK,
    logout: process.env.TAIHU_LOGOUT,
  };

  static callApi<T>(
    url: string,
    params?: Record<string, any>,
    options?: Record<string, any>,
  ): Promise<T> {
    return new Promise((resolve, reject) => {
      request(
        {
          url: `${TaiHuId.config.apiBase}${url}`,
          method: 'POST',
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            ...options?.headers,
          },
          form: params,
          ...options,
        },
        (error: Error, _response: unknown, body: string) => {
          if (error) {
            reject(error);
            return;
          }
          try {
            const parsedBody = JSON.parse(body);
            if (parsedBody.error) {
              reject(new Error(parsedBody.error_description));
              return;
            }
            resolve(parsedBody as T);
          } catch (parseError) {
            reject(new Error('Failed to parse response body as JSON'));
          }
        },
      );
    });
  }

  static getToken(code: string) {
    return TaiHuId.callApi<TaiHuTokenInfo>('/token', {
      client_id: TaiHuId.config.clientId,
      client_secret: TaiHuId.config.clientSecret,
      grant_type: 'authorization_code',
      redirect_uri: TaiHuId.config.callback,
      code,
    });
  }

  static refreshToken(refreshToken: string) {
    return TaiHuId.callApi<TaiHuTokenInfo>('/token', {
      client_id: TaiHuId.config.clientId,
      client_secret: TaiHuId.config.clientSecret,
      grant_type: 'refresh_token',
      refresh_token: refreshToken,
    });
  }

  // get userinfo by api
  static async getUserInfo(accessToken: string) {
    const user_info = await TaiHuId.callApi<any>('/userinfo', undefined, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
    const { user_name } = user_info;
    return {
      user_id: user_name,
      sub: user_name,
      name: user_name,
      email: `${user_name}@tencent.com`,
    };
  }

  static authorize(redirectUrl: string, uiLocales = 'zh-CN') {
    const map = new Map();
    map.set('response_type', 'code');
    map.set('client_id', TaiHuId.config.clientId);
    map.set('ui_locales', uiLocales);
    map.set('scope', 'openid profile');
    map.set('redirect_uri', TaiHuId.config.callback);
    map.set('state', redirectUrl);
    const params = new URLSearchParams(map);
    return `${TaiHuId.config.apiBase}/authorize?${params.toString()}`;
  }

  static logout(redirectUrl: string) {
    const map = new Map();
    map.set('url', redirectUrl);
    const params = new URLSearchParams(map);
    return `${TaiHuId.config.logout}?${params.toString()}`;
  }

  /**
   * 生成JWT
   * @param payload 用户信息
   * @param expiresIn 过期时间，单位秒
   * @returns token
   */
  static sign(payload: GAdminUserInfo, expiresIn = 1800) {
    return new Promise<string>((resolve, reject) => {
      jwt.sign(
        payload,
        TaiHuId.config.clientSecret,
        { expiresIn },
        (err, token) => {
          if (err) {
            reject(err);
            return;
          }
          resolve(token as string);
        },
      );
    });
  }

  /**
   * 验证并返回sign生成的JWT信息
   * @param token JWT
   * @returns 用户信息
   */
  static verify(token: string) {
    return new Promise<GAdminUserInfo>((resolve, reject) => {
      if (!token) {
        reject(new Error('no token'));
        return;
      }

      jwt.verify(token, TaiHuId.config.clientSecret, {}, (err, decoded) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(decoded as GAdminUserInfo);
      });
    });
  }
}
