const joi = require('joi');
const url = require('url');
const querystring = require('querystring');
const got = require('got');
const config = require('src/config/index.js');
const { jwt } = require('src/utils/pkgs.js');

const mod = {};
module.exports = mod;

/**
 * @return {string}
 */
mod.getAuthURL = state => {
  const input = joi
    .object({
      authorized_endpoint: joi.string().required(),
      token_endpoint: joi.string().required(),
      client_id: joi.string().required(),
      client_secret: joi.string().required(),
      state: joi.string().allow('', null).default(''),
    })
    .unknown()
    .validate({ ...config.sso, state });
  if (input.error) throw new Error(input.error.message);

  /**
   * @type {{value: { authorized_endpoint: string, token_endpoint: string, client_id: string, client_secret: string, state: string }}}
   */
  const { value } = input;

  const redirectUri = new url.URL('/oauth/redirect', config.server.url);

  const qs = {
    client_id: value.client_id,
    scope: 'openid',
    response_type: 'code',
    redirect_uri: redirectUri.toString(),
  };
  if (value.state) qs.state = state;

  return `${value.authorized_endpoint}?${querystring.stringify(qs)}`;
};

mod.getLogoutURL = () => {
  const input = joi
    .object({
      logout_endpoint: joi.string().required(),
    })
    .unknown()
    .validate({ ...config.sso });
  if (input.error) throw new Error(input.error.message);
  const redirectUri = new url.URL('/oauth/redirect', config.server.url);

  const qs = { state: 'logout', redirect_uri: redirectUri.toString() };

  return `${input.value.logout_endpoint}?${querystring.stringify(qs)}`;
};

/**
 * @typedef SSOAccount
 * @property {string} username
 * @property {string} display_name
 * @property {string} email
 */

mod.getToken = async (code, state) => {
  const input = joi
    .object({
      authorized_endpoint: joi.string().required(),
      token_endpoint: joi.string().required(),
      client_id: joi.string().required(),
      client_secret: joi.string().required(),
      state: joi.string().required(),
      code: joi.string().required(),
    })
    .unknown()
    .validate({ ...config.sso, state, code });

  if (input.error) throw new Error(input.error.message);

  /**
   * @type {{value: { authorized_endpoint: string, token_endpoint: string, client_id: string, client_secret: string, state: string, code: string }}}
   */
  const { value } = input;

  const redirectUri = new url.URL('/oauth/redirect', config.server.url);

  const qs = {
    client_id: value.client_id,
    client_secret: value.client_secret,
    redirect_uri: redirectUri.toString(),
    code: value.code,
    client_session_state: value.state,
    grant_type: 'authorization_code',
  };

  const resp = await got.default.post(value.token_endpoint, {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: querystring.stringify(qs),
    responseType: 'json',
  });

  const { body } = resp;
  if (!body) throw new Error('resopnse body empty');

  const { id_token: idToken, access_token: accessToken } = body;
  if (!idToken) throw new Error('get id token fail');

  const decoded = jwt.decode(idToken);
  if (!decoded || typeof decoded !== 'object') throw new Error('jwt decode fail');
  console.log(decoded)
  console.log(body)
  // @ts-ignore
  const { preferred_username: preferredUsername } = decoded;
  if (!preferredUsername) throw new Error('id token field missing');

  const displayName = `${decoded.family_name ?? ''}${decoded.given_name ?? ''}`;

  /** @type {SSOAccount} */
  const ssoAccount = {
    username: preferredUsername.toLowerCase(),
    display_name: displayName ?? preferredUsername,
    email: decoded.email ?? '',
  };

  return ssoAccount;
};
