// Valdebebas Life — motor de búsqueda fuzzy con sinónimos, tolerancia a tildes,
// mayúsculas, errores tipográficos. Capa opcional de reformulación con Claude.

/* ---- Normalización ---- */
function vrNorm(s) {
  return (s || "")
    .toString()
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "") // quitar tildes
    .replace(/ñ/gi, "n")
    .toLowerCase()
    .replace(/[^a-z0-9\s]/g, " ")
    .replace(/\s+/g, " ")
    .trim();
}

/* ---- Stopwords (ES) que ignoramos al tokenizar la query ---- */
const VR_STOPWORDS = new Set([
  "de","la","el","los","las","un","una","unos","unas","y","o","u","en","a","al",
  "para","por","con","sin","mi","tu","su","sus","mis","tus","que","quien","como",
  "donde","cuando","alguien","alguno","algun","alguna","mejor","peor","muy","mas",
  "menos","es","son","esta","estan","estos","estas","ese","esa","esto","eso",
  "soy","necesito","busco","quiero","quisiera","me","te","nos","del","ha","he",
  "tengo","tener","hacer","ir","ver","sobre","aqui","alli","cerca","lejos","barrio"
]);

/* ---- Sinónimos del barrio (ampliable). Bidireccionales. ---- */
const VR_SYN_GROUPS = [
  ["fontanero", "fontaneria", "tuberia", "tuberias", "grifo", "grifos", "fuga", "fugas", "agua", "cano"],
  ["electricista", "electricidad", "enchufe", "enchufes", "luz", "luces", "diferencial", "cuadro electrico"],
  ["manitas", "chapuzas", "arreglar", "reparar", "reparaciones", "bricolaje"],
  ["pintor", "pintura", "pintar", "paredes"],
  ["reforma", "reformas", "obras", "albanil", "albanileria"],
  ["mudanza", "mudanzas", "trastero", "trasteros", "embalar", "cajas", "ordenar", "organizar", "organizacion", "orden"],
  ["cerrajero", "cerrajeria", "llave", "llaves", "cerradura"],
  ["aire", "acondicionado", "clima", "climatizacion", "ac", "split"],
  ["peluqueria", "peluquero", "peluquera", "corte", "pelo", "tinte", "mechas"],
  ["barberia", "barbero", "afeitado"],
  ["estetica", "manicura", "pedicura", "unas", "pestanas", "depilacion", "depilar"],
  ["masaje", "masajes", "masajista", "relajante", "descontracturante"],
  ["dentista", "odontologo", "ortodoncia", "boca", "muelas", "endodoncia", "implantes"],
  ["pediatra", "pediatria", "nino", "ninos", "bebe", "bebes", "infantil"],
  ["dermatologo", "dermatologia", "piel", "lunar", "lunares"],
  ["logopeda", "logopedia", "habla", "lenguaje"],
  ["fisio", "fisioterapeuta", "fisioterapia", "lesion", "espalda", "cervicales", "lumbar", "rehabilitacion"],
  ["nutricionista", "nutricion", "dietista", "dieta", "adelgazar"],
  ["psicologo", "psicologa", "psicologia", "terapia", "ansiedad", "estres"],
  ["restaurante", "restaurantes", "comer", "cena", "cenar", "comida", "menu"],
  ["cafeteria", "cafe", "desayuno", "desayunar", "merienda"],
  ["pasteleria", "tarta", "tartas", "pastel", "cumpleanos", "encargo"],
  ["panaderia", "pan", "hogaza", "masa madre"],
  ["takeaway", "para llevar", "comida llevar", "domicilio"],
  ["vinoteca", "vino", "vinos", "bodega"],
  ["canguro", "canguros", "babysitter", "cuidador", "cuidadora", "ninera"],
  ["clases", "particular", "particulares", "profesor", "profesora", "academia", "academias", "refuerzo"],
  ["ludoteca", "ocio", "infantil", "extraescolar", "extraescolares", "fiesta"],
  ["limpieza", "limpiar", "limpieza hogar", "asistenta", "domestica"],
  ["costurera", "arreglos", "coser", "modista"],
  ["lavanderia", "tintoreria", "ropa", "planchar", "plancha"],
  ["taxi", "taxis", "transporte", "vtc", "chofer"],
  ["paseador", "pasear", "perro", "perros", "guarderia canina"],
  ["peluqueria canina", "peluquero canino", "groomer"],
  ["veterinario", "veterinaria", "vacuna", "vacunas", "mascota", "mascotas"],
  ["adiestrador", "adiestradora", "adiestramiento", "educar", "educador canino"],
  ["gestoria", "gestor", "gestora", "asesor", "asesoria", "fiscal", "renta", "modelo 100", "modelo 130", "autonomos"],
  ["abogado", "abogada", "juridico", "juridica", "legal", "demanda", "divorcio"],
  ["seguros", "seguro", "poliza", "polizas", "aseguradora"],
  ["coworking", "oficina", "espacio trabajo", "remoto", "teletrabajo"],
  ["fotografo", "fotografa", "fotografia", "fotos", "boda", "newborn", "book"],
];

/* Convertir a mapa de token → set de tokens equivalentes */
function buildSynMap(groups) {
  const map = new Map();
  groups.forEach((group) => {
    const normGroup = group.map(vrNorm).flatMap((g) => g.split(" "));
    const set = new Set(normGroup);
    normGroup.forEach((tok) => {
      if (!map.has(tok)) map.set(tok, new Set());
      set.forEach((s) => map.get(tok).add(s));
    });
  });
  return map;
}
const VR_SYN_MAP = buildSynMap(VR_SYN_GROUPS);

/* ---- Distancia de Levenshtein con corte temprano ---- */
function levenshtein(a, b, maxDist = 2) {
  if (a === b) return 0;
  if (Math.abs(a.length - b.length) > maxDist) return maxDist + 1;
  if (!a.length) return b.length;
  if (!b.length) return a.length;
  const m = a.length, n = b.length;
  let prev = new Array(n + 1);
  let curr = new Array(n + 1);
  for (let j = 0; j <= n; j++) prev[j] = j;
  for (let i = 1; i <= m; i++) {
    curr[0] = i;
    let rowMin = curr[0];
    for (let j = 1; j <= n; j++) {
      const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
      curr[j] = Math.min(
        curr[j - 1] + 1,
        prev[j] + 1,
        prev[j - 1] + cost
      );
      if (curr[j] < rowMin) rowMin = curr[j];
    }
    if (rowMin > maxDist) return maxDist + 1;
    [prev, curr] = [curr, prev];
  }
  return prev[n];
}

/* ---- Tokenizar y limpiar query ---- */
function tokenize(s) {
  const norm = vrNorm(s);
  if (!norm) return [];
  return norm
    .split(" ")
    .filter((t) => t.length >= 2 && !VR_STOPWORDS.has(t));
}

/* ---- Expandir tokens con sinónimos ---- */
function expandTokens(tokens) {
  const out = new Set(tokens);
  tokens.forEach((t) => {
    if (VR_SYN_MAP.has(t)) {
      VR_SYN_MAP.get(t).forEach((s) => out.add(s));
    }
  });
  return [...out];
}

/* ---- Construir índice de un negocio ---- */
function buildHaystack(b) {
  // Pesos por campo: nombre y subcategoría pesan más
  return {
    name: vrNorm(b.name),
    sub: vrNorm(b.sub || ""),
    cat: vrNorm(b.catLabel || ""),
    tip: vrNorm(b.tip || ""),
    zone: vrNorm(b.zone || ""),
    tokens: new Set(
      tokenize([b.name, b.catLabel, b.sub, b.tip, b.zone].join(" "))
    ),
  };
}

/* ---- Score de un token contra un negocio ---- */
function scoreToken(token, idx) {
  // exacto en cualquier campo
  if (idx.tokens.has(token)) {
    if (idx.name.split(" ").includes(token)) return 6;
    if (idx.sub.split(" ").includes(token)) return 5;
    if (idx.cat.split(" ").includes(token)) return 4;
    return 3;
  }
  // contiene como substring (parcial)
  if (idx.name.includes(token)) return 4;
  if (idx.sub.includes(token)) return 3.5;
  if (idx.cat.includes(token)) return 3;
  if (idx.tip.includes(token)) return 2;
  if (idx.zone.includes(token)) return 1.5;
  // tolerancia a errores tipográficos por Levenshtein contra tokens del haystack
  let best = 0;
  for (const tk of idx.tokens) {
    if (Math.abs(tk.length - token.length) > 2) continue;
    const d = levenshtein(token, tk, 2);
    if (d <= 2 && d < tk.length / 2) {
      const s = 2 - d * 0.7; // 2, 1.3, 0.6
      if (s > best) best = s;
    }
  }
  return best;
}

/* ---- API pública: buscar ---- */
function vrSearch(query, businesses, opts = {}) {
  const rawTokens = tokenize(query);
  if (!rawTokens.length) {
    return businesses.map((b) => ({ b, score: 0 }));
  }
  const expanded = expandTokens(rawTokens);
  const results = businesses.map((b) => {
    const idx = b.__idx || buildHaystack(b);
    b.__idx = idx;
    let score = 0;
    let hits = 0;
    rawTokens.forEach((t) => {
      // mejor score entre token original y sus sinónimos
      let local = scoreToken(t, idx);
      const synSet = VR_SYN_MAP.get(t);
      if (synSet) {
        synSet.forEach((s) => {
          const sc = scoreToken(s, idx) * 0.85; // sinónimo cuesta un poco menos
          if (sc > local) local = sc;
        });
      }
      if (local > 0) hits++;
      score += local;
    });
    // bonus si matchea más de un token
    if (hits > 1) score *= 1 + 0.15 * (hits - 1);
    return { b, score };
  });
  return results
    .filter((r) => r.score > 0)
    .sort((a, b) => b.score - a.score);
}

/* ---- Reformulación opcional con Claude ---- */
async function vrReformulateQuery(rawQuery) {
  if (!window.claude || !window.claude.complete) return null;
  const prompt = `Eres el motor de búsqueda de un directorio vecinal en Valdebebas (Madrid).
Un vecino ha escrito esta consulta libre: "${rawQuery}"

Devuelve SOLO un JSON con esta forma exacta, sin explicaciones:
{
  "intent": "<resumen breve del servicio que busca, 3-6 palabras>",
  "category": "<una de: hogar, belleza, salud, gastro, familia, domestico, mascotas, profesional, all>",
  "subcategory": "<subcategoría exacta o null>",
  "keywords": ["palabra1","palabra2", "..."]
}

Subcategorías válidas:
- hogar: Fontanería, Electricidad, Manitas, Pintura, Reformas integrales, Mudanzas, Cerrajería, Aire acondicionado
- belleza: Peluquerías, Centros de estética, Barberías, Masajes
- salud: Dentistas, Pediatría, Dermatología, Logopedas, Fisioterapia, Nutrición, Psicología
- gastro: Restaurantes, Cafeterías, Pastelería / Tartas por encargo, Comida para llevar, Vinotecas
- familia: Canguros, Clases particulares, Academias, Ocio infantil, Ludotecas
- domestico: Limpieza del hogar, Costureras / Arreglos, Lavandería / Tintorería, Taxis / Transporte privado
- mascotas: Paseadores de perros, Peluquería canina, Veterinarios, Adiestradores
- profesional: Gestoría, Abogados, Seguros, Coworking, Fotografía

Devuelve keywords en singular, en minúsculas y sin tildes.`;

  try {
    const raw = await window.claude.complete(prompt);
    const match = raw.match(/\{[\s\S]*\}/);
    if (!match) return null;
    return JSON.parse(match[0]);
  } catch (e) {
    console.warn("AI reformulation failed", e);
    return null;
  }
}

Object.assign(window, {
  vrNorm,
  vrSearch,
  vrReformulateQuery,
  VR_SYN_MAP,
});
