import { Cache, Random, getShortTimeout } from '../utilities/utilities';

import { HttpRequest, HttpStatusCodes, Accept } from '../utilities/api';
import { AgencyData } from './data/agency';
import { faker } from '@faker-js/faker';
import {
  ICreateCompany,
  IUserAgency,
  IUser,
  IPassenger,
  IPaymentMethod
} from '../interface/company';
const shortTimeout = getShortTimeout();
/**
 * Create a new company
 * @param authToken
 * @returns {ICreateCompany} Information
 */
export function createCompany(authToken: string): Cypress.Chainable<ICreateCompany> {
  const randomId = Random.randomString(8);
  const companyDomain = randomId + '.staging.tripactions.xyz';

  return cy
    .request({
      method: HttpRequest.Post,
      url: 'api/superAdmin/companies',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: {
        name: 'TAAutomation-CYPRESS-' + randomId,
        domain: companyDomain,
        status: 'ACTIVE'
      }
    })
    .then(({ body, status }) => {
      expect(status, 'Successfully created company').to.equal(HttpStatusCodes.Created);
      return body;
    });
}

/**
 * Add predefine user agency for given company
 * @param authToken
 * @param companyUuid
 * @param companyDomain
 * @returns {IUserAgency} Information
 */
export function addUserAgency(
  authToken: string,
  companyUuid: string,
  companyDomain: string
): Cypress.Chainable<IUserAgency> {
  const agencyData = AgencyData.generateUserAgencyData(
    companyUuid,
    companyDomain.split('.')[0]
  );

  return cy
    .request({
      method: HttpRequest.Put,
      url: 'api/travelops/v1/companies/{companyUuid}/agencyAssociations/{agencyUuid}'
        .replace('{companyUuid}', companyUuid)
        .replace('{agencyUuid}', agencyData.agency.uuid),
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: agencyData
    })
    .then(({ body, status }) => {
      expect(status, 'Successfully updated company with agency ').to.equal(
        HttpStatusCodes.Ok
      );
      return body;
    });
}

/**
 * Verify newly generated email does not exists
 * @param authToken
 * @param companyId
 * @returns User email
 */
export function isUserRegistered(
  authToken: string,
  companyId: string
): Cypress.Chainable<string> {
  const email =
    'user_' + Random.randomString(8) + '@' + `${companyId}` + `.staging.tripactions.xyz`;

  return cy
    .request({
      method: HttpRequest.Get,
      url: 'api/uaa/emailinfo?email=',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      qs: { email }
    })
    .then(({ status }) => {
      expect(status, 'User is not registered').to.equal(HttpStatusCodes.Ok);
      return email;
    });
}

/**
 * Send pre signup email
 * @param authToken
 * @param email
 * @returns User email
 */
export function sendPreSignupEmail(
  authToken: string,
  email: string
): Cypress.Chainable<string> {
  return cy
    .request({
      method: HttpRequest.Post,
      url: 'api/preSignup?email=',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      qs: { email }
    })
    .then(({ status }) => {
      expect(status, 'Sign up email triggered').to.equal(HttpStatusCodes.Accepted);
      return email;
    });
}

/**
 * Get signup token
 * @param authToken
 * @param email
 * @returns User email and Signup token
 */
export function getSignUpToken(
  authToken: string,
  email: string
): Cypress.Chainable<{ email: string; body: string }> {
  return cy
    .request({
      method: HttpRequest.Get,
      url: 'api/superAdmin/signupToken',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      qs: { email }
    })
    .then(({ body, status }) => {
      expect(status, 'Signup token retrieved').to.equal(HttpStatusCodes.Ok);
      return { email, body };
    });
}

/**
 * Create user account
 * @param authToken
 * @param email
 * @param token
 * @param password
 * @returns User email
 */
export function createAccount(
  authToken: string,
  email: string,
  token: string,
  password: string
): Cypress.Chainable<string> {
  return cy
    .request({
      method: HttpRequest.Post,
      url: 'api/signup',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: {
        emailVerificationToken: token,
        email: email,
        password: password,
        familyName: faker.name.lastName().replace(/'/g, ''),
        givenName: faker.name.firstName()
      }
    })
    .then(({ status }) => {
      expect(status, 'Account created').to.equal(HttpStatusCodes.Created);
      return email;
    });
}

/**
 * Retrieve user by email
 * @param authToken
 * @param email
 * @param current_attempt
 * @returns {IUser} Information
 */
export function retrieveUserByEmail(
  authToken: string,
  email: string,
  current_attempt = 1
): Cypress.Chainable<IUser> {
  cy.wait(shortTimeout); // Wait until newly created user to sync

  return cy
    .request({
      method: HttpRequest.Get,
      url: 'api/superAdmin/users',
      headers: { Authorization: authToken, Accept: Accept.ApplicationJson },
      qs: { email },
      retryOnStatusCodeFailure: true
    })
    .then(({ body, status }) => {
      expect(status, 'Retrieve user by email').to.equal(HttpStatusCodes.Ok);
      const response: IUser[] = body._embedded.users;
      const userResponse = response.find(function (user: any) {
        return user.email === email.toLowerCase();
      });
      if (userResponse === undefined) {
        if (current_attempt >= 3) {
          throw new Error('Cannot find newly created user after 3 attempts');
        } else {
          return retrieveUserByEmail(authToken, email, ++current_attempt);
        }
      }
      return cy.then(() => {
        Cache.putDataInCache('userResponse', userResponse);
        return userResponse;
      });
    });
}

/**
 * Set user by type
 * @param authToken
 * @param userUuid
 * @param userType
 * @returns {IUser} Information
 */
export function setUserType(
  authToken: string,
  userUuid: string,
  userType: string
): Cypress.Chainable<IUser> {
  const params: { [key: string]: boolean } = {};
  params[userType] = true;
  return cy
    .request({
      method: HttpRequest.Patch,
      url: `api/superAdmin/users/${userUuid}`,
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: params
    })
    .then(({ body, status }) => {
      expect(status, 'Set user type as ' + userType.toString()).to.equal(
        HttpStatusCodes.Ok
      );
      return body;
    });
}

/**
 * Set travel frequency for given user token
 * @param authToken
 * @param travelFrequency
 * @returns Cypress response
 */
export function setTravelFrequency(
  authToken: string,
  travelFrequency = '1-2 times a month'
): Cypress.Chainable<Cypress.Response<any>> {
  return cy
    .request({
      method: HttpRequest.Patch,
      url: 'api/user/profile',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: {
        travelFrequency: travelFrequency
      }
    })
    .then(({ status }) => {
      expect(status, 'Updated travel frequency').to.equal(HttpStatusCodes.Ok);
    });
}

/**
 * Add phone number for given user token
 * @param authToken
 * @param phoneNumber
 * @returns {IUser} Information
 */
export function addPhoneNumber(
  authToken: string,
  phoneNumber = '+14157483333'
): Cypress.Chainable<IUser> {
  return cy
    .request({
      method: HttpRequest.Patch,
      url: 'api/uaa/userinfo',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: {
        phone_number: phoneNumber
      }
    })
    .then(({ body, status }) => {
      expect(status, 'Updated travel phone number').to.equal(HttpStatusCodes.Ok);
      return body;
    });
}

/**
 * Complete onboarding for given user token
 * @param authToken
 * @returns {IUser} Information
 */
export function completeOnboarding(authToken: string): Cypress.Chainable<IUser> {
  return cy
    .request({
      method: HttpRequest.Patch,
      url: 'api/uaa/userinfo',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: {
        onboardingCompleted: true
      }
    })
    .then(({ body, status }) => {
      expect(status, 'Set user to onboarding to complete').to.equal(HttpStatusCodes.Ok);
      return body;
    });
}

/**
 * Update default passenger information for given user token
 * @param authToken
 * @returns {IPassenger} Information
 */
export function updatePassengerInfoDefault(
  authToken: string
): Cypress.Chainable<IPassenger> {
  return cy
    .request({
      method: HttpRequest.Put,
      url: 'api/user/passenger',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: {
        givenName: faker.name.firstName(),
        familyName: faker.name.lastName().replace(/'/g, ''),
        passport: {
          countryOfCitizenship: 'US',
          number: '31195855',
          countryOfIssue: 'US',
          issuedOn: '2021-12-12',
          expiresOn: '2031-12-12'
        },
        birthdate: '1990-12-12',
        gender: 'X',
        secondaryGender: 'MALE'
      }
    })
    .then(({ body, status }) => {
      expect(status, 'Update passenger default information').to.equal(HttpStatusCodes.Ok);
      return body;
    });
}

/**
 * Add credit card personal payment method for given user token
 * @param authToken
 * @returns {IPaymentMethod} Credit card payment method
 */
export function addPersonalPaymentMethod(
  authToken: string
): Cypress.Chainable<IPaymentMethod> {
  return cy
    .request({
      method: HttpRequest.Post,
      url: 'api/user/paymentMethods',
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      body: {
        billingAddress: {
          addressLine1: 'Strawinskylaan',
          addressLine2: '4117',
          state: 'AL',
          city: 'Amsterdam',
          country: 'NL',
          postalCode: '1337AM'
        },
        creditCard: {
          fullName: 'Personal payment',
          givenName: faker.name.firstName(),
          familyName: faker.name.lastName().replace(/'/g, ''),
          number: '4242424242424242',
          expirationMonth: '1',
          expirationYear: '2027',
          cvc: '123'
        },
        nickname: 'Corporate Visa Card'
      }
    })
    .then(({ body, status }) => {
      expect(status, 'Payment method updated').to.equal(HttpStatusCodes.Created);
      return body;
    });
}
