import { Dates } from '../utilities/utilities';
import { HttpRequest, HttpStatusCodes, Accept } from '../utilities/api';
import {
  IHotelLocation,
  IHotelSearch,
  IHotelSearchOptions,
  IHotelRates,
  IContract,
  IPayment
} from '../interface/booking';

/**
 * Get hotel location  information
 * @param authToken
 * @param location
 * @returns {IHotelLocation} Information
 */
export function getHotelLocation(
  authToken: string,
  location = 'New York, NY, USA'
): Cypress.Chainable<IHotelLocation> {
  return cy
    .request({
      method: HttpRequest.Get,
      url: 'api/user/autocomplete/place?input={placeInput}&includeFavoriteHotels=true&addLocation=true'.replace(
        '{placeInput}',
        location
      ),
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      qs: {
        addLocation: true,
        includeFavoriteHotels: true
      }
    })
    .then(({ body, status }) => {
      expect(status, 'location results retrieved').to.equal(HttpStatusCodes.Ok);
      return body.predictions[0];
    });
}

/**
 * Hotel search with place Id
 * @param authToken
 * @param placeId
 * @param userId
 * @param description
 * @param checkInDate
 * @param checkOutDate
 * @returns {IHotelSearch} Information
 */
export function hotelSearchWithPlaceId(
  authToken: string,
  placeId: string,
  userId: string,
  description = 'NY Hotel Booking',
  checkInDate: string = Dates.getFutureDate(20),
  checkOutDate: string = Dates.getFutureDate(21)
): Cypress.Chainable<IHotelSearch> {
  return cy
    .request({
      method: HttpRequest.Get,
      url: 'api/v1/trip/hotel/searches',
      headers: {
        Authorization: authToken,
        ContentType: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true,
      qs: {
        includeSpecialRates: true,
        travelerUuids: userId,
        prefetchRooms: true,
        emptyData: false,
        placeId: placeId,
        description: description,
        checkInDate: checkInDate,
        checkOutDate: checkOutDate,
        numberOfRooms: 1,
        numberOfGuests: 1
      }
    })
    .then(({ body, status }) => {
      expect(status, 'search with placeId results retrieved').to.equal(
        HttpStatusCodes.Ok
      );
      return body;
    });
}

/**
 * Hotel search with search Id
 * @param authToken
 * @param searchId
 * @returns {IHotelSearchOptions} Information
 */
export function hotelSearchesWithSearchId(
  authToken: string,
  searchId: string
): Cypress.Chainable<IHotelSearchOptions> {
  return cy
    .request({
      method: HttpRequest.Get,
      url: 'api/v1/trip/hotel/searches' + `/${searchId}`,
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true
    })
    .then(({ body, status }) => {
      expect(status, 'search with searchId results retrieved').to.equal(
        HttpStatusCodes.Ok
      );
      return body;
    });
}

/**
 * Get hotel rates
 * @param authToken
 * @param searchId
 * @param optionsUuid
 * @returns {IHotelRates} Information
 */
export function getHotelRates(
  authToken: string,
  searchId: string,
  optionsUuid: string
): Cypress.Chainable<IHotelRates> {
  return cy
    .request({
      method: HttpRequest.Get,
      url: 'api/v1/trip/hotel/searches' + `/${searchId}/hotels/${optionsUuid}/rates`,
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true
    })
    .then(({ body, status }) => {
      expect(status, 'hotel rates retrieved').to.equal(HttpStatusCodes.Ok);
      return body;
    });
}

/**
 * Create a contract
 * @param authToken
 * @param linksRatesHref
 * @param roomUuid
 * @returns {IContract} Information
 */
export function contract(
  authToken: string,
  linksRatesHref: string,
  roomUuid: string
): Cypress.Chainable<IContract> {
  const contractUrl = String(linksRatesHref).replace(
    'rates',
    `rooms/${roomUuid}/contract`
  );

  return cy
    .request({
      method: HttpRequest.Post,
      url: contractUrl,
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true
    })
    .then(({ body, status }) => {
      expect(status, 'hotel contract created').to.equal(HttpStatusCodes.Ok);
      return body;
    });
}

/**
 * Get payment method information
 * @param authToken
 * @param contractId
 * @returns {IPayment} Information
 */
export function getPaymentMethod(
  authToken: string,
  contractId: string
): Cypress.Chainable<IPayment> {
  const paymentUrl =
    'api/v2/user/paymentMethods?type=HOTEL&personal=false&' +
    'forGuestInvitation=false&tripInvitationUuid=&contractUuid={contractId}&bookingUuid='.replace(
      '{contractId}',
      contractId
    );

  return cy
    .request({
      method: HttpRequest.Get,
      url: paymentUrl,
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      retryOnStatusCodeFailure: true
    })
    .then(({ body, status }) => {
      expect(status, 'payment method retrieved').to.equal(HttpStatusCodes.Ok);
      return body;
    });
}

/**
 * Complete booking
 * @param authToken
 * @param searchId
 * @param paymentId
 * @param contractId
 * @param userId
 * @param tripName
 * @param reason
 * @param tripId
 * @returns Trip actions booking ID
 */
export function book(
  authToken: string,
  searchId: string,
  paymentId: string,
  contractId: string,
  userId: string,
  tripName = 'NY Hotel Booking',
  reason = 'Automation',
  tripId = 'NEW'
): Cypress.Chainable<string> {
  const bookingUrl =
    'api/v1/trip/hotel/searches' +
    `/${searchId}/contracts/${contractId}/book/streaming?paymentMethodUuid=${paymentId}&tripId=${tripId}&tripName=${tripName}&reason=${reason}&locale=en-US`;

  return cy
    .request({
      method: HttpRequest.Post,
      url: bookingUrl,
      headers: {
        Authorization: authToken,
        Accept: Accept.ApplicationJson
      },
      body: {
        passengerData: [
          {
            passenger: {
              uuid: userId
            }
          }
        ]
      },
      retryOnStatusCodeFailure: true
    })
    .then(({ body, status }) => {
      expect(status, 'hotel booking created').to.equal(HttpStatusCodes.Ok);
      const regex = /"bookingId" : "(.*)"/;
      return body.match(regex)[1];
    });
}
