import { useCallback, useMemo } from 'react';
import { useConfig, useFilterData } from '@assets/filters/state';
import { findParameterConfig } from '@assets/filters/parameters/findParameterConfig';
import type { SelectedFloatParameter } from '@assets/filters/parameters/filterDataSlice';
import type { OrProduceWith } from '@assets/js-shop/helpers';
import { type OptRange } from '@assets/filters/common/components/RangeFilter';
import { trackFloatParameterChange } from '@assets/filters/parameters/float/trackFloatParameterChange';

/**
 * 1) Get the data necessary to render a RangeFilter for a float parameter.
 * 2) Registers a listener for GraphQL response and updates the config context with new product counts.
 * 3) Sends GTM tracking events when something changes.
 *
 * @throws Error when:
 *    - parameter with the specified id is not found,
 *    - or the parameter with the specified id is not a `float` parameter.
 */
export function useRangeParameter(parameterId: number) {
   const { parameters: parametersConfig } = useConfig();
   const { filterData: { parameters }, setFilterData } = useFilterData();

   // Find the correct float parameter from the filters config
   const parameter = useMemo(() => {
      // will throw an error if not found :)
      const parameter = findParameterConfig(parameterId, 'float', parametersConfig ?? []);

      return {
         ...parameter,
      };
   }, [parameterId, parametersConfig]);

   // Always have a range with defined min and max values
   const selected = (parameters ?? {})[parameterId]?.value as SelectedFloatParameter['value'];
   const activeRange = {
      from: selected?.min ?? parameter.values.min,
      to: selected?.max ?? parameter.values.max,
   };

   const setRange = useCallback((newRangeProducer: OrProduceWith<OptRange>) => {
      setFilterData(prev => {
         let newRange: OptRange;
         if (typeof newRangeProducer === 'function') {
            newRange = newRangeProducer(prev.priceRange);
         } else {
            newRange = newRangeProducer;
         }

         trackFloatParameterChange(
            parameterId,
            parameter,
            newRange,
         );

         if (!newRange) {
            const newData = { ...prev };
            if (!newData.parameters?.[parameterId]) {
               return newData;
            }

            delete newData.parameters[parameterId];

            if (Object.keys(newData.parameters).length === 0) {
               delete newData.parameters;
            }

            return newData;
         }

         return {
            ...prev,
            parameters: {
               ...prev.parameters,
               [parameterId]: {
                  type: 'float',
                  value: { min: newRange.from, max: newRange.to },
               },
            },
         };
      });
   }, [setFilterData]);

   return {
      parameter,
      range: activeRange,
      setRange,
   };
}
