import { Logs, Timeouts, utilitySelector, Utility, DaysToMove } from '@utility-lib/index';
import { FlightBookingParams } from '@flight-modal/index';
import {
  HotelSearchResults,
  hotelSearchResultSelector,
  HotelCheckout
} from '@hotel-lib/index';
import { ChatBox, chatBookingSelectors } from './chat';
import { FlightCheckout } from '@flight-lib/index';

const chatbotBookingSelectors = {
  locationInput: 'input[data-testid="Form__groupItem"]',
  getText: '[data-testid="DynamicList__AirportItem"]',
  wrapperButton: '[class*="messageAnim-enter-done"] button'
};

export enum Directions {
  TO = 'To',
  FROM = 'From',
  HOTEL_LOCATION = 'Hotel location',
  DEPARTING_FROM = 'Departing from',
  ARRIVING_AT = 'Arriving at',
  PICKUP = 'Pick-up',
  DROPOFF = 'Drop-off'
}

const options: Intl.DateTimeFormatOptions = {
  weekday: 'short',
  month: 'short',
  day: 'numeric',
  year: 'numeric'
};

export type FlightBookingData = {
  flightBookingParams: FlightBookingParams;
  departingFrom: Directions;
  arrivingAt: Directions;
  isChangeFlight?: boolean;
};

export class ChatBooking {
  static bookTripFlight(
    flightBookingData: FlightBookingData,
    daysToMove: DaysToMove,
    numberOfFlights = 2
  ) {
    const {
      flightBookingParams,
      departingFrom,
      arrivingAt,
      isChangeFlight = false
    } = flightBookingData;

    switch (numberOfFlights) {
      case 1:
        ChatBox.replyToAva('One-way');
        break;
      case 2:
        ChatBox.replyToAva('Roundtrip');
        break;
    }

    cy.allure().logStep('Book round trip flight');
    ChatBooking.selectAirport(departingFrom, flightBookingParams.locations[0]);
    ChatBooking.selectAirport(arrivingAt, flightBookingParams.locations[1]);

    if (!isChangeFlight) {
      this.selectDates(daysToMove);
    }

    this.clickLetsGoButton(false);
    cy.wait(['@waitForGetFlightResults', '@waitForSentMessage']);
    cy.wait(Timeouts.MAX_TIMEOUT_120_SEC.timeout);
    ChatBox.waitUntilAvaStopTyping();
    this.clickLetsGoButton(true);
    ChatBooking.selectTripFlight(numberOfFlights);
    this.clickLetsGoButton(true);
    cy.get(chatBookingSelectors.getChatMsgBody).then(($element) => {
      if (
        $element.text().includes(`I couldn't find any results that match your search.`)
      ) {
        cy.allure().logStep(`I couldn't find any results that match your search.`);
        cy.wait(Timeouts.MEDIUM_TIMEOUT_30_SEC.timeout);
        cy.get(chatBookingSelectors.letsGoButton).as('letsGoButton');
        cy.get('@letsGoButton').should('be.enabled');
        cy.get('@letsGoButton').scrollIntoView();
        cy.get('@letsGoButton').click({ force: true });
        ChatBooking.selectTripFlight(numberOfFlights);
      }
    });
    this.flightCheckout(flightBookingParams);
  }

  static continueBookTripFlight(
    flightBookingData: FlightBookingData,
    numberOfFlights = 2
  ) {
    this.clickLetsGoButton(false);
    cy.wait(Timeouts.MAX_TIMEOUT_120_SEC.timeout);
    ChatBox.waitUntilAvaStopTyping();
    this.clickLetsGoButton(true);
    ChatBooking.selectTripFlight(numberOfFlights);
    cy.get(chatBookingSelectors.getChatMsgBody).then(($element) => {
      if (
        $element.text().includes(`I couldn't find any results that match your search.`)
      ) {
        cy.allure().logStep(`I couldn't find any results that match your search.`);
        cy.wait(Timeouts.MEDIUM_TIMEOUT_30_SEC.timeout);
        cy.get(chatBookingSelectors.letsGoButton).as('letsGoButton');
        cy.get('@letsGoButton').should('be.enabled');
        cy.get('@letsGoButton').scrollIntoView();
        cy.get('@letsGoButton').click({ force: true });
        ChatBooking.selectTripFlight(numberOfFlights);
      }
    });
    this.flightCheckout(flightBookingData.flightBookingParams);
  }

  static changeFlight(flightBookingParams: FlightBookingParams) {
    const selectAirport = (index: number, location: string) => {
      cy.get(chatbotBookingSelectors.locationInput)
        .eq(index)
        .then((element) => {
          cy.wrap(element).click({ force: true });
          cy.wait(Timeouts.SHORT_TIMEOUT_2_SEC.timeout);
          cy.wrap(element).clear();
          cy.wait(Timeouts.SHORT_TIMEOUT_2_SEC.timeout);
          cy.wrap(element).type(location);
          cy.wrap(element)
            .invoke('val')
            .then((text) => {
              const selectLocationFromDropDown = text as string;
              const replacedText = selectLocationFromDropDown.replace(' - ', ' ');
              cy.log('Typed Text: ' + replacedText);
              cy.get(chatbotBookingSelectors.getText).contains(replacedText).click();
            });
        });
    };

    cy.get(chatbotBookingSelectors.wrapperButton)
      .contains('Edit details & search')
      .click();
    cy.wait(Timeouts.SHORT_TIMEOUT_2_SEC.timeout);
    selectAirport(0, flightBookingParams.locations[0]);
    selectAirport(1, flightBookingParams.locations[1]);
  }

  static changeFlightOptions(buttonName: string) {
    cy.get(chatbotBookingSelectors.wrapperButton).contains(buttonName).click();
    ChatBooking.selectFlight(chatBookingSelectors.flightChangeCards);
    cy.wait(['@amplitudeBrowserSDK']);
    cy.get(chatBookingSelectors.chatButton).contains('Book it').click();
    cy.wait('@waitForSentMessage');
  }

  static selectAirport(direction: Directions, airport: string) {
    cy.allure().logStep(`Select ${direction} airport: ${airport}`);
    ChatBooking.clickAddFiledButton(direction);
    cy.get(chatBookingSelectors.airportInput(direction)).type(airport);
    cy.wait('@waitForLocation');
    cy.get(chatBookingSelectors.airportsFromAutocomplete(airport)).click();
  }

  static clickAddFiledButton(fieldName: string, buttonName = 'Add') {
    cy.allure().logStep(`Click ${buttonName} button for ${fieldName} field`);
    cy.get(chatBookingSelectors.formFields).contains(fieldName).click();
  }

  static selectDates(inputDaysToMove: DaysToMove) {
    cy.allure().logStep('Select dates for next month');
    const shiftedDates = Utility.shiftingDates(inputDaysToMove);

    ChatBooking.clickAddFiledButton('Date');
    cy.get(chatBookingSelectors.calendar).should('be.visible');
    if (shiftedDates.monthsToShift > 0) {
      Cypress._.times(shiftedDates.monthsToShift, () => {
        cy.get(chatBookingSelectors.calendarNextMonthButton).click();
      });
    }
    ChatBooking.selectDayInCalendar(shiftedDates.chosenCheckinDay);

    if (shiftedDates.chosenCheckinDay != shiftedDates.chosenCheckoutDay) {
      if (shiftedDates.monthsToShiftToCheckoutDay > 0) {
        Cypress._.times(shiftedDates.monthsToShiftToCheckoutDay, () => {
          cy.get(chatBookingSelectors.calendarNextMonthButton).click();
        });
      }
      ChatBooking.selectDayInCalendar(shiftedDates.chosenCheckoutDay);
    }
  }

  static enterShiftedDates(inputDaysToMove: DaysToMove) {
    Utility.shiftingDates(inputDaysToMove);
    cy.task('getDataFromCache', 'selectedCheckInDate').then(
      (selectedCheckInDate: any) => {
        cy.task('getDataFromCache', 'selectedCheckoutDate').then(
          (selectedCheckoutDate: any) => {
            const checkInDate = new Date(selectedCheckInDate);
            const checkoutDate = new Date(selectedCheckoutDate);
            const checkoutYear = checkInDate.getFullYear();
            const formatter = new Intl.DateTimeFormat('en-US', options);
            const checkInFormatted = formatter.format(checkInDate);
            const checkoutFormatted = formatter.format(checkoutDate);

            const checkInFormattedWithoutYear = checkInFormatted.replace(/,\s\d{4}$/, '');
            const checkoutFormattedWithoutYear = checkoutFormatted.replace(
              /,\s\d{4}$/,
              ''
            );

            const formattedActual = `${checkInFormattedWithoutYear} - ${checkoutFormattedWithoutYear}, ${checkoutYear}`;

            ChatBox.typeMsgToAva(formattedActual);
          }
        );
      }
    );
  }

  static selectDayInCalendar(day: number) {
    cy.allure().logStep(`Select ${day} day in calendar`);
    cy.get(chatBookingSelectors.calendarDayButtons)
      .children(chatBookingSelectors.disabledCalendarDayButton)
      .contains(day)
      .click();
  }

  static selectTripFlight(numberOfFlights = 2) {
    cy.allure().logStep('Select round trip flight');
    Cypress._.times(numberOfFlights, () => {
      ChatBooking.selectFlight(chatBookingSelectors.flightCards);
    });
    cy.wait(['@waitForSentMessage', '@amplitudeBrowserSDK']);
  }

  static selectFlight(selector: string, index = 0, retry = 0) {
    cy.allure().logStep('Select flight');
    cy.wait(Timeouts.MEDIUM_TIMEOUT_30_SEC.timeout);
    cy.get(chatBookingSelectors.flightCardsList).then((element) => {
      if (!element.text().includes('Select')) {
        cy.wait(Timeouts.MEDIUM_TIMEOUT_10_SEC.timeout);
      }
    });
    cy.get(selector).should('be.visible').eq(index).as('flightCard');
    cy.get('@flightCard').contains('Select').click();
    cy.wait('@waitForFlightStatus').then((interception) => {
      if (interception.response?.statusCode !== 200) {
        if (retry > 5) {
          throw new Error(`Failed to select flight. Tried ${retry} times.`);
        }
        retry++;
        Logs.logToCypressAndAllure('Retry to select another flight. Attempt: ' + retry);
        cy.get(chatBookingSelectors.paginatorNextButton).click();
        ChatBooking.selectFlight(selector, ++index, retry);
      }
    });
  }

  static checkIsChatMinimized() {
    cy.allure().logStep('Check if chat is minimized');
    cy.get(chatBookingSelectors.chatView).should((chat) => {
      expect(chat, 'Chat should be minimized').not.exist;
    });
  }

  static bookHotel(location: string, daysToMove: DaysToMove) {
    cy.allure().logStep('Book hotel');
    this.selectAirport(Directions.HOTEL_LOCATION, location);
    this.selectDates(daysToMove);
    ChatBox.getHotelLocationText();
    this.hotelCheckout();
  }

  static hotelCheckout() {
    cy.get(chatBookingSelectors.letsGoButton).click();
    cy.wait('@waitForHotelSearch');
    this.expectHotelDatesAreCorrect();
    HotelSearchResults.selectHotel(0);
    HotelSearchResults.selectRoomOption();
    HotelCheckout.waitToBeLoaded();
    HotelCheckout.checkoutFLow('Business');
    HotelCheckout.expectHotelBookedSuccessfully('Hotel confirmed!');
  }

  static bookCar(
    pickupLocation: string,
    dropoffLocation: string,
    daysToMove: DaysToMove
  ) {
    ChatBooking.selectDates(daysToMove);
    ChatBooking.selectAirport(Directions.PICKUP, pickupLocation);
    ChatBooking.selectAirport(Directions.DROPOFF, dropoffLocation);
    ChatBooking.clickLetsGoButton(false);
  }

  static clickLetsGoButton(withWaiting = true) {
    cy.allure().logStep('Click lets go button');
    if (withWaiting) {
      cy.get(chatBookingSelectors.getChatMsgBody).then(($element) => {
        if (
          $element.text().includes(`I couldn't find any results that match your search.`)
        ) {
          cy.allure().logStep(`I couldn't find any results that match your search.`);
          cy.wait(Timeouts.MEDIUM_TIMEOUT_30_SEC.timeout);
          cy.get(chatBookingSelectors.letsGoButton).as('letsGoButton');
          cy.get('@letsGoButton').should('be.enabled');
          cy.get('@letsGoButton').scrollIntoView();
          cy.get('@letsGoButton').click({ force: true });
        }
      });
    } else {
      cy.get(chatBookingSelectors.letsGoButton)
        .should('be.enabled')
        .click({ force: true });
    }
  }

  static flightCheckout(flightBookingParams: FlightBookingParams) {
    FlightCheckout.waitToPageBeLoaded();
    FlightCheckout.expectNumberOfTravelers(1);
    ChatBooking.expectFlightSummaryIsCorrect(flightBookingParams.flightTripType);
    FlightCheckout.verifyFlightLocation(flightBookingParams);
    FlightCheckout.saveTripInformation();
    FlightCheckout.checkCancellationPolicy();
    FlightCheckout.acceptOutOfPolicyFlight();
    FlightCheckout.updateContactDetails();
    FlightCheckout.validateIfPassportAndVisaInfoRequired();
    FlightCheckout.checkIn(flightBookingParams.checkInType);
    FlightCheckout.bookAndPay(flightBookingParams);
    FlightCheckout.expectFlightSuccessfullyConfirmed(
      flightBookingParams.flightConfirmText
    );
  }

  static expectHotelLocationIsCorrect() {
    cy.wait(Timeouts.SHORT_TIMEOUT_3_SEC.timeout);
    cy.get(hotelSearchResultSelector.selectHotel).should('be.visible');
    cy.task('getDataFromCache', 'locationText').then((chatLocationInput: any) => {
      cy.log('chat Location Input ' + chatLocationInput);
      HotelSearchResults.getHotelLocationInput();
      cy.task('getDataFromCache', 'getHotelLocationInput').then(
        (getHotelLocationInputText: any) => {
          cy.log('Hotel location from search result' + getHotelLocationInputText);
          expect(getHotelLocationInputText).to.equal(chatLocationInput);
        }
      );
    });
  }

  static expectHotelDatesAreCorrect() {
    cy.get(hotelSearchResultSelector.selectHotel).should('be.visible');
    cy.task('getDataFromCache', 'selectedCheckInDate').then(
      (selectedCheckInDate: any) => {
        cy.task('getDataFromCache', 'selectedCheckoutDate').then(
          (selectedCheckoutDate: any) => {
            const checkInDate = new Date(selectedCheckInDate);
            const checkoutDate = new Date(selectedCheckoutDate);
            const checkoutYear = checkInDate.getFullYear();
            const formatter = new Intl.DateTimeFormat('en-US', options);
            const checkInFormatted = formatter.format(checkInDate);
            const checkoutFormatted = formatter.format(checkoutDate);

            const checkInFormattedWithoutYear = checkInFormatted.replace(/,\s\d{4}$/, '');
            const checkoutFormattedWithoutYear = checkoutFormatted.replace(
              /,\s\d{4}$/,
              ''
            );

            const formattedActual = `${checkInFormattedWithoutYear} - ${checkoutFormattedWithoutYear}, ${checkoutYear}`;

            HotelSearchResults.getDatesFromCalendar();

            cy.task('getDataFromCache', 'getDatesFromCalendar').then((dates: any) => {
              cy.log('chat box date:' + formattedActual);
              cy.log('hotel search results dates:' + dates);

              expect(dates).to.equal(formattedActual);
            });
          }
        );
      }
    );
  }

  static expectFlightSummaryIsCorrect(flightType: string) {
    switch (flightType) {
      case 'ONEWAY':
        flightType = 'One-way';
        break;
      case 'ROUNDTRIP':
        flightType = 'Roundtrip';
        break;
    }
    cy.get(utilitySelector.checkoutSummary).should('contain.text', flightType);
    cy.task('getDataFromCache', 'selectedCheckInDate').then(
      (selectedCheckInDate: any) => {
        cy.task('getDataFromCache', 'selectedCheckoutDate').then(
          (selectedCheckoutDate: any) => {
            const checkInDate = new Date(selectedCheckInDate);
            const checkoutDate = new Date(selectedCheckoutDate);
            const formatter = new Intl.DateTimeFormat('en-US', options);
            const checkInFormatted = formatter.format(checkInDate);
            const checkoutFormatted = formatter.format(checkoutDate);

            const checkInFormattedWithoutYear = checkInFormatted.replace(/,\s\d{4}$/, '');
            const checkoutFormattedWithoutYear = checkoutFormatted.replace(
              /,\s\d{4}$/,
              ''
            );

            cy.get(utilitySelector.checkoutSummary).then((checkoutSummary) => {
              const summaryText = checkoutSummary.text();
              expect(summaryText).to.contain(checkInFormattedWithoutYear);
              expect(summaryText).to.contain(checkoutFormattedWithoutYear);
            });
          }
        );
      }
    );
  }

  static changeHotelBookingFlow(location: string) {
    cy.wait(Timeouts.SHORT_TIMEOUT_5_SEC.timeout);
    cy.get(chatBookingSelectors.formFields).contains(Directions.HOTEL_LOCATION).click();
    cy.get(chatBookingSelectors.airportInput(Directions.HOTEL_LOCATION)).type(location);
    cy.wait('@waitForLocation');
    cy.get(chatBookingSelectors.airportsFromAutocomplete(location)).click();
    this.hotelCheckout();
  }
}
