import type { SearchableFieldData } from "./components/SearchResultsView";

type SearchableFieldWithKeywords = SearchableFieldData & {
  keywords?: string[];
};

/**
 * Normalize text for search matching (lowercase, trim, collapse whitespace).
 */
export function normalizeSearchText(text: string): string {
  return text.toLowerCase().trim().replace(/\s+/g, " ");
}

/**
 * Tokenize a query into individual words for multi-word matching.
 */
export function tokenizeSearchText(text: string): string[] {
  return normalizeSearchText(text)
    .split(/\s+/)
    .filter((token) => token.length > 0);
}

/**
 * Calculate a relevance score for a field based on query match quality.
 * Higher score = better match.
 */
export function calculateSearchRelevance(
  query: string,
  field: SearchableFieldWithKeywords,
): number {
  const normalizedQuery = normalizeSearchText(query);
  const queryTokens = tokenizeSearchText(query);

  if (!normalizedQuery || queryTokens.length === 0) {
    return 0;
  }

  let score = 0;
  const normalizedLabel = normalizeSearchText(field.label);

  if (normalizedLabel === normalizedQuery) {
    score += 100;
  } else if (normalizedLabel.startsWith(normalizedQuery)) {
    score += 75;
  } else if (normalizedLabel.includes(normalizedQuery)) {
    score += 50;
  }

  const allTexts = [
    field.label,
    field.description,
    field.section,
    ...(field.keywords || []),
  ]
    .filter(Boolean)
    .map((text) => normalizeSearchText(text as string));

  const combinedText = allTexts.join(" ");
  const allTokensMatch = queryTokens.every((token) =>
    combinedText.includes(token),
  );

  if (!allTokensMatch) {
    return 0;
  }

  if (field.description) {
    const normalizedDescription = normalizeSearchText(field.description);
    if (normalizedDescription.includes(normalizedQuery)) {
      score += 25;
    } else {
      const matchingTokens = queryTokens.filter((token) =>
        normalizedDescription.includes(token),
      );
      score += matchingTokens.length * 5;
    }
  }

  if (field.section) {
    const normalizedSection = normalizeSearchText(field.section);
    if (normalizedSection.includes(normalizedQuery)) {
      score += 20;
    }
  }

  if (field.keywords && field.keywords.length > 0) {
    for (const keyword of field.keywords) {
      const normalizedKeyword = normalizeSearchText(keyword);
      if (normalizedKeyword === normalizedQuery) {
        score += 40;
      } else if (normalizedKeyword.includes(normalizedQuery)) {
        score += 15;
      } else {
        for (const token of queryTokens) {
          if (normalizedKeyword.includes(token)) {
            score += 8;
          }
        }
      }
    }
  }

  for (const text of allTexts) {
    if (queryTokens.every((token) => text.includes(token))) {
      score += 10;
      break;
    }
  }

  return score;
}
