import {useNavigate, useSearchParams} from 'react-router-dom';
import {Key, useCallback, useMemo} from 'react';
import {BackendFilter} from './backend-filter';
import {BackendFiltersUrlKey} from './backend-filters-url-key';
import {decodeBackendFilters} from './utils/decode-backend-filters';
import {
  encodeBackendFilters,
  FilterListValue,
} from './utils/encode-backend-filters';

export function useBackendFilterUrlParams(
  filters?: BackendFilter[],
  pinnedFilters?: string[]
) {
  const [searchParams] = useSearchParams();
  const navigate = useNavigate();

  const encodedFilters = searchParams.get(BackendFiltersUrlKey);

  const decodedFilters: FilterListValue[] = useMemo(() => {
    if (!filters) return [];
    const decoded = decodeBackendFilters(encodedFilters);

    // if filter is pinned, and it is not applied yet, add a placeholder
    (pinnedFilters || []).forEach(key => {
      if (!decoded.find(f => f.key === key)) {
        const config = filters.find(f => f.key === key)!;
        decoded.push({
          key,
          value: config.control.defaultValue,
          operator: config.defaultOperator,
          isInactive: true,
        });
      }
    });

    // preserve original filter order from configuration
    decoded.sort(
      (a, b) =>
        filters.findIndex(f => f.key === a.key) -
        filters.findIndex(f => f.key === b.key)
    );

    return decoded;
  }, [encodedFilters, pinnedFilters, filters]);

  const getDecodedWithoutKeys = useCallback(
    (values: (FilterListValue | Key)[]) => {
      const newFilters = [...decodedFilters];
      values.forEach(value => {
        const key = typeof value === 'object' ? value.key : value;
        const index = newFilters.findIndex(f => f.key === key);
        if (index > -1) {
          newFilters.splice(index, 1);
        }
      });
      return newFilters;
    },
    [decodedFilters]
  );

  const replaceAll = useCallback(
    (filterValues: FilterListValue[]) => {
      const encodedFilters = encodeBackendFilters(filterValues, filters);
      if (encodedFilters) {
        searchParams.set(BackendFiltersUrlKey, encodedFilters);
      } else {
        searchParams.delete(BackendFiltersUrlKey);
      }
      navigate({search: `?${searchParams}`}, {replace: true});
    },
    [filters, navigate, searchParams]
  );

  const add = useCallback(
    (filterValues: FilterListValue[]) => {
      const existing = getDecodedWithoutKeys(filterValues);
      const decodedFilters = [...existing, ...filterValues];
      replaceAll(decodedFilters);
    },
    [getDecodedWithoutKeys, replaceAll]
  );

  const remove = useCallback(
    (key: Key) => replaceAll(getDecodedWithoutKeys([key])),
    [getDecodedWithoutKeys, replaceAll]
  );

  return {
    add,
    remove,
    replaceAll,
    decodedFilters,
    encodedFilters,
  };
}
