import React, { useContext, useMemo, useRef, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import ItemPicker from '../../../qsm/components/item-picker';
import { SearchResultsRootState } from '../../../search-results/store/search-results-store';
import SearchResultsConfigurationContext from '../../../search-results/search-results-configuration-context';
import { getTranslations } from '../../utils/localization-util';
import { setBookingPackageDetails } from '../../../search-results/store/search-results-slice';
import { BookingPackageOption } from '@qite/tide-client';
import { PickerItem } from '../../types';
import { first } from 'lodash';
import Spinner from '../../../search-results/components/spinner/spinner';

type GroupTourFlyInProps = {
  isLoading: boolean;
  isOpen: boolean;
  setIsOpen: (open: boolean) => void;
};

type GroupedAccommodation = {
  accommodationCode: string;
  accommodationName: string;
  regimes: PickerItem[];
};

type RoomOption = BookingPackageOption['rooms'][number]['options'][number];

const formatPrice = (price?: number, currencyCode = 'EUR') => {
  if (typeof price !== 'number') return '';

  return new Intl.NumberFormat('nl-BE', {
    style: 'currency',
    currency: currencyCode
  }).format(price);
};

const GroupTourFlyIn: React.FC<GroupTourFlyInProps> = ({ isLoading, isOpen, setIsOpen }) => {
  const dispatch = useDispatch();
  const context = useContext(SearchResultsConfigurationContext);
  const language = context?.languageCode ?? 'en-GB';
  const translations = getTranslations(language);

  if (isLoading) {
    return <>{context?.customSpinner ?? <Spinner label={translations.SRP.LOADING_OPTIONS} />}</>;
  }

  const { bookingPackageDetails } = useSelector((state: SearchResultsRootState) => state.searchResults);

  const selectedBookingPackageDetails = useMemo<BookingPackageOption | undefined>(() => {
    return bookingPackageDetails?.options?.find((x) => x.isSelected);
  }, [bookingPackageDetails]);

  const packageKey = `${bookingPackageDetails?.transactionId}-${selectedBookingPackageDetails?.id}`;

  const initialSelectedOptionsRef = useRef<Record<string, Record<number, RoomOption | undefined>>>({});

  useEffect(() => {
    if (!packageKey || !selectedBookingPackageDetails?.rooms) return;

    if (!initialSelectedOptionsRef.current[packageKey]) {
      initialSelectedOptionsRef.current[packageKey] = {};
    }

    selectedBookingPackageDetails.rooms.forEach((room, roomIndex) => {
      if (!initialSelectedOptionsRef.current[packageKey][roomIndex]) {
        initialSelectedOptionsRef.current[packageKey][roomIndex] = room.options.find((option) => option.isSelected);
      }
    });
  }, [packageKey, selectedBookingPackageDetails]);

  const groupedRooms = useMemo(() => {
    if (!selectedBookingPackageDetails?.rooms) return [];

    return selectedBookingPackageDetails.rooms.map((room) => {
      const groupedMap = new Map<string, GroupedAccommodation>();

      room.options.forEach((option) => {
        const key = option.accommodationCode;

        if (!groupedMap.has(key)) {
          groupedMap.set(key, {
            accommodationCode: option.accommodationCode,
            accommodationName: option.accommodationName,
            regimes: []
          });
        }

        groupedMap.get(key)?.regimes.push({
          id: option.entryLineGuid,
          label: option.regimeName ?? 'No regime'
        });
      });

      return Array.from(groupedMap.values());
    });
  }, [selectedBookingPackageDetails]);

  if (!bookingPackageDetails || !selectedBookingPackageDetails) {
    return null;
  }

  const getSelectedOptionForRoom = (roomIndex: number) => {
    return selectedBookingPackageDetails.rooms?.[roomIndex]?.options?.find((option) => option.isSelected);
  };

  const getSelectedOptionForAccommodation = (roomIndex: number, accommodationCode: string) => {
    return selectedBookingPackageDetails.rooms?.[roomIndex]?.options?.find((option) => option.accommodationCode === accommodationCode && option.isSelected);
  };

  const getInitialSelectedOptionForRoom = (roomIndex: number) => {
    if (!packageKey) return undefined;
    return initialSelectedOptionsRef.current[packageKey]?.[roomIndex];
  };

  const handlePick = (roomIndex: number, selectedGuid?: string) => {
    if (!bookingPackageDetails || !selectedBookingPackageDetails) return;

    const updatedBookingPackageDetails = {
      ...bookingPackageDetails,
      options: bookingPackageDetails.options.map((bookingOption) => {
        if (!bookingOption.isSelected) {
          return bookingOption;
        }

        return {
          ...bookingOption,
          rooms: bookingOption.rooms.map((room, currentRoomIndex) => {
            if (currentRoomIndex !== roomIndex) {
              return room;
            }

            return {
              ...room,
              options: room.options.map((roomOption) => ({
                ...roomOption,
                isSelected: roomOption.entryLineGuid === selectedGuid
              }))
            };
          })
        };
      })
    };

    dispatch(setBookingPackageDetails({ details: updatedBookingPackageDetails }));
  };

  const handleConfirm = () => {
    if (isOpen) {
      setIsOpen(false);
    }

    if (context?.onBook) {
      context.onBook(bookingPackageDetails);
    }
  };

  const getPriceDifference = (roomIndex: number, accommodationCode: string, regimeId?: string) => {
    const currentSelectedOption = getSelectedOptionForRoom(roomIndex);
    const currentSelectedPrice = currentSelectedOption?.price || 0;

    let targetOption;

    if (regimeId) {
      targetOption = selectedBookingPackageDetails.rooms?.[roomIndex]?.options?.find((option) => option.entryLineGuid === regimeId);
    } else {
      targetOption =
        getSelectedOptionForAccommodation(roomIndex, accommodationCode) ??
        selectedBookingPackageDetails.rooms?.[roomIndex]?.options?.find((option) => option.accommodationCode === accommodationCode);
    }

    return (targetOption?.price || 0) - currentSelectedPrice;
  };

  const formatPriceDifference = (difference: number, currencyCode: string) => {
    if (difference === 0) {
      return null;
    }

    const formattedAbsoluteValue = formatPrice(Math.abs(difference), currencyCode);
    return `${difference > 0 ? '+' : '-'} ${formattedAbsoluteValue}`;
  };

  const getPriceDifferenceClassName = (difference: number) => {
    if (difference < 0) {
      return 'flyin__acco__price flyin__acco__price--decrease';
    }

    if (difference > 0) {
      return 'flyin__acco__price flyin__acco__price--increase';
    }

    return 'flyin__acco__price';
  };

  const regimeFormatter = (roomIndex: number, accommodation: GroupedAccommodation, regimeId: string, label: string) => {
    const difference = getPriceDifference(roomIndex, accommodation.accommodationCode, regimeId);
    return `${label} ${difference !== 0 ? `(${formatPriceDifference(difference, bookingPackageDetails.currencyCode)})` : ''}`;
  };

  const adjustedTotalPrice = useMemo(() => {
    if (!selectedBookingPackageDetails.rooms || typeof selectedBookingPackageDetails.price !== 'number') {
      return 0;
    }

    const basePrice = selectedBookingPackageDetails.price;

    const totalDelta = selectedBookingPackageDetails.rooms.reduce((sum, room, roomIndex) => {
      const initialOption = getInitialSelectedOptionForRoom(roomIndex);
      const currentOption = room.options.find((option) => option.isSelected);

      const initialPrice = initialOption?.price || 0;
      const currentPrice = currentOption?.price || 0;

      return sum + (currentPrice - initialPrice);
    }, 0);

    return basePrice + totalDelta;
  }, [selectedBookingPackageDetails, packageKey]);

  return (
    <>
      <div className="flyin__content">
        {groupedRooms.map((roomAccommodations, roomIndex) => {
          const selectedRoomOption = getSelectedOptionForRoom(roomIndex);

          return (
            <div className="flyin__acco" key={`room-${roomIndex}`}>
              <h3 className="flyin__acco__room-title">Room {roomIndex + 1}</h3>
              <div className="flyin__acco__cards">
                {roomAccommodations.map((accommodation) => {
                  const selectedOption = getSelectedOptionForAccommodation(roomIndex, accommodation.accommodationCode);
                  const priceDifference = getPriceDifference(roomIndex, accommodation.accommodationCode);

                  return (
                    <div className="flyin__acco__card" key={`${roomIndex}-${accommodation.accommodationCode}`}>
                      <div className="flyin__acco__content">
                        <h4 className="flyin__acco__title">{accommodation.accommodationName}</h4>
                      </div>

                      <div className="flyin__acco__footer">
                        <ItemPicker
                          items={accommodation.regimes}
                          selection={selectedOption != null ? selectedOption.regimeName ?? 'No regime' : undefined}
                          label=""
                          placeholder="Select regime"
                          classModifier=""
                          onPick={(selected, selectedGuid) => handlePick(roomIndex, selectedGuid)}
                          valueFormatter={(id, label) => regimeFormatter(roomIndex, accommodation, id, label)}
                        />

                        <div className="flyin__acco__footer__actions">
                          <button
                            className={
                              selectedRoomOption?.accommodationCode === accommodation.accommodationCode ? 'cta cta--select cta--selected' : 'cta cta--select'
                            }
                            onClick={() => {
                              handlePick(roomIndex, selectedOption ? selectedOption.entryLineGuid : first(accommodation.regimes)?.id);
                            }}>
                            {selectedRoomOption?.accommodationCode === accommodation.accommodationCode
                              ? translations?.SHARED.SELECTED
                              : translations?.SHARED.SELECT}
                          </button>

                          <div className="flyin__acco__price__wrapper">
                            <span className={getPriceDifferenceClassName(priceDifference)}>
                              {formatPriceDifference(priceDifference, bookingPackageDetails.currencyCode)}
                            </span>
                          </div>
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>

      <div className="flyin__footer">
        <div className="flyin__footer__price">
          {translations.SHARED.TOTAL_PRICE}: {formatPrice(adjustedTotalPrice, bookingPackageDetails.currencyCode)}
        </div>

        <div className="flyin__button-wrapper">
          <button className="cta cta--select" onClick={handleConfirm}>
            {translations.PRODUCT.BOOK_NOW}
          </button>
        </div>
      </div>
    </>
  );
};

export default GroupTourFlyIn;
