function sanitizeFilterValue(value) {
  if (!value) return "";
  const withoutHtml = value.replace(/<[^>]*>/g, "");
  const cleaned = withoutHtml.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
  return cleaned.trim();
}
function parseMultiSelect(value) {
  if (!value || value.trim() === "") return [];
  return value.split(",").map((v) => sanitizeFilterValue(v)).filter(Boolean);
}
function parseNumber(value) {
  if (!value || value.trim() === "") return null;
  const sanitized = sanitizeFilterValue(value);
  const parsed = parseFloat(sanitized);
  if (!Number.isFinite(parsed)) return null;
  return parsed;
}
function parseNumberRange(minValue, maxValue) {
  return {
    min: parseNumber(minValue),
    max: parseNumber(maxValue)
  };
}
function parseDateRange(startValue, endValue) {
  const parseDate = (val) => {
    if (!val || val.trim() === "") return null;
    const sanitized = sanitizeFilterValue(val);
    if (!/^\d{4}-\d{2}-\d{2}$/.test(sanitized)) return null;
    return sanitized;
  };
  return {
    start: parseDate(startValue),
    end: parseDate(endValue)
  };
}
function parseFiltersFromUrl(schema, searchParams) {
  const result = {};
  for (const [key, definition] of Object.entries(schema)) {
    switch (definition.type) {
      case "search":
        result[key] = searchParams.get(definition.urlParam) || "";
        break;
      case "singleSelect":
        result[key] = searchParams.get(definition.urlParam) || null;
        break;
      case "multiSelect":
        result[key] = parseMultiSelect(searchParams.get(definition.urlParam));
        break;
      case "numberRange": {
        const def = definition;
        result[key] = parseNumberRange(
          searchParams.get(def.urlParam),
          searchParams.get(def.urlParamMax)
        );
        break;
      }
      case "dateRange": {
        const def = definition;
        result[key] = parseDateRange(
          searchParams.get(def.urlParam),
          searchParams.get(def.urlParamEnd)
        );
        break;
      }
    }
  }
  return result;
}
function serializeMultiSelect(values) {
  if (!values || values.length === 0) return null;
  return values.join(",");
}
function serializeFiltersToUrl(schema, filters) {
  const params = new URLSearchParams();
  for (const [key, definition] of Object.entries(schema)) {
    const value = filters[key];
    switch (definition.type) {
      case "search":
        if (value && value.trim() !== "") {
          params.set(definition.urlParam, value);
        }
        break;
      case "singleSelect":
        if (value && value !== null) {
          params.set(definition.urlParam, value);
        }
        break;
      case "multiSelect": {
        const serialized = serializeMultiSelect(value);
        if (serialized) {
          params.set(definition.urlParam, serialized);
        }
        break;
      }
      case "numberRange": {
        const def = definition;
        const range = value;
        if (range.min !== null) {
          params.set(def.urlParam, range.min.toString());
        }
        if (range.max !== null) {
          params.set(def.urlParamMax, range.max.toString());
        }
        break;
      }
      case "dateRange": {
        const def = definition;
        const range = value;
        if (range.start) {
          params.set(def.urlParam, range.start);
        }
        if (range.end) {
          params.set(def.urlParamEnd, range.end);
        }
        break;
      }
    }
  }
  return params.toString();
}
function getDefaultValue(definition) {
  switch (definition.type) {
    case "search":
      return "";
    case "singleSelect":
      return null;
    case "multiSelect":
      return [];
    case "numberRange":
      return { min: null, max: null };
    case "dateRange":
      return { start: null, end: null };
  }
}
function getDefaultFilterValues(schema) {
  const result = {};
  for (const [key, definition] of Object.entries(schema)) {
    result[key] = getDefaultValue(definition);
  }
  return result;
}
function isFilterActive(definition, value) {
  switch (definition.type) {
    case "search":
      return typeof value === "string" && value.trim() !== "";
    case "singleSelect":
      return value !== null && value !== "";
    case "multiSelect":
      return Array.isArray(value) && value.length > 0;
    case "numberRange": {
      const range = value;
      return range.min !== null || range.max !== null;
    }
    case "dateRange": {
      const range = value;
      return range.start !== null || range.end !== null;
    }
  }
}
function countActiveFilters(schema, filters) {
  let count = 0;
  for (const [key, definition] of Object.entries(schema)) {
    if (isFilterActive(definition, filters[key])) {
      count++;
    }
  }
  return count;
}
function hasActiveFilters(schema, filters) {
  return countActiveFilters(schema, filters) > 0;
}
function buildFilterUrl(baseUrl, schema, filters, overrides) {
  const mergedFilters = overrides ? { ...filters, ...overrides } : filters;
  const params = serializeFiltersToUrl(schema, mergedFilters);
  return params ? `${baseUrl}?${params}` : baseUrl;
}
export {
  buildFilterUrl,
  countActiveFilters,
  getDefaultFilterValues,
  hasActiveFilters,
  parseFiltersFromUrl,
  serializeFiltersToUrl
};
