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

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

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

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

// see https://docs.qq.com/doc/DSEN0SlhZWWFJSHR4
export default class YuFuId {
  static config = {
    clientId: process.env.YUFU_CLIENT_ID,
    clientSecret: process.env.YUFU_CLIENT_SECRET || 'gadmin-default-secret',
    apiBase: process.env.YUFU_BASE,
    callback: process.env.YUFU_CALLBACK,
    logout: process.env.YUFU_LOGOUT,
  };

  static callApi<T>(
    url: string,
    params?: Record<string, any>,
    options?: Record<string, any>,
  ): Promise<T> {
    return new Promise((resolve, reject) => {
      request(
        {
          url: `${YuFuId.config.apiBase}${url}`,
          method: 'POST',
          json: true,
          body: params,
          ...options,
        },
        (error: Error, _response: unknown, body: Record<string, any>) => {
          if (error) {
            reject(error);
            return;
          }
          if (body.error) {
            reject(new Error(body.error_description));
            return;
          }
          resolve(body as T);
        },
      );
    });
  }

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

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

  // get userinfo by api
  static getUserInfo(accessToken: string) {
    return YuFuId.callApi<YuFuUserInfo>('/userinfo', undefined, {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });
  }

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

  static logout(redirectUrl: string) {
    const map = new Map();
    map.set('return_to', redirectUrl);
    const params = new URLSearchParams(map);
    return `${YuFuId.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,
        YuFuId.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, YuFuId.config.clientSecret, {}, (err, decoded) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(decoded as GAdminUserInfo);
      });
    });
  }
}
