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 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 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 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 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 selectTranslations = (state: RootState) => {
  const defaultStaticTranslations = getTranslations(state.booking.languageCode) as Record<string, any>;
  const dynamicTranslations = (state.booking.translations && state.booking.translations.find(x => x.language == state.booking.languageCode)?.value) ?? {};
  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 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 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 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,
  selectAgentId,
  selectGeneratePaymentUrl,
  selectSkipPaymentWithAgent,
  selectNotifications,
  selectTagIds,
  selectBookingRemarks,
  selectVoucherCodes,
  (
    bookingPackageRequest: BookingPackageRequest<BookingPackageBookRequest>,
    bookingOptions,
    bookingType,
    pax,
    address,
    packageDetails,
    calculateDeposit,
    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,
      returnPaymentUrl: returnPaymentUrl,
      notifications: notifications,
      tagIds: tagIds,
      remarks: remarks,
      voucherCodes: voucherCodes,
      customerRequests: [],
    };

    return bookingPackageRequest;
  }
);

const buildPax = (traveler: Traveler, mainBookerId?: number) => {
  return {
    id: traveler.id,
    gender: parseGender(traveler.gender),
    firstName: traveler.firstName,
    lastName: traveler.lastName,
    dateOfBirth: traveler.birthDate,
    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;
  }
};
