import type { CodevFiche, CodevMatch } from '~/types/codev'

const STOP_WORDS_FR = new Set([
  'le', 'la', 'les', 'un', 'une', 'des', 'de', 'du', 'au', 'aux',
  'et', 'ou', 'mais', 'donc', 'car', 'ni', 'or',
  'a', 'en', 'pour', 'par', 'sur', 'avec', 'sans', 'dans', 'sous',
  'je', 'tu', 'il', 'elle', 'on', 'nous', 'vous', 'ils', 'elles',
  'mon', 'ma', 'mes', 'ton', 'ta', 'tes', 'son', 'sa', 'ses',
  'notre', 'nos', 'votre', 'vos', 'leur', 'leurs',
  'ce', 'cet', 'cette', 'ces', 'qui', 'que', 'quoi', 'dont',
  'est', 'sont', 'etre', 'ai', 'as', 'avoir',
  'pas', 'plus', 'moins', 'tres', 'aussi', 'bien', 'tout', 'tous',
  'me', 'te', 'se', 'lui', 'leur', 'y',
])

function tokenize(text: string): Set<string> {
  if (!text) return new Set()
  const tokens = text
    .toLowerCase()
    .replace(/[.,;:!?()'"\-/]/g, ' ')
    .split(/\s+/)
    .filter((t) => t.length >= 3 && !STOP_WORDS_FR.has(t))
  return new Set(tokens)
}

function jaccard(a: Set<string>, b: Set<string>): number {
  if (a.size === 0 || b.size === 0) return 0
  let inter = 0
  for (const x of a) if (b.has(x)) inter++
  const union = a.size + b.size - inter
  return union === 0 ? 0 : inter / union
}

function score(textA: string, hashtagsA: string[], textB: string, hashtagsB: string[]): number {
  const tagsA = new Set(hashtagsA.map((h) => h.toLowerCase()))
  const tagsB = new Set(hashtagsB.map((h) => h.toLowerCase()))

  if (tagsA.size > 0 && tagsB.size > 0) {
    return jaccard(tagsA, tagsB)
  }
  return jaccard(tokenize(textA), tokenize(textB))
}

// scoreDirect tokenise TOUJOURS les textes, ignore les hashtags
// Utilise pour matchSolution : besoin vs offre doivent etre compares par leur contenu reel
function scoreDirect(textA: string, textB: string): number {
  return jaccard(tokenize(textA), tokenize(textB))
}

export function matchSolution(fiches: CodevFiche[], threshold = 0.18): CodevMatch[] {
  const matches: CodevMatch[] = []
  for (const a of fiches) {
    for (const b of fiches) {
      if (a.id === b.id) continue
      // Solution : on compare le TEXTE besoin de A avec le TEXTE offre de B
      // On ignore les hashtags pour differencier besoin et offre
      const s = scoreDirect(a.besoin, b.offre)
      if (s >= threshold) {
        matches.push({ fromId: a.id, toId: b.id, score: s, mode: 'solution' })
      }
    }
  }
  return matches
}

export function matchAlliance(fiches: CodevFiche[], threshold = 0.25): CodevMatch[] {
  const matches: CodevMatch[] = []
  for (let i = 0; i < fiches.length; i++) {
    for (let j = i + 1; j < fiches.length; j++) {
      const a = fiches[i], b = fiches[j]
      // Alliance : besoins similaires — on compare hashtags si presents, sinon textes
      const s = score(a.besoin, a.hashtags, b.besoin, b.hashtags)
      if (s >= threshold) {
        matches.push({ fromId: a.id, toId: b.id, score: s, mode: 'alliance' })
      }
    }
  }
  return matches
}

export function matchSurprise(fiches: CodevFiche[], threshold = 0.25): CodevMatch[] {
  const matches: CodevMatch[] = []
  for (let i = 0; i < fiches.length; i++) {
    for (let j = i + 1; j < fiches.length; j++) {
      const a = fiches[i], b = fiches[j]
      // Surprise : offres similaires
      const s = score(a.offre, a.hashtags, b.offre, b.hashtags)
      if (s >= threshold) {
        matches.push({ fromId: a.id, toId: b.id, score: s, mode: 'surprise' })
      }
    }
  }
  return matches
}

export function computeMatches(
  fiches: CodevFiche[],
  mode: 'solution' | 'alliance' | 'surprise',
  threshold?: number,
): CodevMatch[] {
  switch (mode) {
    case 'solution': return matchSolution(fiches, threshold)
    case 'alliance': return matchAlliance(fiches, threshold)
    case 'surprise': return matchSurprise(fiches, threshold)
  }
}
