import JsonURL from '@jsonurl/jsonurl';
import {
  BookingPackageDetailsRequest,
  BookingPackageItem,
  BookingPackagePax,
  BookingPackageRequest,
  BookingPackageRequestRoom,
  BookingPackageSearchRequest
} from '@qite/tide-client/build/types';
import { addDays, addMonths, addYears, format, formatISO } from 'date-fns';
import { isEmpty, now, omit } from 'lodash';
import React, { useContext, useEffect, useRef, useState } from 'react';
import { ApiSettingsState } from '../../shared/types';
import { getTranslations } from '../../shared/utils/localization-util';
import { getDateAsDateFromParams, getRoomsFromParams } from '../../shared/utils/query-string-util';
import SettingsContext from '../settings-context';
import { DateRange, ProductRoom } from '../types';
import packageApi from '../utils/api';
import { formatPriceByMode } from '../utils/price';
import Dates from './dates';
import Footer from './footer';
import Header from './header';
import ListView from './list-view';
import Rooms from './rooms';
import { DATE_FORMAT } from '../constants';

interface ProductProps {
  productCode: string;
  productName: string;
  duration?: number;
  rating?: number;
}

const Product: React.FC<ProductProps> = ({ productCode, productName, duration, rating }) => {
  const {
    apiKey,
    apiUrl,
    officeId,
    agentId,
    catalogueId,
    includeFlights,
    language,
    basePath,
    priceMode,
    addProductToQuery,
    isOffer,
    displayMode = 'calendar',
    searchType = 0
  } = useContext(SettingsContext);
  const translations = getTranslations(language);

  const [loaded, setLoaded] = useState<boolean>(false);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [roomsIsDisabled, setRoomsIsDisabled] = useState<boolean>(true);
  const [price, setPrice] = useState<number>();
  const [hasFlight, setHasFlight] = useState<boolean>(false);
  const [hasTransfer, setHasTransfer] = useState<boolean>(false);
  const [rooms, setRooms] = useState<ProductRoom[]>([{ adults: 2, children: 0, childAges: [] }]);
  const [searchResponse, setSearchResponse] = useState<BookingPackageItem[]>([]);
  const [detailResponse, setDetailResponse] = useState<any>();
  const [dateRange, setDateRange] = useState<DateRange>();
  const [packageProductName, setPackageProductName] = useState<string>(productName);
  const [currencyCode, setCurrencyCode] = useState<string>('');
  const [searchResults, setSearchResults] = useState<BookingPackageItem[]>([]);
  const skipNextFetchRef = useRef(false);

  const fetchPackage = async (signal: AbortSignal) => {
    if (displayMode === 'list' && searchType !== 1) {
      console.error('The combination of searchType and displayMode is not supported.' + " Please set searchType to 1 when using displayMode 'list'.");
      return;
    }

    if (loaded && productCode && dateRange?.fromDate && dateRange?.toDate && rooms) {
      const apiSettingsState =
        apiKey && apiUrl
          ? ({
              apiKey: apiKey,
              apiUrl: apiUrl
            } as ApiSettingsState)
          : undefined;

      const startDate = formatISO(dateRange?.fromDate, { representation: 'date' }) + 'T00:00:00Z';
      const endDate = formatISO(dateRange?.toDate, { representation: 'date' }) + 'T00:00:00Z';

      const requestRooms = rooms.map((room, i) => {
        const requestRoom = {
          index: i,
          pax: []
        } as BookingPackageRequestRoom;

        for (var a = 0; a < room.adults; a++) {
          requestRoom.pax.push({
            age: 30
          } as BookingPackagePax);
        }

        for (var c = 0; c < room.children; c++) {
          requestRoom.pax.push({
            age: room.childAges[c]
          } as BookingPackagePax);
        }

        return requestRoom;
      });

      const detailsRequest: BookingPackageRequest<BookingPackageDetailsRequest> = {
        officeId: officeId,
        agentId: agentId,
        payload: {
          searchType: 0,
          catalogueId: catalogueId,
          productCode: productCode,
          fromDate: startDate,
          toDate: endDate,
          includeFlights: includeFlights,
          rooms: requestRooms
        }
      };

      const searchRequest: BookingPackageRequest<BookingPackageSearchRequest> = {
        officeId: officeId,
        agentId: agentId,
        payload: {
          searchType: 1,
          useExactDates: false,
          earliestFromOffset: 0,
          latestToOffset: 0,
          catalogueIds: [catalogueId],
          productCodes: [productCode],
          fromDate: new Date(now()).toISOString(),
          toDate: addYears(new Date(now()), 1).toISOString(),
          includeFlights: includeFlights,
          rooms: requestRooms
        }
      };

      setIsLoading(true);

      if (displayMode === 'calendar' && searchType === 0) {
        setDetailResponse(await packageApi.fetchDetails(detailsRequest, signal, language, apiSettingsState));
      }

      if (searchType === 1) {
        setSearchResponse(await packageApi.fetchSearch(searchRequest, signal, apiSettingsState));
      }

      setIsLoading(false);
    }
  };

  const handleBookClick = () => {
    const params: Record<string, string> = {};

    if (rooms) {
      const serialized = JsonURL.stringify(
        rooms.map((room) => omit(room, ['children'])),
        { AQF: true }
      );

      if (serialized) {
        params['rooms'] = serialized;
      }
    }

    if (dateRange?.fromDate) {
      params['startDate'] = format(dateRange.fromDate, DATE_FORMAT);
    }

    if (dateRange?.toDate) {
      params['endDate'] = format(dateRange.toDate, DATE_FORMAT);
    }

    params['catalogueId'] = catalogueId.toString();

    if (addProductToQuery) {
      params['productCode'] = productCode;
      params['productName'] = encodeURI(packageProductName)!;
    }

    const path = window.location.pathname;
    const paramString = Object.keys(params)
      .map((key) => `${key}=${params[key]}`)
      .join('&');

    window.location.href = basePath.startsWith('/')
      ? `${window.location.protocol}//${window.location.host}${basePath}?${paramString}`
      : `${path}${path.endsWith('/') ? '' : '/'}${basePath}?${paramString}`;
  };

  const handleRoomChange = (rooms: ProductRoom[]) => {
    setRooms(rooms);
  };

  const handleDateChange = (value: DateRange) => {
    if (searchType === 1) {
      skipNextFetchRef.current = true;
    }
    setDateRange(value);

    if (value.fromDate && value.toDate && searchType === 1 && searchResults.length > 0) {
      const from = format(value.fromDate, DATE_FORMAT);
      const to = format(value.toDate, DATE_FORMAT);

      const selectedItem = searchResults.find((item) => {
        const itemFrom = format(new Date(item.fromDate), DATE_FORMAT);
        const itemTo = format(new Date(item.toDate), DATE_FORMAT);

        return itemFrom === from && itemTo === to;
      });

      if (selectedItem) {
        setPrice(selectedItem.price);
        setPackageProductName(selectedItem.name);
      }
    }
  };

  const handleDateSelect = (selectedItem: BookingPackageItem) => {
    if (searchType === 1) {
      skipNextFetchRef.current = true;
    }
    const fromDate = new Date(selectedItem.fromDate);
    const toDate = new Date(selectedItem.toDate);

    setDateRange({ fromDate, toDate });
    setPrice(selectedItem.price);
    setPackageProductName(selectedItem.name);
  };

  useEffect(() => {
    if (searchResponse && !isEmpty(searchResponse)) {
      setSearchResults(searchResponse);
      const selectedItem = searchResponse.find(
        (item) => new Date(item.fromDate).getTime() === dateRange?.fromDate?.getTime() && new Date(item.toDate).getTime() === dateRange?.toDate?.getTime()
      );
      if (selectedItem) {
        setPrice(selectedItem.price);
        setPackageProductName(selectedItem.name);
        setCurrencyCode(selectedItem.currencyCode);
      } else {
        setPrice(searchResponse[0].price);
        setPackageProductName(searchResponse[0].name);
        setCurrencyCode(searchResponse[0].currencyCode);
      }
    } else {
      setSearchResults([]);
      setIsLoading(false);
      setPackageProductName(translations.PRODUCT.NOT_AVAILABLE);
    }
  }, [searchResponse]);

  useEffect(() => {
    if (detailResponse && !detailResponse.errorCode && detailResponse.payload) {
      const selectedOption = detailResponse.payload.options.find((x: any) => x.isSelected);

      if (selectedOption) {
        const hasFlight = selectedOption.includedServiceTypes.some((x: any) => x === 7);
        const hasTranfer = selectedOption.includedServiceTypes.some((x: any) => x === 13);

        setPrice(selectedOption.price);
        setHasFlight(hasFlight);
        setHasTransfer(hasTranfer);
        setCurrencyCode(detailResponse.payload.currencyCode);
        setPackageProductName(selectedOption.name);
      }
    } else if (detailResponse !== undefined) {
      setPrice(undefined);
      setHasFlight(false);
      setHasTransfer(false);
      setCurrencyCode('');
    }
  }, [detailResponse]);

  useEffect(() => {
    if (searchType === 1 && skipNextFetchRef.current) {
      skipNextFetchRef.current = false;
      return;
    }

    const controller = new AbortController();
    const { signal } = controller;

    (async () => {
      fetchPackage(signal);
    })();
    return () => {
      controller.abort();
    };
  }, [dateRange?.fromDate?.valueOf(), dateRange?.toDate?.valueOf(), JSON.stringify(rooms)]);

  useEffect(() => {
    const params = new URLSearchParams(location.search);
    const rooms = getRoomsFromParams(params, 'rooms');
    const from = getDateAsDateFromParams(params, 'from') || getDateAsDateFromParams(params, 'startDate');
    const to = getDateAsDateFromParams(params, 'to') || getDateAsDateFromParams(params, 'endDate');

    if (rooms) {
      setRooms(rooms);
    }

    if (from && duration) {
      const durationTo = new Date(Date.UTC(from.getFullYear(), from.getMonth(), from.getDate() + duration));

      setDateRange({ fromDate: from, toDate: durationTo });
    } else if (from && to) {
      setDateRange({ fromDate: from, toDate: to });
    } else if (duration) {
      const now = new Date();

      const durationFrom = new Date(Date.UTC(now.getFullYear(), now.getMonth() + 3, now.getDate()));
      const durationTo = new Date(Date.UTC(durationFrom.getFullYear(), durationFrom.getMonth(), durationFrom.getDate() + duration));

      setDateRange({ fromDate: durationFrom, toDate: durationTo });
    } else {
      const now = new Date();
      const startDate = addMonths(now, 1);
      const endDate = addDays(startDate, 7);

      setDateRange({ fromDate: startDate, toDate: endDate });
    }

    setLoaded(true);
  }, [location]);

  useEffect(() => {
    if (searchType === 1 && searchResults.length > 0) {
      const first = searchResults[0];

      setDateRange({
        fromDate: new Date(first.fromDate),
        toDate: new Date(first.toDate)
      });

      setPackageProductName(first.name);
      setPrice(first.price);
      setCurrencyCode(first.currencyCode);

      skipNextFetchRef.current = true;
    }
  }, [searchResults, searchType]);

  const personCount = rooms.reduce((s, r) => s + r.adults + r.children, 0);
  const durationCount = 0;
  const priceText = formatPriceByMode(
    price,
    priceMode,
    personCount,
    durationCount,
    translations.PRODUCT.PER_PERSON,
    translations.PRODUCT.PER_NIGHT,
    translations.PRODUCT.PER_PERSON_PER_NIGHT,
    currencyCode
  );

  return (
    <div className="booking-product">
      <Header name={packageProductName} rating={rating} priceText={priceText} isLoading={isLoading} hasFlight={hasFlight} hasTransfer={hasTransfer} />
      <div className="booking-product__body">
        <Rooms rooms={rooms} isDisabled={roomsIsDisabled} setIsDisabled={setRoomsIsDisabled} onChange={handleRoomChange} />
        {displayMode === 'calendar' && (
          <Dates
            value={dateRange}
            duration={duration}
            isLoading={isLoading}
            onChange={handleDateChange}
            availableDatePairs={searchResults.map((item) => ({
              fromDate: new Date(item.fromDate),
              toDate: new Date(item.toDate)
            }))}
          />
        )}
        {displayMode === 'list' && <ListView searchResults={searchResults} onSelect={handleDateSelect} />}
      </div>
      <Footer priceText={priceText} isLoading={isLoading} isOffer={isOffer} roomsIsDisabled={roomsIsDisabled} handleBookClick={handleBookClick} />
    </div>
  );
};

export default Product;
