import { Logs, Timeouts, Utility } from '@utility-lib/index';
import { FlightReSearchUtility, FlightBookingParams } from '@flight-modal/index';
import { Flights } from '@flight-lib/index';

export const flightSearchResultSelector = {
  flightResultLoadedCount: '[qaId^="flightResultCountText"]',
  flightFareRow: '[qaId^="flightFareRow"]',
  selectFareType: '[qaId^="flightFareCard"]',
  selectFlight: '[qaId="selectFlightButton"]',
  bundleFlightCard: '[qaId^="flightBundleCard"]',
  changeDepartureFlight: '[class^="flight-departure-card__change"]',
  passengersTag: '.flight-header__user-multiple__label',
  filterByAirLine: '[headerlabel="Airlines"]',
  filterAirLineSelector: '.filter-boolean__label',
  flightHeaderSearchInfo: '.flight-header__info',
  flightMultiHeaderSearchPanel: 'ta-search-form-flight-multi-minimized',
  flightNoResultSection: '.flight-no-results',
  loyaltyCardFilterButton: '[title="Airlines"] [data-testid="activeFilter"]',
  selectLoyaltyCard: '[qaId="flightFilterInClubFilter"] [class="mat-checkbox-frame"]'
};

export class FlightSearchResults {
  // one way flight
  static onewaySearchResult(flightParams: FlightBookingParams) {
    FlightReSearchUtility.isNoResultFoundWrapper(() => {
      cy.get(flightSearchResultSelector.flightFareRow).then((fareRows) => {
        const randomOrderedArray = FlightSearchResults.getRandomArrayWithLength(fareRows.length);
        FlightSearchResults.selectFirstAvailableOnewayFlight(fareRows.length, 0, randomOrderedArray);
      });
    }, flightParams);
  }

  static onewayMockSearchResult() {
    cy.get(flightSearchResultSelector.flightFareRow)
      .eq(0)
      .find(flightSearchResultSelector.selectFareType)
      .eq(0)
      .click({ force: true });
    cy.wait(['@waitForDepartureFlightFares', '@waitForDepartureFlightValidation']);
    cy.get(flightSearchResultSelector.selectFlight).eq(0).click({ force: true });
  }

  static roundtripMockSearchResult() {
    cy.get(flightSearchResultSelector.flightFareRow)
      .eq(0)
      .find(flightSearchResultSelector.selectFareType)
      .eq(0)
      .click({ force: true });
    cy.wait(['@waitForDepartureFlightFares', '@waitForDepartureFlightValidation']);
    cy.get(flightSearchResultSelector.selectFlight).eq(0).click({ force: true });
    cy.wait('@waitForReturnFlight');
    cy.get(flightSearchResultSelector.flightFareRow)
      .eq(0)
      .find(flightSearchResultSelector.selectFareType)
      .eq(0)
      .click({ force: true });
    cy.wait(['@waitForReturnFlightFares', '@waitForReturnFlightValidation']);
    cy.get(flightSearchResultSelector.selectFlight).eq(0).click({ force: true });
  }

  static multiCityBundleMockSearchResult() {
    cy.get(flightSearchResultSelector.bundleFlightCard)
      .eq(0)
      .find(flightSearchResultSelector.selectFlight)
      .eq(0)
      .click({ force: true });
  }

  static selectFirstAvailableOnewayFlight(maxIndex: number, currentIndex: number, randomOrderedArray: number[]) {
    cy.log(`flight search result - selectFirstAvailableOnewayFlight - currentIndex ${currentIndex}`);

    cy.get(flightSearchResultSelector.flightFareRow)
      .eq(randomOrderedArray[currentIndex])
      .find(flightSearchResultSelector.selectFareType)
      .eq(0)
      .click({ force: true });
    cy.wait(['@waitForDepartureFlightFares', '@waitForDepartureFlightValidation']);

    cy.get(flightSearchResultSelector.selectFlight).eq(0).click({ force: true });
    cy.wait('@waitForOnewayFlightContract').then((xhr) => {
      cy.log(
        `flight search result - selectFirstAvailableOnewayFlight - waitForFlightContract - response code ${xhr.response?.statusCode} - currentIndex ${currentIndex}`
      );
      if (xhr.response?.statusCode !== 200) {
        if (maxIndex === currentIndex + 1) {
          // for now, let test pass gracefully
        } else {
          cy.log(`flight search result - selectFirstAvailableOnewayFlight - recursion - currentIndex ${currentIndex}`);
          FlightSearchResults.selectFirstAvailableOnewayFlight(maxIndex, currentIndex + 1, randomOrderedArray);
        }
      } else {
        cy.log(
          `flight search result - selectFirstAvailableOnewayFlight - proceed to contract page - currentIndex ${currentIndex}`
        );
      }
    });
  }

  // round trip flight
  static roundtripSearchResult(flightParams: FlightBookingParams) {
    FlightReSearchUtility.isNoResultFoundWrapper(() => {
      cy.get(flightSearchResultSelector.flightFareRow).then((fareRows) => {
        const randomOrderedDepartureArray = FlightSearchResults.getRandomArrayWithLength(fareRows.length);
        FlightSearchResults.selectFirstAvailableRoundtripDepartureFlight(
          fareRows.length,
          0,
          randomOrderedDepartureArray
        );
      });
    }, flightParams);
  }

  // this is a double recursion on both departure flight and return flight
  static selectFirstAvailableRoundtripDepartureFlight(
    maxDepartureIndex: number,
    currentDepartureIndex: number,
    randomOrderedDepartureArray: number[]
  ) {
    cy.log(
      `flight search result - selectFirstAvailableRoundtripDepartureFlight - currentDepartureIndex ${currentDepartureIndex}`
    );

    cy.get(flightSearchResultSelector.flightFareRow)
      .eq(randomOrderedDepartureArray[currentDepartureIndex])
      .find(flightSearchResultSelector.selectFareType)
      .eq(0)
      .click({ force: true });
    cy.wait(['@waitForDepartureFlightFares', '@waitForDepartureFlightValidation']);

    cy.get(flightSearchResultSelector.selectFlight).eq(0).click({ force: true });
    cy.wait('@waitForReturnFlight');

    // wait for page to load
    cy.get(flightSearchResultSelector.flightResultLoadedCount, Timeouts.MEDIUM_TIMEOUT_60_SEC).should('exist');

    cy.log(
      `flight search result - selectFirstAvailableRoundtripDepartureFlight - get list of return flights - currentDepartureIndex ${currentDepartureIndex}`
    );
    // once i get return flight, i need to recursion on the return flight as well
    cy.get(flightSearchResultSelector.flightFareRow).then((fareRows) => {
      const randomOrderedReturnArray = FlightSearchResults.getRandomArrayWithLength(fareRows.length);
      FlightSearchResults.selectFirstAvailableRoundtripReturnFlight(
        maxDepartureIndex,
        currentDepartureIndex,
        fareRows.length,
        0,
        randomOrderedDepartureArray,
        randomOrderedReturnArray
      );
    });
  }

  // eslint-disable-next-line max-params
  static selectFirstAvailableRoundtripReturnFlight(
    maxDepartureIndex: number,
    currentDepartureIndex: number,
    maxReturnIndex: number,
    currentReturnIndex: number,
    randomOrderedDepartureArray: number[],
    randomOrderedReturnArray: number[]
  ) {
    cy.log(
      `flight search result - selectFirstAvailableRoundtripReturnFlight - currentDepartureIndex ${currentDepartureIndex} - currentReturnIndex ${currentReturnIndex}`
    );

    cy.get(flightSearchResultSelector.flightFareRow)
      .eq(randomOrderedReturnArray[currentReturnIndex])
      .find(flightSearchResultSelector.selectFareType)
      .eq(0)
      .click({ force: true });
    cy.wait(['@waitForReturnFlightFares', '@waitForReturnFlightValidation']);

    cy.get(flightSearchResultSelector.selectFlight).eq(0).click({ force: true });
    cy.wait('@waitForRoundtripFlightContract').then((xhr) => {
      cy.log(
        `flight search result - selectFirstAvailableRoundtripReturnFlight - waitForFlightContract - response code ${xhr.response?.statusCode} - currentDepartureIndex ${currentDepartureIndex} - currentReturnIndex ${currentReturnIndex}`
      );
      if (xhr.response?.statusCode !== 200) {
        if (maxReturnIndex === currentReturnIndex + 1) {
          // this means return flight loops to the last one, now bump the departure flight
          // unless departure flight also loops to the last one
          if (maxDepartureIndex === currentDepartureIndex + 1) {
            // for now, let test pass gracefully
          } else {
            cy.log(
              `flight search result - selectFirstAvailableRoundtripReturnFlight - recursion on departure flight - currentDepartureIndex ${currentDepartureIndex} - currentReturnIndex ${currentReturnIndex}`
            );
            // go back to deparutre page
            cy.get(flightSearchResultSelector.changeDepartureFlight).click({
              force: true
            });
            // wait for page to load
            cy.get(flightSearchResultSelector.flightResultLoadedCount, Timeouts.MEDIUM_TIMEOUT_60_SEC).should('exist');
            FlightSearchResults.selectFirstAvailableRoundtripDepartureFlight(
              maxDepartureIndex,
              currentDepartureIndex + 1,
              randomOrderedDepartureArray
            );
          }
        } else {
          cy.log(
            `flight search result - selectFirstAvailableRoundtripReturnFlight - recursion on return flight - currentDepartureIndex ${currentDepartureIndex} - currentReturnIndex ${currentReturnIndex}`
          );
          FlightSearchResults.selectFirstAvailableRoundtripReturnFlight(
            maxDepartureIndex,
            currentDepartureIndex,
            maxReturnIndex,
            currentReturnIndex + 1,
            randomOrderedDepartureArray,
            randomOrderedReturnArray
          );
        }
      } else {
        cy.log(
          `flight search result - selectFirstAvailableRoundtripReturnFlight - proceed to contract page - currentDepartureIndex ${currentDepartureIndex} - currentReturnIndex ${currentReturnIndex}`
        );
      }
    });
  }

  // bundle flight
  static bundleSearchResult(flightParams: FlightBookingParams) {
    FlightReSearchUtility.isNoResultFoundWrapper(() => {
      cy.get(flightSearchResultSelector.bundleFlightCard).then((bundles) => {
        const randomOrderedArray = FlightSearchResults.getRandomArrayWithLength(bundles.length);
        FlightSearchResults.selectFirstAvailableBundleFlight(bundles.length, 0, randomOrderedArray);
      });
    }, flightParams);
  }

  static selectFirstAvailableBundleFlight(maxIndex: number, currentIndex: number, randomOrderedArray: number[]) {
    cy.log(`flight search result - selectFirstAvailableBundleFlight - currentIndex ${currentIndex}`);

    cy.get(flightSearchResultSelector.bundleFlightCard)
      .eq(randomOrderedArray[currentIndex])
      .find(flightSearchResultSelector.selectFlight)
      .eq(0)
      .click({ force: true });

    cy.wait('@waitForBundleFlightContract').then((xhr) => {
      cy.log(
        `flight search result - selectFirstAvailableBundleFlight - waitForFlightContract - response code ${xhr.response?.statusCode} - currentIndex ${currentIndex}`
      );
      if (xhr.response?.statusCode !== 200) {
        if (maxIndex === currentIndex + 1) {
          // for now, let test pass gracefully
        } else {
          cy.log(`flight search result - selectFirstAvailableBundleFlight - recursion - currentIndex ${currentIndex}`);
          FlightSearchResults.selectFirstAvailableBundleFlight(maxIndex, currentIndex + 1, randomOrderedArray);
        }
      } else {
        cy.log(
          `flight search result - selectFirstAvailableBundleFlight - proceed to contract page - currentIndex ${currentIndex}`
        );
      }
    });
  }

  static getRandomArrayWithLength(arrayLength: number): number[] {
    const result = Array.from(Array(arrayLength).keys());
    let currentIndex = result.length,
      randomIndex;

    // shuffle the array to random
    while (currentIndex > 0) {
      randomIndex = Math.floor(Math.random() * currentIndex);
      currentIndex--;

      [result[currentIndex], result[randomIndex]] = [result[randomIndex], result[currentIndex]];
    }
    return result;
  }

  static multicityFlexSearchResult(flightParams: FlightBookingParams) {
    FlightReSearchUtility.isNoResultFoundWrapper(() => {
      cy.intercept('POST', '**/api/v1/trip/flight/bundle/flexible/searches/**/1**').as('waitForNextLegFlight');

      FlightSearchResults.flexFlightSearchResult(0);
      cy.allure().logStep('flight - search result - multi city flex flight - go to next leg flights');
      cy.wait('@waitForNextLegFlight');

      FlightReSearchUtility.isNoResultFoundWrapper(() => {
        cy.intercept('POST', '**/api/v1/trip/flight/bundle/flexible/searches/**/contract**').as(
          'waitForFlightContract'
        );
        FlightSearchResults.flexFlightSearchResult(1);
        cy.allure().logStep('flight -  multi city flex flight - search result - get contract');
        cy.wait('@waitForFlightContract');
        FlightReSearchUtility.switchLocationIfSearchResultEmpty(flightParams);
      }, flightParams);
    }, flightParams);
  }

  static flexFlightSearchResult(index: number) {
    cy.allure().logStep('flight - search result - multi city flex flight - pick random fare group');
    cy.intercept('GET', `**/api/v1/trip/flight/bundle/flexible/searches/**/${index}/flights/**/fares`).as(
      'waitForFlexibleFlightFares'
    );
    cy.intercept('GET', `**/api/v1/trip/flight/bundle/flexible/searches/**/${index}/flights/**/validate**`).as(
      'waitForFlexibleFlightValidation'
    );
    cy.get(flightSearchResultSelector.selectFareType).then((fares) => {
      fares[Math.floor(Math.random() * fares.length)].click();
    });
    cy.wait(['@waitForFlexibleFlightFares', '@waitForFlexibleFlightValidation']);

    cy.allure().logStep('flight - search result - multi city flex flight - pick a fare');
    cy.get(flightSearchResultSelector.selectFlight).eq(0).click({ force: true });
  }

  public static isNoResultFound(): Cypress.Chainable<boolean> {
    return cy.checkIfExist(flightSearchResultSelector.flightNoResultSection);
  }

  static clickOnFlightHeaderSearchInfoPanel(isItMultiCitySearch: boolean) {
    if (isItMultiCitySearch) {
      cy.get(flightSearchResultSelector.flightMultiHeaderSearchPanel).click();
    } else {
      cy.clickIfExist(flightSearchResultSelector.flightHeaderSearchInfo, Timeouts.SHORT_TIMEOUT_3_SEC);
    }
  }

  static selectFutureFlightDate() {
    Utility.selectFutureDate(2);
  }

  static setOrigin(location: string) {
    Flights.setOrigin(location);
  }

  static setDestination(location: string) {
    Flights.setDestination(location);
  }

  static searchFlight() {
    Flights.searchFlight();
  }

  static verifyNumberOfBookings() {
    cy.allure().logStep('verify number of bookings is not 0');
    cy.get(flightSearchResultSelector.flightNoResultSection, Timeouts.MEDIUM_TIMEOUT_60_SEC).should('not.be.exist');
  }

  static verifyNumberOfPassengers(numberOfPassengers: number) {
    cy.allure().logStep(`verify number of passengers is ${numberOfPassengers}`);
    cy.get(flightSearchResultSelector.passengersTag).contains(`${numberOfPassengers} Passengers`);
  }

  static filterByAirLine(airLine: string) {
    cy.allure().logStep('filter by air line');
    cy.get(flightSearchResultSelector.filterByAirLine).click();
    cy.get(flightSearchResultSelector.filterAirLineSelector).contains(airLine).click();
    cy.wait('@waitForFlightStatus').then((xhr) => {
      Logs.cypressResponseLog('AIRLINE RESPONSE', xhr);
    });
  }

  static addLoyaltyCardFilter() {
    cy.get(flightSearchResultSelector.loyaltyCardFilterButton).click();
    cy.wait(Timeouts.SHORT_TIMEOUT_2_SEC.timeout);
    cy.get(flightSearchResultSelector.selectLoyaltyCard).click({
      force: true
    });
    cy.wait(Timeouts.SHORT_TIMEOUT_2_SEC.timeout);
  }
}
