import React, { useContext, useState } from 'react';
import { useSelector } from 'react-redux';
import {
  getDateFromParams,
  getFlightsFromParams,
  getNumberFromParams,
  getNumbersFromParams,
  getRoomsFromParams,
  getStringFromParams
} from '../../../shared/utils/query-string-util';
import {
  fetchPackage,
  setAccommodationViewId,
  setAgentAdressId,
  setBookingAttributes,
  setBookingNumber,
  setBookingOptions,
  setBookingType,
  setCalculateDeposit,
  setCurrentStep,
  setGeneratePaymentUrl,
  setLanguageCode,
  setOfficeId,
  setProductAttributes,
  setShowCommission,
  setSkipPayment,
  setTagIds,
  setTranslations
} from './booking-slice';

import { isNil } from 'lodash';
import { useEffect } from 'react';
import Loader from '../../../shared/components/loader';
import { setApiKey, setApiUrl } from '../../api-settings-slice';
import StepRoute from '../../components/step-route';
import SettingsContext from '../../settings-context';
import { useAppDispatch } from '../../store';
import Confirmation from '../confirmation/confirmation';
import Error from '../error/error';
import FlightOptionsForm from '../flight-options';
import OptionsForm from '../product-options/options-form';
import RoomOptionsForm from '../room-options';
import Sidebar from '../sidebar';
import Summary from '../summary/summary';
import TravelersForm from '../travelers-form/travelers-form';
import {
  CONFIRMATION_STEP,
  ERROR_STEP,
  FLIGHT_OPTIONS_FORM_STEP,
  OPTIONS_FORM_STEP,
  ROOM_OPTIONS_FORM_STEP,
  SUMMARY_STEP,
  TRAVELERS_FORM_STEP
} from './constants';
import {
  selectApiSettings,
  selectBookingAttributes,
  selectBookingNumber,
  selectBookingRooms,
  selectCurrentStep,
  selectIsUnavailable,
  selectPackageDetails,
  selectProductAttributes,
  selectTranslations
} from './selectors';

interface BookingProps {
  productCode: string;
  productName: string;
  thumbnailUrl?: string;
}

const BookingSelfContained: React.FC<BookingProps> = ({ productCode, productName, thumbnailUrl }) => {
  const {
    officeId,
    bookingOptions,
    showSidebarDeposit,
    showCommission,
    includeFlights,
    skipPaymentWithAgent,
    generatePaymentUrl,
    tagIds,
    agentAdressId,
    language,
    apiUrl,
    apiKey,
    translationFiles,
    customTranslationsUrl,
    roomOptions,
    flightOptions,
    accommodationViewId,
    isOffer,
    allowOption,
    travellers
  } = useContext(SettingsContext);

  // Render gate: when the host supplies a `customTranslationsUrl`, suppress the
  // initial render until the fetch resolves so end users never see a flash of
  // built-in copy that then jumps to the customer-specific labels. Starts true
  // when no fetch is needed.
  const [translationsReady, setTranslationsReady] = useState<boolean>(!customTranslationsUrl);

  const dispatch = useAppDispatch();

  const productAttributes = useSelector(selectProductAttributes);
  const bookingAttributes = useSelector(selectBookingAttributes);
  const rooms = useSelector(selectBookingRooms);
  const bookingNumber = useSelector(selectBookingNumber);
  const packageDetails = useSelector(selectPackageDetails);
  const isUnvailable = useSelector(selectIsUnavailable);
  const currentStep = useSelector(selectCurrentStep);
  const apiSettings = useSelector(selectApiSettings);
  const translations = useSelector(selectTranslations);

  useEffect(() => {
    dispatch(setSkipPayment(skipPaymentWithAgent ?? false));
    dispatch(setGeneratePaymentUrl(generatePaymentUrl ?? false));
  }, [skipPaymentWithAgent, generatePaymentUrl]);

  useEffect(() => {
    if (!isNil(apiUrl) && !isNil(apiKey)) {
      dispatch(setApiUrl(apiUrl));
      dispatch(setApiKey(apiKey));
    }
  }, [apiUrl, apiKey]);

  useEffect(() => {
    const params = new URLSearchParams(location.search);
    const startDate = getDateFromParams(params, 'startDate');
    const endDate = getDateFromParams(params, 'endDate');
    const catalogueId = getNumberFromParams(params, 'catalogueId') ?? getNumberFromParams(params, 'catalog');
    const rooms = getRoomsFromParams(params, 'rooms');
    const flight = getFlightsFromParams(params, 'flight');
    const flightRouteId = getStringFromParams(params, 'flightRouteId');
    const vendorConfigurationId = getNumberFromParams(params, 'vendorConfigurationId');
    const allotmentName = getStringFromParams(params, 'allotmentName');
    const allotmentIds = getNumbersFromParams(params, 'allotmentId');
    const tourCode = getStringFromParams(params, 'tourCode');
    const bookingNumber = params.get('bookingNr') ?? undefined;

    if (typeof window !== 'undefined') {
      window.scrollTo(0, 0);
    }

    if (!isNil(bookingNumber)) {
      dispatch(setBookingNumber(bookingNumber));
      dispatch(setCurrentStep(CONFIRMATION_STEP));
    } else if (travellers.travelersFirstStep) {
      dispatch(setCurrentStep(TRAVELERS_FORM_STEP));
    } else if (!flightOptions.isHidden) {
      dispatch(setCurrentStep(FLIGHT_OPTIONS_FORM_STEP));
    } else if (!roomOptions.isHidden) {
      dispatch(setCurrentStep(ROOM_OPTIONS_FORM_STEP));
    }

    if (!isNil(startDate) && !isNil(endDate) && !isNil(catalogueId) && !isNil(rooms)) {
      dispatch(
        setBookingAttributes({
          startDate,
          endDate,
          catalogueId,
          rooms,
          flight,
          includeFlights,
          allotmentName,
          allotmentIds,
          tourCode,
          flightRouteId,
          vendorConfigurationId
        })
      );
    } else {
      console.error('Failure when setting booking attributes', startDate, endDate, catalogueId, rooms);
    }
  }, [location.search, setBookingAttributes, setBookingNumber, includeFlights]);

  useEffect(() => {
    if (!isNil(productCode) && !isNil(productName)) {
      dispatch(
        setProductAttributes({
          productCode,
          productName
        })
      );
    } else {
      console.error('Failure when setting product attributes', productCode, productName);
    }
  }, [productCode, productName, setProductAttributes]);

  const getTranslations = async () => {
    // 1) Built-in mechanism: `translationFiles` is an array of {language, path}
    //    pairs. Fetch each and pair it with its language tag.
    const fromTranslationFiles =
      translationFiles?.map((x) =>
        fetch(x.path)
          .then((y) => y.json())
          .then((z) => ({ language: x.language, value: z }))
      ) ?? [];

    // 2) Newer mechanism: `customTranslationsUrl` is a single URL template
    //    with a `{language}` token. Fetch only the active language. Failures
    //    are non-fatal — we log a warning and fall back to built-in copy.
    const fromCustomUrl =
      customTranslationsUrl && language
        ? [
            (async () => {
              const url = customTranslationsUrl.replace('{language}', language);
              try {
                const response = await fetch(url);
                if (!response.ok) {
                  console.warn(`[tide-booking] Custom translations not found at ${url} (status ${response.status}). Falling back to built-in translations.`);
                  return null;
                }
                const value = await response.json();
                return { language, value };
              } catch (err) {
                console.warn(`[tide-booking] Failed to load custom translations from ${url}. Falling back to built-in translations.`, err);
                return null;
              }
            })()
          ]
        : [];

    const all = await Promise.all([...fromTranslationFiles, ...fromCustomUrl]);
    return all.filter((x): x is { language: string; value: any } => !!x);
  };

  useEffect(() => {
    const loadTranslations = async () => {
      const translations = await getTranslations();
      dispatch(setTranslations(translations)); // Now dispatch the resolved value
      // Unblock the initial render — runs whether the fetch succeeded or fell
      // back to built-ins, so the UI never hangs on a 404 or network error.
      setTranslationsReady(true);
    };

    loadTranslations();

    dispatch(setOfficeId(officeId));
    dispatch(setLanguageCode(language));
    dispatch(setBookingOptions(bookingOptions));
    dispatch(setCalculateDeposit(showSidebarDeposit));
    dispatch(setShowCommission(showCommission));
    if (tagIds && tagIds.length > 0) {
      dispatch(setTagIds(tagIds ?? undefined));
    }
    dispatch(setAgentAdressId(agentAdressId ?? undefined));
    if (agentAdressId && agentAdressId != 0) {
      dispatch(setBookingType('b2b'));
    }
    if (accommodationViewId && accommodationViewId != 0) {
      dispatch(setAccommodationViewId(accommodationViewId));
    }
  }, [
    officeId,
    language,
    bookingOptions,
    showSidebarDeposit,
    showCommission,
    setOfficeId,
    setLanguageCode,
    setCalculateDeposit,
    tagIds,
    agentAdressId,
    accommodationViewId
  ]);

  useEffect(() => {
    if (!productAttributes || !bookingAttributes || !rooms?.length || !isNil(bookingNumber) || !isNil(packageDetails)) {
      return;
    }

    // Fetch data
    const promise = dispatch(fetchPackage());
    return () => {
      promise.abort();
    };
  }, [productAttributes, bookingAttributes, rooms, bookingNumber, packageDetails, apiSettings.apiUrl, apiSettings.apiKey]);

  const renderStep = () => {
    switch (currentStep) {
      case FLIGHT_OPTIONS_FORM_STEP:
        return <StepRoute number={1} title={translations.STEPS.FLIGHT_OPTIONS} component={<FlightOptionsForm />} />;
      case ROOM_OPTIONS_FORM_STEP:
        return <StepRoute number={1 + (flightOptions.isHidden ? 0 : 1)} title={translations.STEPS.ROOM_OPTIONS} component={<RoomOptionsForm />} />;
      case OPTIONS_FORM_STEP:
        return (
          <StepRoute
            number={1 + (roomOptions.isHidden ? 0 : 1) + (flightOptions.isHidden ? 0 : 1)}
            title={translations.STEPS.EXTRA_OPTIONS}
            component={<OptionsForm />}
          />
        );
      case TRAVELERS_FORM_STEP:
        return (
          <StepRoute
            number={2 + (roomOptions.isHidden ? 0 : 1) + (flightOptions.isHidden ? 0 : 1)}
            title={translations.STEPS.PERSONAL_DETAILS}
            component={<TravelersForm />}
          />
        );
      case SUMMARY_STEP:
        return (
          <StepRoute
            number={3 + (roomOptions.isHidden ? 0 : 1) + (flightOptions.isHidden ? 0 : 1)}
            title={translations.STEPS.SUMMARY}
            component={<Summary />}
          />
        );
      case CONFIRMATION_STEP:
        return (
          <StepRoute
            number={4 + (roomOptions.isHidden ? 0 : 1) + (flightOptions.isHidden ? 0 : 1)}
            title={translations.STEPS.CONFIRMATION}
            component={<Confirmation />}
          />
        );
      case ERROR_STEP:
        return (
          <StepRoute number={5 + (roomOptions.isHidden ? 0 : 1) + (flightOptions.isHidden ? 0 : 1)} title={translations.STEPS.ERROR} component={<Error />} />
        );
    }
  };

  // Suppress everything until the custom translations fetch has resolved,
  // otherwise end users would briefly see built-in labels jump to the
  // customer-specific copy once the override loads.
  if (!translationsReady) {
    return null;
  }

  return (
    <>
      {((productAttributes && bookingAttributes && packageDetails) || bookingNumber) && (
        <div className="booking">
          <div className="booking__content">
            <div className="booking__panel">{renderStep()}</div>
            <div className="backdrop" id="backdrop"></div>
            {packageDetails && <Sidebar productName={productName} thumbnailUrl={thumbnailUrl} />}
          </div>
        </div>
      )}
      {!packageDetails && !bookingNumber && !isUnvailable && (
        <div className="booking">
          <div className="booking__loader">
            <Loader />
            <p className="booking__loader-text">
              {allowOption ? translations.MAIN.PREPARING_DOSSIER : isOffer ? translations.MAIN.PREPARING_OFFER : translations.MAIN.PREPARING_BOOKING}
            </p>
          </div>
        </div>
      )}
      {isUnvailable && (
        <div className="booking">
          <div className="booking__loader">
            <p className="booking__loader-text">{translations.MAIN.PRODUCT_UNAVAILABLE}</p>
          </div>
        </div>
      )}
    </>
  );
};

export default BookingSelfContained;
