import JsonURL from '@jsonurl/jsonurl';
import { Gender } from '@qite/tide-client';
import { BookingPackageAddress, BookingPackageBookRequest, BookingPackagePax, BookingPackageRequest } from '@qite/tide-client/build/types';
import { createSelector } from '@reduxjs/toolkit';
import { format, parseISO } from 'date-fns';
import { omit } from 'lodash';
import { getTranslations } from '../../../shared/utils/localization-util';
import { RootState } from '../../store';
import { FlightInfo, Room, Traveler } from '../../types';
import { selectNotifications } from '../price-details/price-details-slice';
import { selectAgentId, selectTravelersFormValues } from '../travelers-form/travelers-form-slice';

export const selectHasMounted = (state: RootState) => state.booking.hasMounted;

export const selectIsFetching = (state: RootState) => state.booking.isFetching;

export const selectCurrentStep = (state: RootState) => state.booking.currentStep;

export const selectGeneratePaymentUrl = (state: RootState) => state.booking.generatePaymentUrl;

export const selectSkipPaymentWithAgent = (state: RootState) => state.booking.skipPaymentWithAgent;

export const selectIsFetchingProductOptions = (state: RootState) => state.booking.isBusy;

export const selectDepartureFlight = (state: RootState) => state.booking.package?.outwardFlights?.find((x) => x.isSelected);

export const selectReturnFlight = (state: RootState) => state.booking.package?.returnFlights?.find((x) => x.isSelected);

export const selectPackageRooms = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.rooms;

export const selectIncludedServiceTypes = (state: RootState) => selectActiveOption(state)?.includedServiceTypes;

export const selectAvailabilities = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.availabilities;

export const selectPackageTags = (state: RootState) => state.booking.package?.tags;

export const selectIsOnRequest = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.isOnRequest;

export const selectPackageOptionUnits = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.optionUnits;

export const selectPackageOptionPax = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.optionPax;

export const selectPackageGroups = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.groups;

export const selectPackageDetails = (state: RootState) => state.booking.package;

export const selectPackageFlights = (state: RootState) => ({
  outward: state.booking.package?.outwardFlights,
  return: state.booking.package?.returnFlights
});

export const selectFlightMetaData = (state: RootState) => state.booking.package?.flightInfo.metaData;

export const selectActiveOption = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected);

export const selectPackageAirlineGroups = createSelector(
  selectActiveOption,
  selectDepartureFlight,
  selectReturnFlight,
  (option, departureFlight, returnFlight) =>
    option?.airlineGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? ''))
);

export const selectPackageAirportGroups = createSelector(
  selectActiveOption,
  selectDepartureFlight,
  selectReturnFlight,
  (option, departureFlight, returnFlight) =>
    option?.airportGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? ''))
);

export const selectRoomOptionDepartureFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
  const roomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected).shift());
  return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
});

export const selectRoomOptionReturnFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
  const selectedRoomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected));
  // check if there are return flights selected
  if (selectedRoomOptions?.some((ro) => ro.length > 1)) {
    const roomOptions = ativeOption?.rooms.map((r) =>
      r.options
        .filter((o) => o.isSelected)
        .reverse()
        .shift()
    );
    return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
  }
  return undefined;
});

export const selectApiSettings = (state: RootState) => state.apiSettings;

export const selectIsUnavailable = (state: RootState) => state.booking.isUnavailable;

export const selectRequestRooms = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.requestRooms;

export const selectOfficeId = (state: RootState) => state.booking.officeId;

export const selectLanguageCode = (state: RootState) => state.booking.languageCode;

export const selectCurrencyCode = (state: RootState) => state.booking.package?.currencyCode ?? 'EUR';

export const selectDefaultStaticTranslations = (state: RootState) => getTranslations(state.booking.languageCode) as Record<string, any>;

export const selectLanguage = (state: RootState) => state.booking.languageCode;

export const selectAllDynamicTranslations = (state: RootState) => state.booking.translations;

export const selectDynamicTranslations = createSelector([selectAllDynamicTranslations, selectLanguage], (allDynamicTranslations, language) => {
  return allDynamicTranslations?.find((x) => x.language == language)?.value ?? {};
});

export const selectTranslations = createSelector(
  [selectDefaultStaticTranslations, selectDynamicTranslations],
  (defaultStaticTranslations, dynamicTranslations) => {
    const merged = {} as Record<string, any>;

    for (const key of Array.from(new Set([...Object.keys(defaultStaticTranslations), ...Object.keys(dynamicTranslations)]))) {
      merged[key] = {
        ...(defaultStaticTranslations[key] || {}),
        ...(dynamicTranslations[key] || {})
      };
    }

    return merged;
  }
);

export const selectBookingOptions = (state: RootState) => state.booking.bookingOptions;

export const selectBookingType = (state: RootState) => state.booking.bookingType;

export const selectTagIds = (state: RootState) => state.booking.tagIds;

export const selectAgentAdressId = (state: RootState) => state.booking.agentAdressId;

export const selectProductAttributes = (state: RootState) => state.booking.productAttributes;

export const selectBookingAttributes = (state: RootState) => state.booking.bookingAttributes;

export const selectBookingNumber = (state: RootState) => state.booking.bookingNumber;

export const selectIsOption = (state: RootState) => state.booking.isOption;

export const selectBookingRooms = (state: RootState) => state.booking.bookingAttributes?.rooms;

export const selectBookingRemarks = (state: RootState) => state.booking.remarks;

export const selectVoucherCodes = (state: RootState) => state.booking.voucherCodes;

export const selectCalculateDeposit = (state: RootState) => state.booking.calculateDeposit;

export const selectShowCommission = (state: RootState) => state.booking.showCommission;

export const selectIsRetry = (state: RootState) => state.booking.isRetry;

export const selectStartDate = (state: RootState) => state.booking.package?.options.find((x) => x.isSelected)?.fromDate;

export const selectAgents = (state: RootState) => state.booking.agents;

export const selectCountries = (state: RootState) => state.booking.countries;

export const selectProductCode = (state: RootState) => state.booking.productAttributes?.productCode;

export const selectAccommodationCodes = (state: RootState) => {
  const accommodationCodes: string[] = [];
  state.booking.package?.options.forEach((o) => {
    o.rooms.forEach((r) =>
      r.options.forEach((ro) => {
        if (!accommodationCodes.some((y) => y === ro.accommodationCode)) {
          accommodationCodes.push(ro.accommodationCode);
        }
      })
    );
  });
  return accommodationCodes;
};

export const selectAccommodationViews = (state: RootState) => state.booking.accommodationViews;

export const selectBookingQuery = (state: RootState) => {
  const bookingAttributes = state.booking.bookingAttributes;

  if (!bookingAttributes) {
    return undefined;
  }

  const params: Record<string, string> = {};
  Object.entries(bookingAttributes).forEach(([key, value]) => {
    if (key === 'startDate' || key === 'endDate') {
      value = format(parseISO(value), 'yyyy-MM-dd');
    }

    if (key === 'rooms') {
      value = JsonURL.stringify(
        (value as Room[]).map((room) => omit(room, ['children'])),
        {
          AQF: true
        }
      );
    }

    if (key === 'allotmentIds' && !value.length) {
      value = undefined;
    }

    if (key === 'flight' && value) {
      var flightInfo = value as FlightInfo;
      value = JsonURL.stringify(flightInfo, { AQF: true });
    }

    if (value) {
      params[key] = value;
    }
  });

  return params;
};

export const selectBookingQueryString = createSelector(selectBookingQuery, (params) => {
  if (!params) {
    return undefined;
  }

  return Object.keys(params)
    .filter((key) => typeof params[key] !== 'undefined')
    .map((key) => `${key}=${params[key]}`)
    .join('&');
});

export const selectMainBookerId = createSelector(selectTravelersFormValues, (formValues) => formValues?.mainBookerId);

export const selectBookingPackagePax = createSelector(selectTravelersFormValues, (formValues) => {
  var pax: BookingPackagePax[] = [];

  formValues?.rooms.forEach((r) =>
    r.adults.forEach((x) => {
      const adultPax = buildPax(x, formValues?.mainBookerId);

      if (adultPax.isMainBooker) {
        adultPax.mobilePhone = formValues?.phone;
        adultPax.email = formValues?.email;
      }

      pax.push(adultPax);
    })
  );

  formValues?.rooms.forEach((r) =>
    r.children.forEach((x) => {
      pax.push(buildPax(x));
    })
  );

  return pax;
});

export const selectBookingAddress = createSelector(selectTravelersFormValues, selectBookingPackagePax, selectBookingType, (formValues, pax, bookingType) => {
  const mainBooker = pax.find((x) => x.isMainBooker);
  if (!mainBooker || bookingType == 'b2b') return undefined;

  return {
    name: `${mainBooker.firstName} ${mainBooker.lastName}`,
    street: formValues?.street,
    number: formValues?.houseNumber,
    box: formValues?.box,
    postalCode: formValues?.zipCode,
    location: formValues?.place,
    country: formValues?.country,
    mobilePhone: formValues?.phone,
    email: formValues?.email
  } as BookingPackageAddress;
});

export const selectBookingPackageRequest = createSelector(selectOfficeId, selectAgentId, selectAgentAdressId, (officeId, agentId, agentAdressId) => {
  const agencyId = (agentId ?? agentAdressId ?? 0) > 0 ? agentId ?? agentAdressId : null;

  return {
    officeId: officeId,
    agentId: agencyId,
    payload: null
  } as BookingPackageRequest<any>;
});

export const selectBookingPackageBookRequest = createSelector(
  selectBookingPackageRequest,
  selectBookingOptions,
  selectBookingType,
  selectBookingPackagePax,
  selectBookingAddress,
  selectPackageDetails,
  selectCalculateDeposit,
  selectShowCommission,
  selectAgentId,
  selectGeneratePaymentUrl,
  selectSkipPaymentWithAgent,
  selectNotifications,
  selectTagIds,
  selectBookingRemarks,
  selectVoucherCodes,
  (
    bookingPackageRequest: BookingPackageRequest<BookingPackageBookRequest>,
    bookingOptions,
    bookingType,
    pax,
    address,
    packageDetails,
    calculateDeposit,
    showCommission,
    agentId,
    generatePaymentUrl,
    skipPaymentWithAgent,
    notifications,
    tagIds,
    remarks,
    voucherCodes
  ) => {
    if (!packageDetails) return null;

    let returnPaymentUrl = false;

    if (generatePaymentUrl && (!skipPaymentWithAgent || (agentId ?? 0) == 0)) {
      returnPaymentUrl = true;
    }

    var entryStatus = 0;
    var customEntryStatusId = undefined;

    switch (bookingType) {
      case 'b2b':
        if (bookingOptions.b2b.tagIds && bookingOptions.b2b.tagIds.length > 0) {
          tagIds = tagIds?.concat(bookingOptions.b2b.tagIds);
        }
        if (bookingOptions.b2b.entryStatus) {
          entryStatus = bookingOptions.b2b.entryStatus;
        }
        if (bookingOptions.b2b.customEntryStatusId) {
          customEntryStatusId = bookingOptions.b2b.customEntryStatusId;
        }
        break;
      case 'b2b2c':
        if (bookingOptions.b2b2c.tagIds && bookingOptions.b2b2c.tagIds.length > 0) {
          tagIds = tagIds?.concat(bookingOptions.b2b2c.tagIds);
        }
        if (bookingOptions.b2b2c.entryStatus) {
          entryStatus = bookingOptions.b2b2c.entryStatus;
        }
        if (bookingOptions.b2b2c.customEntryStatusId) {
          customEntryStatusId = bookingOptions.b2b2c.customEntryStatusId;
        }
        break;
      default:
        if (bookingOptions.b2c.tagIds && bookingOptions.b2c.tagIds.length > 0) {
          tagIds = tagIds?.concat(bookingOptions.b2c.tagIds);
        }
        if (bookingOptions.b2c.entryStatus) {
          entryStatus = bookingOptions.b2c.entryStatus;
        }
        if (bookingOptions.b2c.customEntryStatusId) {
          customEntryStatusId = bookingOptions.b2c.customEntryStatusId;
        }
        break;
    }

    bookingPackageRequest.payload = {
      package: packageDetails,
      status: entryStatus,
      customStatusId: customEntryStatusId,
      address: address,
      pax: pax?.length != 0 ? pax : packageDetails.options[0].requestRooms.flatMap((x) => x.pax),
      nonTravelPax: [],
      calculateDeposit: calculateDeposit,
      showCommission: showCommission,
      returnPaymentUrl: returnPaymentUrl,
      notifications: notifications,
      tagIds: tagIds,
      remarks: remarks,
      voucherCodes: voucherCodes,
      customerRequests: []
    };

    return bookingPackageRequest;
  }
);

export const selectTravelersFirstStep = (state: any) => state.booking.travelersFirstStep;

const buildPax = (traveler: Traveler, mainBookerId?: number) => {
  return {
    id: traveler.id,
    gender: parseGender(traveler.gender),
    firstName: traveler.firstName,
    lastName: traveler.lastName,
    dateOfBirth: traveler.birthDate,
    age: traveler.birthDate ? null : traveler.age,
    isMainBooker: traveler.id == mainBookerId
  } as BookingPackagePax;
};

const parseGender = (gender: string): number => {
  switch (gender) {
    case 'm':
      return Gender.male;
    case 'f':
      return Gender.female;
    case 'x':
    default:
      return Gender.other;
  }
};
