export type KeywordMatchMode = 'whole-word' | 'substring'

function escapeRegex(text: string): string {
  return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}

export function splitKeywordsToTerms(keywords: string): string[] {
  const input = (keywords || '').trim()
  if (input.length === 0) return []

  const terms: string[] = []
  const pattern = /"([^"]*)"?|'([^']*)'?|(\S+)/g
  let match: RegExpExecArray | null
  while ((match = pattern.exec(input)) !== null) {
    const term = (match[1] ?? match[2] ?? match[3]).trim()
    if (term.length > 0) {
      terms.push(term.toLowerCase())
    }
  }
  return terms
}

function containsCJK(text: string): boolean {
  return /[\u4e00-\u9fff\u3400-\u4dbf\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(text)
}

function buildWholeWordPattern(escapedTerm: string): string {
  // CJK text has no word boundaries, so whole-word mode degrades to substring matching.
  if (containsCJK(escapedTerm)) {
    return escapedTerm
  }
  // Whole word means "not inside a larger alphanumeric token"; this avoids
  // false positives like API-key fragments and suffix-only matches.
  return `(?<![\\p{L}\\p{N}])${escapedTerm}(?![\\p{L}\\p{N}])`
}

function addRegexFlag(flags: string, flag: string): string {
  return flags.includes(flag) ? flags : `${flags}${flag}`
}

function buildKeywordPattern(term: string, matchMode: KeywordMatchMode): string {
  const escaped = escapeRegex(term)
  return matchMode === 'whole-word' ? buildWholeWordPattern(escaped) : escaped
}

function buildKeywordRegex(term: string, options: { matchMode: KeywordMatchMode; flags?: string }): RegExp {
  const flags = options.flags ?? 'i'
  const normalizedFlags = options.matchMode === 'whole-word' ? addRegexFlag(flags, 'u') : flags
  return new RegExp(buildKeywordPattern(term, options.matchMode), normalizedFlags)
}

export function buildKeywordRegexes(
  terms: string[],
  options: { matchMode: KeywordMatchMode; flags?: string }
): RegExp[] {
  return terms.filter((term) => term.length > 0).map((term) => buildKeywordRegex(term, options))
}
