function normalizeSpace(value) {
  return String(value ?? "")
    .replace(/\u00a0/g, " ")
    .replace(/\s+/g, " ")
    .trim();
}

function normalizeMatchText(value) {
  return normalizeSpace(value)
    .normalize("NFKC")
    .toLowerCase()
    .replace(/[（）()【】\[\]{}<>《》“”"'`‘’·•、，,。.!！？?;；:：\-_/\\|+=]+/g, "")
    .replace(/\s+/g, "");
}

function buildKeywordTokens(keyword) {
  const rawTokens = normalizeSpace(keyword)
    .normalize("NFKC")
    .split(/\s+/)
    .map((token) => normalizeMatchText(token))
    .filter(Boolean);

  return [...new Set(rawTokens)];
}

function collectMatchHaystacks(result) {
  return [
    result?.title,
    result?.rawCardText,
    ...(Array.isArray(result?.highlightTexts) ? result.highlightTexts : []),
  ]
    .map((value) => normalizeMatchText(value))
    .filter(Boolean);
}

export function keywordMatchesSearchResult(keyword, result) {
  const tokens = buildKeywordTokens(keyword);
  if (tokens.length === 0) {
    return false;
  }

  const haystacks = collectMatchHaystacks(result);
  if (haystacks.length === 0) {
    return false;
  }

  return tokens.every((token) => haystacks.some((haystack) => haystack.includes(token)));
}