const normalizeCsv = (value) => {
  if (Array.isArray(value)) {
    return value
      .map((item) => String(item ?? '').trim())
      .filter(Boolean);
  }
  return String(value ?? '')
    .split(',')
    .map((item) => item.trim())
    .filter(Boolean);
};

const normalizeDefault = (value) => {
  if (Array.isArray(value)) {
    return value
      .map((item) => String(item ?? '').trim())
      .filter(Boolean)
      .join(',');
  }
  return String(value ?? '').trim();
};

const normalizeEntries = (meta = {}) => {
  const entries = [];
  if (!meta || typeof meta !== 'object') {
    return entries;
  }

  Object.entries(meta).forEach(([key, value]) => {
    if (key === 'custom_fields' && Array.isArray(value)) {
      value.forEach((custom, index) => {
        if (!custom || typeof custom !== 'object') {return;}
        entries.push({
          key: String(custom.key || custom.param || `custom_${index}`),
          ...custom
        });
      });
      return;
    }

    if (!value || typeof value !== 'object' || Array.isArray(value)) {
      return;
    }

    entries.push({
      key,
      ...value
    });
  });

  return entries;
};

const findMetaEntry = (meta = {}, chip = {}) => {
  const entries = normalizeEntries(meta);
  const chipParam = String(chip?.param || '').trim();
  const chipMeta = String(chip?.meta || '').trim();
  if (chipMeta.startsWith('date_range:')) {
    const [, fromParam, toParam] = chipMeta.split(':');
    return entries.find((entry) => (
      String(entry?.type || '').toLowerCase() === 'date_range'
      && String(entry?.from_param || '') === String(fromParam || '')
      && String(entry?.to_param || '') === String(toParam || '')
    )) || null;
  }

  return entries.find((entry) => {
    const type = String(entry?.type || '').toLowerCase();
    const entryParam = String(entry?.param || entry?.key || '').trim();
    if (chipParam !== '' && entryParam === chipParam) {
      return true;
    }
    if (type !== 'date_range') {
      return false;
    }
    return (
      String(entry?.from_param || '').trim() === chipParam
      || String(entry?.to_param || '').trim() === chipParam
    );
  }) || null;
};

const resolveLabel = (entry = {}, rawValue = '') => {
  const value = String(rawValue ?? '').trim();
  if (value === '') {
    return value;
  }
  const options = entry?.options && typeof entry.options === 'object'
    ? entry.options
    : {};
  return String(options[value] ?? value);
};

export const buildChipsFromMeta = (state = {}, meta = {}) => {
  const chips = [];
  const filters = state?.filters && typeof state.filters === 'object'
    ? state.filters
    : {};

  normalizeEntries(meta).forEach((entry) => {
    const type = String(entry?.type || 'text').toLowerCase();
    const label = String(entry?.label || entry?.key || '').trim();
    if (label === '') {
      return;
    }

    if (type === 'date_range') {
      const fromParam = String(entry?.from_param || '').trim();
      const toParam = String(entry?.to_param || '').trim();
      if (fromParam === '' || toParam === '') {
        return;
      }
      const fromValue = String(filters[fromParam] ?? '').trim();
      const toValue = String(filters[toParam] ?? '').trim();
      if (fromValue === '' && toValue === '') {
        return;
      }
      const rangeLabel = [fromValue, toValue].filter(Boolean).join(' -> ');
      chips.push({
        id: `${fromParam}:${toParam}`,
        param: fromParam,
        value: rangeLabel,
        valueType: 'range',
        meta: `date_range:${fromParam}:${toParam}`,
        label: `${label}: ${rangeLabel}`
      });
      return;
    }

    const param = String(entry?.param || entry?.key || '').trim();
    if (param === '') {
      return;
    }

    if (param === 'search') {
      const searchValue = String(state?.search ?? '').trim();
      if (searchValue === '') {
        return;
      }
      chips.push({
        id: 'search',
        param: 'search',
        value: searchValue,
        valueType: 'scalar',
        label: `${label}: ${searchValue}`
      });
      return;
    }

    if (type === 'multi_csv') {
      normalizeCsv(filters[param]).forEach((value) => {
        const mapped = resolveLabel(entry, value);
        chips.push({
          id: `${param}:${value}`,
          param,
          value,
          valueType: 'multi',
          label: `${label}: ${mapped}`
        });
      });
      return;
    }

    const value = String(filters[param] ?? '').trim();
    if (value === '') {
      return;
    }

    const defaultValue = normalizeDefault(entry?.default ?? '');
    if (defaultValue !== '' && value === defaultValue) {
      return;
    }

    const mapped = resolveLabel(entry, value);
    chips.push({
      id: `${param}:${value}`,
      param,
      value,
      valueType: 'scalar',
      label: `${label}: ${mapped}`
    });
  });

  return chips;
};

export const removeChipFromMetaState = (chip = {}, state = {}, meta = {}) => {
  const next = {
    ...state,
    filters: { ...(state?.filters || {}) }
  };
  const param = String(chip?.param || '').trim();

  if (param === 'search') {
    next.search = '';
    return next;
  }

  if (String(chip?.meta || '').startsWith('date_range:')) {
    const [, fromParam, toParam] = String(chip.meta).split(':');
    if (fromParam) {
      next.filters[fromParam] = '';
    }
    if (toParam) {
      next.filters[toParam] = '';
    }
    return next;
  }

  if (chip?.valueType === 'multi') {
    const current = normalizeCsv(next.filters[param]);
    const filtered = current.filter((item) => item !== String(chip?.value || ''));
    next.filters[param] = filtered.join(',');
    return next;
  }

  const entry = findMetaEntry(meta, chip);
  next.filters[param] = normalizeDefault(entry?.default ?? '');
  return next;
};

export const clearMetaState = (state = {}, meta = {}, options = {}) => {
  const preserveSearch = options?.preserveSearch === true;
  const preserveFilterParams = new Set(
    Array.isArray(options?.preserveFilterParams)
      ? options.preserveFilterParams.map((item) => String(item ?? '').trim()).filter(Boolean)
      : []
  );

  const next = {
    ...state,
    search: preserveSearch ? String(state?.search ?? '') : '',
    filters: { ...(state?.filters || {}) }
  };

  normalizeEntries(meta).forEach((entry) => {
    const type = String(entry?.type || '').toLowerCase();
    if (type === 'date_range') {
      const fromParam = String(entry?.from_param || '').trim();
      const toParam = String(entry?.to_param || '').trim();
      if (fromParam !== '' && !preserveFilterParams.has(fromParam)) {
        next.filters[fromParam] = '';
      }
      if (toParam !== '' && !preserveFilterParams.has(toParam)) {
        next.filters[toParam] = '';
      }
      return;
    }

    const param = String(entry?.param || entry?.key || '').trim();
    if (param === '' || param === 'search' || preserveFilterParams.has(param)) {
      return;
    }
    next.filters[param] = normalizeDefault(entry?.default ?? '');
  });

  return next;
};
