import { Logger } from '@assets/filters/common/utils/logger';
import type { FilterConfig } from '@assets/filters/common/contexts/config';
import type { FilterData } from '@assets/filters/common/contexts/filterData';
import { mergeInStoreConfig } from '@assets/filters/common/state-parsers/helpers/mergeInStoreConfig';
import { mergeConfigPriceRange } from '@assets/filters/common/state-parsers/helpers/mergeConfigPriceRange';
import { mergeFlatValuesConfig } from '@assets/filters/common/state-parsers/helpers/mergeFlatValuesConfig';
import { mergeParametersConfig } from '@assets/filters/common/state-parsers/helpers/mergeParametersConfig';
import { mergeVariationsConfig } from '@assets/filters/common/state-parsers/helpers/mergeVariationsConfig';
import { mergeConvertorsConfig } from '@assets/filters/common/state-parsers/helpers/mergeConvertorsConfig';
import { mergeProducersConfig } from '@assets/filters/common/state-parsers/helpers/mergeProducersConfig';
import { mergeSellersConfig } from '@assets/filters/common/state-parsers/helpers/mergeSellersConfig';

const configIgnoreKeys = ['common'] as const;

export type MergeFunc<TKey extends keyof FilterConfig> =
   (oldState: FilterConfig[TKey],
    newState: FilterConfig[TKey],
    filterData: FilterData | undefined,
    commonConfig: FilterConfig['common'],
   ) => FilterConfig[TKey];

type MergeFuncsMap = {
   [TKey in keyof Omit<FilterConfig, typeof configIgnoreKeys[number]>]-?: MergeFunc<TKey>;
};

const mergeFuncs: MergeFuncsMap = {
   inStore: mergeInStoreConfig,
   priceRange: mergeConfigPriceRange,
   sections: mergeFlatValuesConfig,
   producers: mergeProducersConfig,
   labels: mergeFlatValuesConfig,
   parameters: mergeParametersConfig,
   variations: mergeVariationsConfig,
   convertors: mergeConvertorsConfig,
   sellers: mergeSellersConfig,
};

const logger = new Logger('mergeConfigs');

/**
 * Merge new config with the previous one. The values missing in new config will receive count = 0.
 *
 * @param initial previous or initial config (containing all possible options)
 * @param updated new config (possibly after filtering - will be missing some options)
 * @param filterData
 *
 * @return merged config
 *
 * @todo test
 */
export function mergeConfigs(initial: FilterConfig, updated: FilterConfig, filterData?: FilterData): FilterConfig {
   const result = { ...initial };

   const ignoreKeys = configIgnoreKeys as readonly string[];

   const filterDataToPass = Object.freeze(filterData ?? {});
   const commonConfig = Object.freeze(initial.common ?? {});

   for (const [key, initialValue] of Object.entries(initial)) {
      if (!initialValue) {
         continue;
      }

      if (!(key in mergeFuncs)) {
         if (!ignoreKeys.includes(key)) {
            logger.error(`Unknown filter type "${key}" encountered when merging filter configs.`);
         }

         continue;
      }

      result[key] = mergeFuncs[key](
         initialValue,
         updated[key],
         filterDataToPass,
         commonConfig,
      );
   }

   return result;
}
