import React, { useContext, useEffect, useRef, useState } from 'react';
import { concat, first, groupBy, isEmpty, minBy, orderBy } from 'lodash';
import { ExtendedFlightSearchResponseItem, Filters, SortByType } from '../../../types';
import { getArrivalSegment, getDepartureSegment, getNumberOfStops } from '../../../utils/flight-utils';
import { dateToDateStruct, durationInTicksInMinutes, rangeFromDateTimeInMinutes } from '../../../../shared/utils/localization-util';
import { FlightSearchFinishResponse, FlightSearchRequest, FlightSearchResponse } from '@qite/tide-client';
import { v4 as uuidv4 } from 'uuid';
import { getDateAsDateFromParams, getNumberFromParams, getStringFromParams } from '../../../../shared/utils/query-string-util';

interface SearchState {
  requestId: number | null;
  results: ExtendedFlightSearchResponseItem[];
  sequenceIds: number[];
  lastSequenceId: number | null;
  loading: boolean;
}

interface FlightFilterContextProps {
  children: React.ReactNode;
  tideConnection: any;
}

interface FlightSearchContext {
  // Search States
  isHubReady: boolean;
  searchRequestId: number;
  detailesSearchRequestId: number;
  valuesManuallyChanged: boolean;
  flightsLoading: boolean;
  flightSearchDetailsLoading: boolean;
  numberOfTravellers: number;

  // Search Results
  flightSearchResults: ExtendedFlightSearchResponseItem[];
  flightDetailsSearchResults: ExtendedFlightSearchResponseItem[];
  filteredResults: ExtendedFlightSearchResponseItem[];

  // Filter
  filters: Filters;
  sortByTypes: SortByType[];
  selectedSortByType: SortByType;

  airlineResults: ExtendedFlightSearchResponseItem[];
  numberOfStopsResults: ExtendedFlightSearchResponseItem[];
  departureRangeResults: ExtendedFlightSearchResponseItem[];
  departureAirportsResults: ExtendedFlightSearchResponseItem[];
  arrivalAirportsResults: ExtendedFlightSearchResponseItem[];
  travelTimeResults: ExtendedFlightSearchResponseItem[];
  priceResults: ExtendedFlightSearchResponseItem[];

  onFlightSearch: (selectedFlight?: ExtendedFlightSearchResponseItem) => void;
  onCancelSearch: () => void;
  // Filter Setters
  setFilters: (filters: Filters) => void;
  setSelectedSortByType: (val: SortByType) => void;
  resetFilters: () => void;
  setValuesManuallyChanged: (val: boolean) => void;
  onFilter: () => void;
}

const initialState: FlightSearchContext = {
  isHubReady: false,
  searchRequestId: 0,
  detailesSearchRequestId: 0,
  valuesManuallyChanged: false,
  flightsLoading: false,
  flightSearchDetailsLoading: false,
  numberOfTravellers: 0,
  flightSearchResults: [],
  flightDetailsSearchResults: [],
  filters: { airlines: [], numberOfStops: [], departureRanges: [], departureAirports: [], arrivalAirports: [], travelTimes: [], prices: [] } as Filters,
  filteredResults: [],

  sortByTypes: [
    { direction: 'asc', label: 'price' } as SortByType,
    { direction: 'desc', label: 'price' } as SortByType,
    { direction: 'asc', label: 'departureTime' } as SortByType,
    { direction: 'desc', label: 'departureTime' } as SortByType,
    { direction: 'asc', label: 'durationInTicks' } as SortByType,
    { direction: 'desc', label: 'durationInTicks' } as SortByType
  ],
  selectedSortByType: { direction: 'asc', label: 'price' } as SortByType,

  airlineResults: [],
  numberOfStopsResults: [],
  departureRangeResults: [],
  departureAirportsResults: [],
  arrivalAirportsResults: [],
  travelTimeResults: [],
  priceResults: [],

  onFlightSearch: () => {},
  onCancelSearch: () => {},
  setFilters: () => {},
  onFilter: () => {},
  setSelectedSortByType: () => {},
  setValuesManuallyChanged: () => {},
  resetFilters: () => {}
};

const FlightSearchContext = React.createContext<FlightSearchContext>(initialState);

const mapItems = (response: FlightSearchResponse) =>
  response.items.map(
    (item) =>
      ({
        ...item,
        guid: uuidv4(),
        requestId: response.requestId
      } as ExtendedFlightSearchResponseItem)
  );

const checkIfFinished = (state: SearchState) => {
  console.log('Checking if finished with state:', state);
  if (state.lastSequenceId === null) return false;

  for (let i = 0; i <= state.lastSequenceId; i++) {
    if (!state.sequenceIds.includes(i)) return false;
  }

  return true;
};

export const FlightSearchProvider: React.FC<FlightFilterContextProps> = ({ children, tideConnection }) => {
  const [regularSearch, setRegularSearch] = useState<SearchState>({
    requestId: null,
    results: [],
    sequenceIds: [],
    lastSequenceId: null,
    loading: true
  });

  const [detailSearch, setDetailSearch] = useState<SearchState>({
    requestId: null,
    results: [],
    sequenceIds: [],
    lastSequenceId: null,
    loading: true
  });

  const hubProxyRef = useRef<any>(null);
  const [isHubReady, setIsHubReady] = useState<boolean>(false);

  // FILTER States
  const [filters, setFilters] = useState<Filters>(initialState.filters);
  const [groupedResults, setGroupedResults] = useState<ExtendedFlightSearchResponseItem[]>([]);
  const [filteredResults, setFilteredResults] = useState<ExtendedFlightSearchResponseItem[]>(initialState.filteredResults);

  const [valuesManuallyChanged, setValuesManuallyChanged] = useState<boolean>(initialState.valuesManuallyChanged);

  const [airlineResults, setAirlineResults] = useState<ExtendedFlightSearchResponseItem[]>([]);
  const [numberOfStopsResults, setNumberOfStopsResults] = useState<ExtendedFlightSearchResponseItem[]>([]);
  const [departureRangeResults, setDepartureRangeResults] = useState<ExtendedFlightSearchResponseItem[]>([]);
  const [departureAirportsResults, setDepartureAirportsResults] = useState<ExtendedFlightSearchResponseItem[]>([]);
  const [arrivalAirportsResults, setArrivalAirportsResults] = useState<ExtendedFlightSearchResponseItem[]>([]);
  const [travelTimeResults, setTravelTimeResults] = useState<ExtendedFlightSearchResponseItem[]>([]);
  const [priceResults, setPriceResults] = useState<ExtendedFlightSearchResponseItem[]>([]);

  const [sortByTypes] = useState<SortByType[]>(initialState.sortByTypes);
  const [selectedSortByType, setSelectedSortByType] = useState<SortByType>(initialState.selectedSortByType);

  const [numberOfTravellers, setNumberOfTravellers] = useState<number>(0);

  // Connect to SignalR of Tide and set up listeners for results
  useEffect(() => {
    if (typeof window === 'undefined') return;
    console.log('Setting up SignalR connection for flight search results');
    // @ts-ignore: SignalR augments jQuery
    import('signalr-no-jquery').then(({ hubConnection }) => {
      const connection = hubConnection(tideConnection.host, { useDefaultPath: true });
      const hubProxy = connection.createHubProxy('Search');

      hubProxy.on('FlightResponse', (response: FlightSearchResponse) => {
        console.log('FlightResponse received:', response);

        setRegularSearch((prev) => {
          if (response.requestId !== prev.requestId) return prev;

          const updated = {
            ...prev,
            results: [...prev.results, ...mapItems(response)],
            sequenceIds: prev.sequenceIds.includes(response.sequenceId) ? prev.sequenceIds : [...prev.sequenceIds, response.sequenceId]
          };

          return {
            ...updated,
            loading: !checkIfFinished(updated)
          };
        });

        setDetailSearch((prev) => {
          if (response.requestId !== prev.requestId) return prev;

          const updated = {
            ...prev,
            results: [...prev.results, ...mapItems(response)],
            sequenceIds: prev.sequenceIds.includes(response.sequenceId) ? prev.sequenceIds : [...prev.sequenceIds, response.sequenceId]
          };
          return {
            ...updated,
            loading: !checkIfFinished(updated)
          };
        });
      });

      hubProxy.on('FinishResponse', (response: FlightSearchFinishResponse) => {
        console.log(`FinishResponse received for id ${response.requestId}, response:`, response);

        setRegularSearch((prev) => {
          if (response.requestId !== prev.requestId) return prev;

          const updated = {
            ...prev,
            lastSequenceId: response.sequenceId
          };

          return {
            ...updated,
            loading: !checkIfFinished(updated)
          };
        });

        setDetailSearch((prev) => {
          if (response.requestId !== prev.requestId) return prev;

          const updated = {
            ...prev,
            lastSequenceId: response.sequenceId
          };

          return {
            ...updated,
            loading: !checkIfFinished(updated)
          };
        });
      });

      connection.start().then(() => {
        hubProxyRef.current = hubProxy;
        setTimeout(() => {
          setIsHubReady(true);
        }, 1000); // Delay to ensure hub is ready
      });

      return () => {
        connection.stop();
      };
    });
  }, []);

  const onFlightSearch = async (selectedFlight?: ExtendedFlightSearchResponseItem) => {
    const hubProxy = hubProxyRef.current;
    if (!hubProxy) {
      console.error('Hub proxy not ready yet');
      return;
    }

    const params = new URLSearchParams(location.search);
    let fromDate = getDateAsDateFromParams(params, 'fromDate');
    let toDate = getDateAsDateFromParams(params, 'toDate');

    let departureAirport = getStringFromParams(params, 'departureAirport');
    let destinationAirport = getStringFromParams(params, 'destinationAirport');
    let returnAirport = getStringFromParams(params, 'returnAirport');
    let onlyDirect = getStringFromParams(params, 'onlyDirect') === 'true' ? true : null;
    let luggageIncluded = getStringFromParams(params, 'luggageIncluded') === 'true' ? true : null;
    let travelClass = getStringFromParams(params, 'travelClass');
    let adults = getNumberFromParams(params, 'adults');
    let kids = getNumberFromParams(params, 'kids');
    let babies = getNumberFromParams(params, 'babies');

    let request = {
      officeId: tideConnection?.officeId ?? 0,
      catalogueId: first(tideConnection?.catalogueIds) ?? 0,
      departureAirportCode: departureAirport,
      arrivalAirportCode: destinationAirport,
      returnAirportCode: returnAirport,
      luggageIncluded: luggageIncluded,
      maxStops: onlyDirect ? 0 : null,
      travelClass: travelClass,
      pax: concat(
        Array.from({ length: adults ?? 0 }, (_, index) => ({
          id: index,
          age: 31
        })),
        Array.from({ length: kids ?? 0 }, (_, index) => ({
          id: index + (adults ?? 0),
          age: 8
        })),
        Array.from({ length: babies ?? 0 }, (_, index) => ({
          id: index + (adults ?? 0) + (kids ?? 0),
          age: 1
        }))
      )
    } as FlightSearchRequest;

    if (selectedFlight) {
      request.outward = {
        date: fromDate ? dateToDateStruct(fromDate) : dateToDateStruct(getDepartureSegment(selectedFlight.outward)?.departureDateTime),
        flightCode: selectedFlight.outward.code,
        flightNumbers: selectedFlight.outward.segments.map((s) => s.flightNumber),
        airlines: [selectedFlight.airlineCode]
      };
      request.return = {
        date: toDate ? dateToDateStruct(toDate) : dateToDateStruct(getDepartureSegment(selectedFlight.return)?.departureDateTime),
        flightCode: selectedFlight.return.code,
        flightNumbers: selectedFlight.return.segments.map((s) => s.flightNumber),
        airlines: [selectedFlight.airlineCode]
      };
      request.flightProvider = selectedFlight.provider;
      request.vendorConfigurationId = selectedFlight.vendorConfigurationId;
    } else {
      if (fromDate) {
        request.outward = {
          date: dateToDateStruct(fromDate)
        };
      }

      if (toDate) {
        request.return = {
          date: dateToDateStruct(toDate)
        };
      }
    }
    setNumberOfTravellers(request.pax.length);

    try {
      if (selectedFlight) {
        if (detailSearch.requestId) {
          hubProxy.invoke('CancelSearch', detailSearch.requestId);
        }
        const requestId = await hubProxy.invoke('SearchFlights', request);
        console.log(`Invoking detailed search with for id ${requestId} with request:`, request);

        setDetailSearch({
          requestId,
          results: [],
          sequenceIds: [],
          lastSequenceId: null,
          loading: true
        });
      } else {
        if (regularSearch.requestId) {
          hubProxy.invoke('CancelSearch', regularSearch.requestId);
        }
        const requestId = await hubProxy.invoke('SearchFlights', request);
        console.log(`Invoking regular search with for id ${requestId} with request:`, request);

        setRegularSearch({
          requestId,
          results: [],
          sequenceIds: [],
          lastSequenceId: null,
          loading: true
        });
      }
    } catch (err) {
      console.error('Error invoking search:', err);
    }
  };

  const onCancelSearch = async () => {
    const hubProxy = hubProxyRef.current;
    if (detailSearch.requestId) {
      hubProxy.invoke('CancelSearch', detailSearch.requestId);
    }
  };

  // useEffect(() => {
  //   console.log('detailSearch search results updated:', detailSearch.results);
  //   if (!isEmpty(detailSearch.results)) {
  //     const grouped = groupBy(detailSearch.results, (x) => x.flightRouteId);
  //     const lowestPerRoute = Object.values(grouped).map((group) => minBy(group, (item) => item.price));
  //     console.log('Lowest per route:', lowestPerRoute.filter(Boolean) as ExtendedFlightSearchResponseItem[]);
  //     setDetailSearch(prev => {
  //       const updated = {
  //         ...prev,
  //         grouped: lowestPerRoute.filter(Boolean) as ExtendedFlightSearchResponseItem[]
  //       };

  //       return {
  //         ...updated
  //       };
  //     });
  //   } else {
  //   }
  // }, [detailSearch.results]);

  useEffect(() => {
    if (!isEmpty(regularSearch.results)) {
      onFilter();
    } else {
      setFilteredResults([]);
    }
  }, [regularSearch.results]);

  // Sort the grouped results
  useEffect(() => {
    if (groupedResults) {
      setFilteredResults(sortResults(groupedResults));
    }
  }, [selectedSortByType, groupedResults]);

  const sortResults = (results: ExtendedFlightSearchResponseItem[]) => {
    switch (selectedSortByType.label) {
      case 'departureTime':
        return orderBy(
          results,
          [(result) => getDepartureSegment(result.outward)?.departureDateTime],
          [selectedSortByType.direction] // or "desc"
        );
      case 'durationInTicks':
        return orderBy(
          results,
          [(result) => durationInTicksInMinutes(result.outward?.durationInTicks ?? 0)],
          [selectedSortByType.direction] // or "desc"
        );
      default:
        return orderBy(results, [selectedSortByType.label], [selectedSortByType.direction]);
    }
  };

  useEffect(() => {
    if (filters && !isEmpty(filters)) {
      onFilter();
    }
  }, [filters]);

  const onFilter = () => {
    if (!isEmpty(regularSearch.results)) {
      const filterByAirline = (result: ExtendedFlightSearchResponseItem) => {
        if (isEmpty(filters.airlines)) {
          return true;
        }
        return filters.airlines.some((airline) => airline.id === result.airlineCode);
      };

      const filterByNumberOfStops = (result: ExtendedFlightSearchResponseItem) => {
        if (isEmpty(filters.numberOfStops)) {
          return true;
        }
        const outwardStops = getNumberOfStops(result.outward);
        const returnStops = getNumberOfStops(result.return);
        return filters.numberOfStops.some((stop) => stop.id === outwardStops && stop.id === returnStops);
      };

      const filterByDepartureRange = (result: ExtendedFlightSearchResponseItem) => {
        if (isEmpty(filters.departureRanges)) {
          return true;
        }
        const departureRange = rangeFromDateTimeInMinutes(getDepartureSegment(result.outward)?.departureDateTime);
        return filters.departureRanges.some((range) => range.id === departureRange);
      };

      const filterByDepartureAirport = (result: ExtendedFlightSearchResponseItem) => {
        if (isEmpty(filters.departureAirports)) {
          return true;
        }
        return filters.departureAirports.some((airport) => airport.id === getDepartureSegment(result.outward)?.departureAirportCode);
      };

      const filterByArrivalAirport = (result: ExtendedFlightSearchResponseItem) => {
        if (isEmpty(filters.arrivalAirports)) {
          return true;
        }
        return filters.arrivalAirports.some((airport) => airport.id === getArrivalSegment(result.outward)?.arrivalAirportCode);
      };

      const filterByTravelTime = (result: ExtendedFlightSearchResponseItem) => {
        if (isEmpty(filters.travelTimes) || filters.travelTimes.length !== 2 || result.outward?.durationInTicks === undefined) {
          return true;
        }

        return (
          durationInTicksInMinutes(result.outward?.durationInTicks) >= filters.travelTimes[0].lowestPrice &&
          durationInTicksInMinutes(result.outward?.durationInTicks) <= filters.travelTimes[1].lowestPrice
        );
      };

      const filterByPrice = (result: ExtendedFlightSearchResponseItem) => {
        if (isEmpty(filters.prices) || filters.prices.length !== 2 || result.price === undefined) {
          return true;
        }

        return result.price >= filters.prices[0].lowestPrice && result.price <= filters.prices[1].lowestPrice;
      };

      const filtered = regularSearch.results.filter(
        (result) =>
          filterByAirline(result) &&
          filterByNumberOfStops(result) &&
          filterByDepartureRange(result) &&
          filterByDepartureAirport(result) &&
          filterByArrivalAirport(result) &&
          filterByTravelTime(result) &&
          filterByPrice(result)
      );
      const grouped = groupBy(filtered, (x) => x.flightRouteId);
      const lowestPerRoute = Object.values(grouped).map((group) => minBy(group, (item) => item.price));
      setGroupedResults(lowestPerRoute.filter(Boolean) as ExtendedFlightSearchResponseItem[]);

      // Set filter option
      setAirlineResults(
        regularSearch.results.filter(
          (result) =>
            filterByNumberOfStops(result) &&
            filterByDepartureRange(result) &&
            filterByDepartureAirport(result) &&
            filterByArrivalAirport(result) &&
            filterByTravelTime(result) &&
            filterByPrice(result)
        )
      );
      setNumberOfStopsResults(
        regularSearch.results.filter(
          (result) =>
            filterByAirline(result) &&
            filterByDepartureRange(result) &&
            filterByDepartureAirport(result) &&
            filterByArrivalAirport(result) &&
            filterByTravelTime(result) &&
            filterByPrice(result)
        )
      );
      setDepartureRangeResults(
        regularSearch.results.filter(
          (result) =>
            filterByAirline(result) &&
            filterByNumberOfStops(result) &&
            filterByDepartureAirport(result) &&
            filterByArrivalAirport(result) &&
            filterByTravelTime(result) &&
            filterByPrice(result)
        )
      );
      setDepartureAirportsResults(
        regularSearch.results.filter(
          (result) =>
            filterByAirline(result) &&
            filterByNumberOfStops(result) &&
            filterByDepartureRange(result) &&
            filterByArrivalAirport(result) &&
            filterByTravelTime(result) &&
            filterByPrice(result)
        )
      );
      setArrivalAirportsResults(
        regularSearch.results.filter(
          (result) =>
            filterByAirline(result) &&
            filterByNumberOfStops(result) &&
            filterByDepartureRange(result) &&
            filterByDepartureAirport(result) &&
            filterByTravelTime(result) &&
            filterByPrice(result)
        )
      );
      setTravelTimeResults(
        regularSearch.results.filter(
          (result) =>
            filterByAirline(result) &&
            filterByNumberOfStops(result) &&
            filterByDepartureRange(result) &&
            filterByDepartureAirport(result) &&
            filterByArrivalAirport(result)
        )
      );
      setPriceResults(
        regularSearch.results.filter(
          (result) =>
            filterByAirline(result) &&
            filterByNumberOfStops(result) &&
            filterByDepartureRange(result) &&
            filterByDepartureAirport(result) &&
            filterByArrivalAirport(result) &&
            filterByTravelTime(result)
        )
      );
    }
  };

  const resetFilters = () => {
    setFilters({ airlines: [], numberOfStops: [], departureRanges: [], departureAirports: [], arrivalAirports: [], travelTimes: [], prices: [] });
  };

  return (
    <FlightSearchContext.Provider
      value={{
        flightsLoading: regularSearch.loading,
        flightSearchDetailsLoading: detailSearch.loading,
        isHubReady,
        valuesManuallyChanged,
        setValuesManuallyChanged,
        searchRequestId: regularSearch.requestId ?? 0,
        detailesSearchRequestId: detailSearch.requestId ?? 0,
        onFlightSearch,
        onCancelSearch,
        flightSearchResults: regularSearch.results,
        flightDetailsSearchResults: detailSearch.results,
        filters,
        filteredResults,
        sortByTypes,
        selectedSortByType,
        setFilters,
        onFilter,
        setSelectedSortByType,
        resetFilters,
        airlineResults,
        numberOfStopsResults,
        departureRangeResults,
        departureAirportsResults,
        arrivalAirportsResults,
        travelTimeResults,
        priceResults,
        numberOfTravellers
      }}>
      {children}
    </FlightSearchContext.Provider>
  );
};

export const useFlightSearch = (): FlightSearchContext => {
  const context = useContext(FlightSearchContext);
  if (!context) {
    throw new Error('useFlightSearch must be used within a FlightSearchProvider');
  }
  return context;
};
