import React, { useContext, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { first } from 'lodash';
import { SearchResultsRootState } from '../../store/search-results-store';
import { getTranslations } from '../../../shared/utils/localization-util';
import SearchResultsConfigurationContext from '../../search-results-configuration-context';
import { PackagingAccommodationResponse } from '@qite/tide-client';
import { confirmExcursionForDay, setFlyInIsOpen, setSelectedExcursionSearchResult } from '../../store/search-results-slice';
import { format, parseISO } from 'date-fns';

type ExcursionOption = PackagingAccommodationResponse['rooms'][number]['options'][number];

type GroupedExcursion = {
  accommodationCode: string;
  accommodationName: string;
  options: ExcursionOption[];
};

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

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

const getExcursionDayKey = (date: string | Date) => {
  const parsed = typeof date === 'string' ? parseISO(date) : date;
  return format(parsed, 'yyyy-MM-dd');
};

const getOptionPaxIds = (option: ExcursionOption): number[] => {
  return Array.isArray(option.paxIds) ? Array.from(new Set(option.paxIds)).sort((a, b) => a - b) : [];
};

const optionAppliesToPax = (option: ExcursionOption, paxId: number) => {
  return getOptionPaxIds(option).includes(paxId);
};

const optionAppliesToAllTravellers = (option: ExcursionOption, travellerCount: number) => {
  const paxIds = getOptionPaxIds(option);
  const expected = Array.from({ length: travellerCount }, (_, i) => i);

  return paxIds.length === expected.length && paxIds.every((id, index) => id === expected[index]);
};

const groupOptionsByExcursion = (options: ExcursionOption[]): GroupedExcursion[] => {
  const groupedMap = new Map<string, GroupedExcursion>();

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

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

    groupedMap.get(key)!.options.push(option);
  });

  return Array.from(groupedMap.values());
};

const ExcursionDetails: React.FC = () => {
  const context = useContext(SearchResultsConfigurationContext);
  const dispatch = useDispatch();

  const { selectedExcursionSearchResult, editablePackagingEntry, excursionSearchParams } = useSelector((state: SearchResultsRootState) => state.searchResults);

  if (!context || !selectedExcursionSearchResult || !editablePackagingEntry || !excursionSearchParams?.date) {
    return null;
  }

  const translations = getTranslations(context.languageCode ?? 'en-GB');
  const travellerCount = editablePackagingEntry.pax.length;

  const allOptions = useMemo(() => {
    return selectedExcursionSearchResult.rooms.flatMap((room) => room.options ?? []);
  }, [selectedExcursionSearchResult]);

  const sharedOptions = useMemo(() => {
    return allOptions.filter((option) => optionAppliesToAllTravellers(option, travellerCount));
  }, [allOptions, travellerCount]);

  const sharedExcursions = useMemo(() => {
    return groupOptionsByExcursion(sharedOptions);
  }, [sharedOptions]);

  const paxGroups = useMemo(() => {
    return editablePackagingEntry.pax.map((pax) => {
      const paxOptions = allOptions.filter((option) => optionAppliesToPax(option, pax.id) && !optionAppliesToAllTravellers(option, travellerCount));

      return {
        pax,
        paxId: pax.id,
        excursions: groupOptionsByExcursion(paxOptions)
      };
    });
  }, [editablePackagingEntry.pax, allOptions, travellerCount]);

  const getSelectedSharedOption = () => {
    return sharedOptions.find((option) => option.isSelected);
  };

  const getSelectedSharedOptionForExcursion = (accommodationCode: string) => {
    return sharedOptions.find((option) => option.accommodationCode === accommodationCode && option.isSelected);
  };

  const getSelectedOptionForPax = (paxId: number) => {
    return allOptions.find((option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option, travellerCount) && option.isSelected);
  };

  const getSelectedOptionForExcursion = (paxId: number, accommodationCode: string) => {
    return allOptions.find(
      (option) =>
        optionAppliesToPax(option, paxId) &&
        !optionAppliesToAllTravellers(option, travellerCount) &&
        option.accommodationCode === accommodationCode &&
        option.isSelected
    );
  };

  const handlePick = (selectedGuid?: string, paxId?: number) => {
    const updatedExcursionSearchResult: PackagingAccommodationResponse = {
      ...selectedExcursionSearchResult,
      rooms: selectedExcursionSearchResult.rooms.map((room) => ({
        ...room,
        options: room.options.map((option) => {
          const isSharedOption = optionAppliesToAllTravellers(option, travellerCount);

          if (paxId === undefined) {
            if (!isSharedOption) {
              return option;
            }

            return {
              ...option,
              isSelected: option.guid === selectedGuid
            };
          }

          if (isSharedOption || !optionAppliesToPax(option, paxId)) {
            return option;
          }

          return {
            ...option,
            isSelected: option.guid === selectedGuid
          };
        })
      }))
    };

    dispatch(setSelectedExcursionSearchResult(updatedExcursionSearchResult));
  };

  const calculateTotalPrice = () => {
    const selectedOptions = allOptions.filter((option) => option.isSelected);
    const totalPrice = selectedOptions.reduce((total, option) => total + (option.price || 0), 0);

    return formatPrice(totalPrice, selectedExcursionSearchResult.currencyCode);
  };

  const getSharedPriceDifference = (accommodationCode: string) => {
    const currentSelectedShared = getSelectedSharedOption();

    let targetPrice = 0;

    const selectedOption = getSelectedSharedOptionForExcursion(accommodationCode);

    if (selectedOption?.price) {
      targetPrice = selectedOption.price;
    } else {
      const firstOption = sharedOptions.find((option) => option.accommodationCode === accommodationCode);
      targetPrice = firstOption?.price || 0;
    }

    return targetPrice - (currentSelectedShared?.price || 0);
  };

  const getPriceDifference = (currentSelectedPrice: number | undefined, paxId: number, accommodationCode: string) => {
    let targetPrice = 0;

    const selectedOption = getSelectedOptionForExcursion(paxId, accommodationCode);

    if (selectedOption?.price) {
      targetPrice = selectedOption.price;
    } else {
      const firstOption = allOptions.find(
        (option) => optionAppliesToPax(option, paxId) && !optionAppliesToAllTravellers(option, travellerCount) && option.accommodationCode === accommodationCode
      );

      targetPrice = firstOption?.price || 0;
    }

    return targetPrice - (currentSelectedPrice || 0);
  };

  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 handleConfirm = () => {
    const dayKey = getExcursionDayKey(excursionSearchParams.date);

    dispatch(
      confirmExcursionForDay({
        dayKey,
        excursion: selectedExcursionSearchResult
      })
    );

    dispatch(setFlyInIsOpen(false));
  };

  return (
    <>
      <div className="flyin__content">
        {sharedExcursions.length > 0 && (
          <div className="flyin__acco">
            <h3 className="flyin__acco__room-title">{translations.QSM.ALL_TRAVELERS}</h3>

            <div className="flyin__acco__cards">
              {sharedExcursions.map((excursion) => {
                const selectedOption = getSelectedSharedOptionForExcursion(excursion.accommodationCode);
                const priceDifference = getSharedPriceDifference(excursion.accommodationCode);

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

                    <div className="flyin__acco__footer">
                      <div className="flyin__acco__footer__actions">
                        <button
                          className={selectedOption ? 'cta cta--select cta--selected' : 'cta cta--select'}
                          onClick={() => handlePick(selectedOption ? selectedOption.guid : first(excursion.options)?.guid)}>
                          {selectedOption ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT}
                        </button>

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

        {paxGroups.map(({ pax, paxId, excursions }) => {
          if (excursions.length === 0) {
            return null;
          }

          const selectedPaxOption = getSelectedOptionForPax(paxId);

          return (
            <div className="flyin__acco" key={`pax-${pax.id}`}>
              <h3 className="flyin__acco__room-title">
                {translations.SUMMARY.TRAVELER} {pax.id + 1}
              </h3>

              <div className="flyin__acco__cards">
                {excursions.map((excursion) => {
                  const selectedOption = getSelectedOptionForExcursion(paxId, excursion.accommodationCode);
                  const priceDifference = getPriceDifference(selectedPaxOption?.price, paxId, excursion.accommodationCode);

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

                      <div className="flyin__acco__footer">
                        <div className="flyin__acco__footer__actions">
                          <button
                            className={
                              selectedPaxOption?.accommodationCode === excursion.accommodationCode ? 'cta cta--select cta--selected' : 'cta cta--select'
                            }
                            onClick={() => handlePick(selectedOption ? selectedOption.guid : first(excursion.options)?.guid, paxId)}>
                            {selectedPaxOption?.accommodationCode === excursion.accommodationCode ? translations?.SHARED.SELECTED : translations?.SHARED.SELECT}
                          </button>

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

      <div className="flyin__footer">
        <div className="flyin__footer__price">
          {translations.SHARED.TOTAL_PRICE}: {calculateTotalPrice()}
        </div>

        <button type="button" className="cta cta--primary" onClick={handleConfirm}>
          {translations?.QSM.CONFIRM}
        </button>
      </div>
    </>
  );
};

export default ExcursionDetails;
