/**
 * Account information PNZ-API-CentreLib
 *
 * This file was automatically generated by APIMATIC v3.0 ( https://www.apimatic.io ).
 */

import { ApiResponse, RequestOptions } from '../core';
import {
  ContentType2Enum,
  contentType2EnumSchema,
} from '../models/contentType2Enum';
import {
  ContentTypeEnum,
  contentTypeEnumSchema,
} from '../models/contentTypeEnum';
import {
  HFAC8Exchangeauthorizationcodeforaccesstoken,
  hFAC8ExchangeauthorizationcodeforaccesstokenSchema,
} from '../models/hFAC8Exchangeauthorizationcodeforaccesstoken';
import { string } from '../schema';
import { ClientAssertionJWT } from '../utils';
import { BaseController } from './baseController';
import {v4 as uuidv4} from 'uuid';
import request from 'request';

export class AuthorizeConsentController extends BaseController {
  /**
   * Initiate Authorization Code Grant for an Account Access Consent
   *
   * @param clientID
   * @param redirectUri
   * @param privateKey
   * @param KID
   * @param consentID
   * @return Response from the API call
   */
  initiateAuthorizationCodeGrant(
    clientID: string,
    redirectUri: string,
    privateKey: string,
    KID: string,
    consentID: string
  ): Promise<string> {
    const scope = 'openid accounts';
    const responseType = 'code id_token';
    const state = uuidv4();
    const nonce = uuidv4();
    const payload = {
      iss: clientID,
      client_id: clientID,
      response_type: 'code id_token',
      scope: 'openid accounts',
      redirect_uri: redirectUri,
      aud: 'https://api-nomatls.apicentre.middleware.co.nz/oauth/v2.0',
      state: state,
      nonce: nonce,
      iat: Math.floor(Date.now() / 1000) - 120,
      exp: Math.floor(Date.now() / 1000) + 3599,
      claims: {
        id_token: {
          ConsentId: {
            value: consentID,
            essential: true,
          },
          acr: {
            value: 'urn:openbanking:nz:ca',
            essential: true,
          },
        },
      },
    };
    const clientAssertion = ClientAssertionJWT(privateKey, KID, payload);

    const options = {
      method: 'GET',
      url: `https://api-nomatls.apicentre.middleware.co.nz/oauth/v2.0/authorize?scope=${scope}&response_type=${responseType}&client_id=${clientID}&redirect_uri=${redirectUri}&state=${state}&nonce=${nonce}&request=${clientAssertion}`,
      followRedirect: false, // to get the redirected URL from the Node.js request module
    };

    return new Promise(function(resolve, reject) {
      request(options, function(error, response) {
        if (error) {
          reject(error);
          return error;
        }
        resolve(response.headers.location as string);
      });
    });
  }

  /**
   * This request is following the redirect which was returned in the previous step. The redirect is to
   * the Bank's login page. In the browser, this redirect happens automatically, and your application
   * won't need to worry about this. However, we must explicitly follow the redirect because we need to
   * mimic the user logging in and consenting.
   *
   * Preconditions:
   *
   * *   This step relies on the presence of a PNZ-T-AC-LoginAppLocation environment variable, which is
   * set upon successful completion of Step HF-AC3 - Initiate authorization code grant for an account
   * access consent.
   *
   *
   * Response:
   *
   * *   The response should be a Middleware Bank login screen.
   * *   This response also returns the OB_AUTHSESSION cookie that is used throughout the login and
   * consent flows in Steps HF-AC5 through HF-AC7.
   *
   *
   * Postconditions:
   *
   * *   The returned request JWT and redirect location are put in the PNZ-T-AC-ObaRequest and PNZ-T-AC-
   * LoginApp environment variables, respectively, in preparation for Step HF-AC5 - Authenticate customer
   * to Middleware Bank.
   *
   *
   * Examples:
   *
   * *   There is one example which shows the request and the expected response.
   *
   *
   * Troubleshooting:
   *
   * *   The most likely cause of errors is that the step is being attempted out of sequence. Be sure to
   * follow the steps in sequence.
   * *   Another likely cause of errors are out of date variable values. Try starting from Step AC1 -
   * Initiate client credentials grant again.
   *
   * @param contentType
   * @param obaRequest
   * @return Response from the API call
   */
  async followRedirectToMiddlewareBankSLoginPage(
    contentType: ContentType2Enum,
    obaRequest: string,
    requestOptions?: RequestOptions
  ): Promise<ApiResponse<string>> {
    const req = this.createRequest('GET', '/auth/login');
    req.baseUrl('server 2');
    const mapped = req.prepareArgs({
      contentType: [contentType, contentType2EnumSchema],
      obaRequest: [obaRequest, string()],
    });
    req.header('Content-Type', mapped.contentType);
    req.query('oba_request', mapped.obaRequest);
    req.authenticate(false);
    return req.callAsText(requestOptions);
  }

  /**
   * This step mimics the user authenticating at Middleware Bank. Your application doesn't need to worry
   * about this UI because it is provided by the API Provider.
   *
   * Preconditions:
   *
   * *   The user who authenticates is defined by the environment variable PNZ-E-Username. The default
   * user is 'user01'. If you want to authenticate 'user02', then you'll need to change the PNZ-E-
   * Username environment variable to 'user02'.
   * *   This step relies on the presence of the PNZ-T-AC-ObaRequest environment variable, which is set
   * upon successful completion of Step HF-AC4 - Follow redirect to Middleware Bank's login page.
   * *   This step also relies on the OB_AUTHSESSION cookie that is created in step HF-AC4 - Follow
   * redirect to Middleware Bank's login page.
   *
   *
   * Response:
   *
   * *   The response should be a redirect to the Middleware Bank consent page.
   *
   *
   * Postconditions:
   *
   * *   The returned redirect URI is saved in the PNZ-T-AC-ConsentLocation environment variable in
   * preparation for Step HF-AC6 - Follow redirect to Middleware Bank's consent page.
   *
   *
   * Examples:
   *
   * *   There is one example which shows the request and the expected response.
   *
   *
   * Troubleshooting:
   *
   * *   The most likely cause of errors is that the step is being attempted out of sequence. Be sure to
   * follow the steps in sequence.
   * *   Another likely cause of errors are out of date variable values. Try starting from Step AC1 -
   * Initiate client credentials grant again.
   * *   Another cause may be an incorrect username. The only valid usernames are 'user01' and 'user02'.
   * These user names are associated to the two test customers we create in the Sandbox test data set for
   * you.
   *
   * @param contentType
   * @param username
   * @param password
   * @param obaRequest
   * @return Response from the API call
   */
  async authenticateCustomerToMiddlewareBank(
    contentType: ContentTypeEnum,
    username: string,
    password: string,
    obaRequest: string,
    requestOptions?: RequestOptions
  ): Promise<ApiResponse<void>> {
    const req = this.createRequest('POST', '/mwnz-bank/v2.0/login');
    req.baseUrl('server 2');
    const mapped = req.prepareArgs({
      contentType: [contentType, contentTypeEnumSchema],
      username: [username, string()],
      password: [password, string()],
      obaRequest: [obaRequest, string()],
    });
    req.header('Content-Type', mapped.contentType);
    req.form({
      username: mapped.username,
      password: mapped.password,
      oba_request: mapped.obaRequest,
    });
    req.authenticate(false);
    return req.call(requestOptions);
  }

  /**
   * This step is following the redirect which was returned in the previous step. In the browser, this
   * redirect happens automatically, but we must explicitly follow the redirect.
   *
   * Preconditions:
   *
   * *   This step relies on the presence of a PNZ-T-AC-ConsentLocation environment variable, which is
   * set upon successful completion of Step HF-AC5 - Authenticate customer to Middleware Bank.
   * *   This step also relies on the OB_AUTHSESSION cookie that is created in step HF-AC4 - Follow
   * redirect to Middleware Bank's login page.
   *
   *
   * Response:
   *
   * *   The response should be a Middleware Bank consent screen.
   *
   *
   * Postconditions:
   *
   * *   The list of accounts for which access will be consented is put in the PNZ-T-AC-ConsentedAccounts
   * environment variable, and the Consent App URL from the action of the form is put into the PNZ-T-AC-
   * Consent environment variable in preparation for Step HF-AC7 - Post consented accounts array to
   * complete consent.
   *
   *
   * Examples:
   *
   * *   There is one example which shows the request and the expected response.
   *
   *
   * Troublshooting:
   *
   * *   The most likely cause of errors is that the step is being attempted out of sequence. Be sure to
   * follow the steps in sequence.
   * *   Another likely cause of errors are out of date variable values. Try starting from Step AC1 -
   * Initiate client credentials grant again.
   *
   * @param contentType
   * @param obaRequest
   * @param cookie
   * @return Response from the API call
   */
  async followRedirectToMiddlewareBankSConsentPage(
    contentType: ContentTypeEnum,
    obaRequest: string,
    cookie: string,
    requestOptions?: RequestOptions
  ): Promise<ApiResponse<string>> {
    const req = this.createRequest('GET', '/auth/consent');
    req.baseUrl('server 2');
    const mapped = req.prepareArgs({
      contentType: [contentType, contentTypeEnumSchema],
      obaRequest: [obaRequest, string()],
      cookie: [cookie, string()],
    });
    req.header('Content-Type', mapped.contentType);
    req.header('Cookie', mapped.cookie);
    req.query('oba_request', mapped.obaRequest);
    req.authenticate(false);
    return req.callAsText(requestOptions);
  }

  /**
   * HF AC 7 new
   *
   * @param cookie
   * @param body
   * @return Response from the API call
   */
  async postConsentedAccountsArray(
    cookie: string,
    body: string,
    requestOptions?: RequestOptions
  ): Promise<ApiResponse<void>> {
    const req = this.createRequest('POST', '/mwnz-bank/v2.0/consent');
    req.baseUrl('server 2');
    const mapped = req.prepareArgs({
      cookie: [cookie, string()],
      body: [body, string()],
    });
    req.header('Cookie', mapped.cookie);
    req.header('Content-Type', 'application/x-www-form-urlencoded');
    req.text(mapped.body);
    req.authenticate(false);
    return req.call(requestOptions);
  }

  /**
   * @param clientID
   * @param privateKey
   * @param KID
   * @param redirectUri
   * @param code
   * @return Response from the API call
   */
  async exchangeAuthorizationCode(
    clientID: string,
    privateKey: string,
    KID: string,
    redirectUri: string,
    code: string,
    requestOptions?: RequestOptions
  ): Promise<ApiResponse<HFAC8Exchangeauthorizationcodeforaccesstoken>> {
    const req = this.createRequest('POST', '/oauth/v2.0/token');
    req.baseUrl('server 1');
    const grantType = 'authorization_code';
    const clientAssertionType =
      'urn:ietf:params:oauth:client-assertion-type:jwt-bearer';
    const jti = '9OyL2boCICsjI4wM4CKTlw';
    const payload: {
      sub: string;
      aud: string;
      iss: string;
      exp: number;
      iat: number;
      jti: string;
    } = {
      sub: clientID,
      aud: 'https://api-nomatls.apicentre.middleware.co.nz/oauth/v2.0/token',
      iss: clientID,
      exp: Math.floor(Date.now() / 1000) + 3599,
      iat: Math.floor(Date.now() / 1000) - 120,
      jti: jti,
    };
    const clientAssertion = ClientAssertionJWT(privateKey, KID, payload);
    const mapped = req.prepareArgs({
      grantType: [grantType, string()],
      code: [code, string()],
      redirectUri: [redirectUri, string()],
      clientAssertion: [clientAssertion, string()],
      clientAssertionType: [clientAssertionType, string()],
    });
    req.header('Content-Type', 'application/x-www-form-urlencoded');
    req.form({
      grant_type: mapped.grantType,
      code: mapped.code,
      redirect_uri: mapped.redirectUri,
      client_assertion: mapped.clientAssertion,
      client_assertion_type: mapped.clientAssertionType,
    });
    req.authenticate(false);
    return req.callAsJson(
      hFAC8ExchangeauthorizationcodeforaccesstokenSchema,
      requestOptions
    );
  }
}
