import { SearchFilters } from '../types/api';
import type { SearchSettings } from '../types/auth';

export interface SearchStateFromURL {
  query: string;
  filters: SearchFilters;
  selectedFilters: Record<string, string[]>;
  rangeFilters: Record<string, { min: string; max: string }>;
  denseWeight: number;
  rerank: boolean;
  recencyBoost: boolean;
  recencyWeight: number;
  recencyScaleDays: number;
  sectionTypes: string[];
  keywordBoostShortQueries: boolean;
  minChunkSize: number;
  semanticHighlighting: boolean;
  autoMinScore: boolean;
  deduplicate: boolean;
  fieldBoost: boolean;
  fieldBoostFields: Record<string, number>;
  model: string | null;
  modelCombo: string | null;
  dataset: string | null;
}

export const DEFAULT_FIELD_BOOST_FIELDS: Record<string, number> = { country: 1, organization: 0.5 };

export const DEFAULT_SECTION_TYPES = [
  'executive_summary',
  'context',
  'methodology',
  'findings',
  'conclusions',
  'recommendations',
  'other',
];

/** Hardcoded system defaults for all search/content settings. */
export const SYSTEM_DEFAULTS: Required<SearchSettings> = {
  denseWeight: 0.8,
  rerank: true,
  recencyBoost: false,
  recencyWeight: 0.15,
  recencyScaleDays: 365,
  sectionTypes: DEFAULT_SECTION_TYPES,
  keywordBoostShortQueries: true,
  minChunkSize: 100,
  semanticHighlighting: true,
  autoMinScore: false,
  deduplicate: true,
  fieldBoost: true,
  fieldBoostFields: { ...DEFAULT_FIELD_BOOST_FIELDS },
  greetingMessage: '',
};

/**
 * Merge search_settings from multiple groups (first non-null per key wins).
 * Returns a partial SearchSettings with only the overridden keys.
 */
export const mergeGroupSettings = (
  groups: Array<{ search_settings?: SearchSettings | null }>
): SearchSettings => {
  const merged: Record<string, unknown> = {};
  for (const group of groups) {
    const settings = group.search_settings;
    if (!settings) continue;
    for (const [key, value] of Object.entries(settings)) {
      if (value !== undefined && value !== null && !(key in merged)) {
        merged[key] = value;
      }
    }
  }
  return merged as SearchSettings;
};

const parseFilterParam = (params: URLSearchParams, key: string): string[] => {
  const value = params.get(key);
  if (!value) return [];
  return value
    .split(',')
    .map((item) => item.trim())
    .filter((item) => item.length > 0);
};

const parseFloatParam = (
  params: URLSearchParams,
  key: string,
  defaultValue: number
): number => {
  const value = params.get(key);
  return value === null ? defaultValue : parseFloat(value);
};

const parseIntParam = (
  params: URLSearchParams,
  key: string,
  defaultValue: number
): number => {
  const value = params.get(key);
  return value === null ? defaultValue : parseInt(value, 10);
};

const parseBooleanParam = (
  params: URLSearchParams,
  key: string,
  defaultValue: boolean
): boolean => {
  const value = params.get(key);
  return value === null ? defaultValue : value === 'true';
};

const parseSectionTypes = (
  params: URLSearchParams,
  defaultSectionTypes: string[]
): string[] => {
  const sectionTypesParam = params.get('sections');
  return sectionTypesParam && sectionTypesParam.trim()
    ? sectionTypesParam.split(',').filter((item) => item.trim())
    : defaultSectionTypes;
};

const isDynamicFilterKey = (key: string): boolean =>
  key.startsWith('src_') || key.startsWith('tag_');

const isRangeParam = (key: string): boolean =>
  isDynamicFilterKey(key) && (key.endsWith('_min') || key.endsWith('_max'));

const parseDynamicParams = (
  params: URLSearchParams,
  filters: SearchFilters,
  selectedFilters: Record<string, string[]>,
  rangeFilters: Record<string, { min: string; max: string }>
): void => {
  for (const [key, value] of params.entries()) {
    if (!value || !isDynamicFilterKey(key)) continue;

    if (isRangeParam(key)) {
      const base = key.slice(0, -4);
      const bound = key.endsWith('_min') ? 'min' : 'max';
      if (!rangeFilters[base]) {
        rangeFilters[base] = { min: '', max: '' };
      }
      rangeFilters[base][bound] = value;
      filters[key] = value;
    } else if (!filters[key]) {
      filters[key] = value;
      selectedFilters[key] = parseFilterParam(params, key);
    }
  }
};

const parseFilters = (
  params: URLSearchParams,
  coreFields: string[]
): {
  filters: SearchFilters;
  selectedFilters: Record<string, string[]>;
  rangeFilters: Record<string, { min: string; max: string }>;
} => {
  const filters: SearchFilters = {};
  const selectedFilters: Record<string, string[]> = {};
  const rangeFilters: Record<string, { min: string; max: string }> = {};

  for (const field of coreFields) {
    const value = params.get(field);
    if (value) {
      filters[field] = value;
      selectedFilters[field] = parseFilterParam(params, field);
    } else {
      selectedFilters[field] = [];
    }
  }

  parseDynamicParams(params, filters, selectedFilters, rangeFilters);

  return { filters, selectedFilters, rangeFilters };
};

const parseFieldBoostFields = (params: URLSearchParams): Record<string, number> => {
  const raw = params.get('field_boost_fields');
  if (!raw) return { ...DEFAULT_FIELD_BOOST_FIELDS };
  const result: Record<string, number> = {};
  for (const item of raw.split(',')) {
    const trimmed = item.trim();
    if (!trimmed) continue;
    const colonIdx = trimmed.indexOf(':');
    if (colonIdx > 0) {
      const field = trimmed.slice(0, colonIdx);
      const weight = parseFloat(trimmed.slice(colonIdx + 1));
      result[field] = isNaN(weight) ? 0.5 : weight;
    } else {
      result[trimmed] = 0.5;
    }
  }
  return Object.keys(result).length > 0 ? result : { ...DEFAULT_FIELD_BOOST_FIELDS };
};

export const getSearchStateFromURL = (
  coreFields: string[],
  defaultSectionTypes: string[],
  groupDefaults?: SearchSettings
): SearchStateFromURL => {
  const params = new URLSearchParams(window.location.search);
  const { filters, selectedFilters, rangeFilters } = parseFilters(params, coreFields);

  // For each setting: URL param wins, then group default, then system default.
  const d = { ...SYSTEM_DEFAULTS, ...groupDefaults };

  return {
    query: params.get('q') || '',
    filters,
    selectedFilters,
    rangeFilters,
    denseWeight: parseFloatParam(params, 'weight', d.denseWeight ?? SYSTEM_DEFAULTS.denseWeight),
    rerank: parseBooleanParam(params, 'rerank', d.rerank ?? SYSTEM_DEFAULTS.rerank),
    recencyBoost: parseBooleanParam(params, 'recency', d.recencyBoost ?? SYSTEM_DEFAULTS.recencyBoost),
    recencyWeight: parseFloatParam(params, 'recency_weight', d.recencyWeight ?? SYSTEM_DEFAULTS.recencyWeight),
    recencyScaleDays: parseIntParam(params, 'recency_scale', d.recencyScaleDays ?? SYSTEM_DEFAULTS.recencyScaleDays),
    sectionTypes: parseSectionTypes(params, d.sectionTypes ?? defaultSectionTypes),
    keywordBoostShortQueries: parseBooleanParam(params, 'keyword_boost', d.keywordBoostShortQueries ?? SYSTEM_DEFAULTS.keywordBoostShortQueries),
    minChunkSize: parseIntParam(params, 'min_chunk_size', d.minChunkSize ?? SYSTEM_DEFAULTS.minChunkSize),
    semanticHighlighting: parseBooleanParam(params, 'highlight', d.semanticHighlighting ?? SYSTEM_DEFAULTS.semanticHighlighting),
    autoMinScore: parseBooleanParam(params, 'auto_min_score', d.autoMinScore ?? SYSTEM_DEFAULTS.autoMinScore),
    deduplicate: parseBooleanParam(params, 'deduplicate', d.deduplicate ?? SYSTEM_DEFAULTS.deduplicate),
    fieldBoost: parseBooleanParam(params, 'field_boost', d.fieldBoost ?? SYSTEM_DEFAULTS.fieldBoost),
    fieldBoostFields: parseFieldBoostFields(params),
    model: params.get('model'),
    modelCombo: params.get('model_combo'),
    dataset: params.get('dataset'),
  };
};

const setParamIfNonEmpty = (
  params: URLSearchParams,
  key: string,
  value?: string | null
): void => {
  if (value) {
    params.set(key, value);
  }
};

const setParamIfDefined = (
  params: URLSearchParams,
  key: string,
  value?: string | number | boolean | null
): void => {
  if (value !== undefined && value !== null) {
    params.set(key, value.toString());
  }
};

const setParamIfNotDefault = (
  params: URLSearchParams,
  key: string,
  value: number | undefined,
  defaultValue: number
): void => {
  if (value !== undefined && value !== defaultValue) {
    params.set(key, value.toString());
  }
};

const setParamIfFalse = (
  params: URLSearchParams,
  key: string,
  value?: boolean
): void => {
  if (value === false) {
    params.set(key, 'false');
  }
};

const setParamIfTrue = (
  params: URLSearchParams,
  key: string,
  value?: boolean
): void => {
  if (value) {
    params.set(key, 'true');
  }
};

const setFilterParams = (params: URLSearchParams, filters: SearchFilters): void => {
  for (const [field, value] of Object.entries(filters)) {
    if (value) {
      params.set(field, value);
    }
  }
};

export const buildSearchURL = (
  query: string,
  filters: SearchFilters,
  denseWeight?: number,
  rerank?: boolean,
  recencyBoost?: boolean,
  recencyWeight?: number,
  recencyScaleDays?: number,
  sectionTypes?: string[],
  keywordBoostShortQueries?: boolean,
  minChunkSize?: number,
  semanticHighlighting?: boolean,
  autoMinScore?: boolean,
  deduplicate?: boolean,
  model?: string | null,
  modelCombo?: string | null,
  dataset?: string | null,
  fieldBoost?: boolean,
  fieldBoostFields?: Record<string, number>
): string => {
  const params = new URLSearchParams();
  setParamIfNonEmpty(params, 'q', query);
  setFilterParams(params, filters);
  setParamIfNotDefault(params, 'weight', denseWeight, 0.8);
  setParamIfDefined(params, 'rerank', rerank);
  setParamIfTrue(params, 'recency', recencyBoost);
  setParamIfNotDefault(params, 'recency_weight', recencyWeight, 0.15);
  setParamIfNotDefault(params, 'recency_scale', recencyScaleDays, 365);
  if (sectionTypes && sectionTypes.length > 0) {
    params.set('sections', sectionTypes.join(','));
  }
  setParamIfFalse(params, 'keyword_boost', keywordBoostShortQueries);
  setParamIfNotDefault(params, 'min_chunk_size', minChunkSize, 100);
  setParamIfFalse(params, 'highlight', semanticHighlighting);
  setParamIfTrue(params, 'auto_min_score', autoMinScore);
  setParamIfFalse(params, 'deduplicate', deduplicate);
  setParamIfFalse(params, 'field_boost', fieldBoost);
  if (fieldBoostFields && Object.keys(fieldBoostFields).length > 0) {
    const encoded = Object.entries(fieldBoostFields)
      .map(([field, weight]) => `${field}:${weight}`)
      .join(',');
    params.set('field_boost_fields', encoded);
  }
  setParamIfNonEmpty(params, 'model', model);
  setParamIfNonEmpty(params, 'model_combo', modelCombo);
  setParamIfNonEmpty(params, 'dataset', dataset);
  return params.toString();
};
