// vita-chat.jsx — conversation engine (real AI) + voice I/O hooks

// ── Build the prompt & call Claude ──────────────────────────────
const PROFILE_LABELS = {
  nombre: 'Nombre', edad: 'Edad', sexo: 'Sexo',
  objetivo: 'Objetivo de salud', condiciones: 'Condiciones',
  medicamentos: 'Medicamentos', alergias: 'Alergias',
  antecedentes: 'Antecedentes familiares', sueno: 'Sueño',
  actividad: 'Actividad física', alimentacion: 'Alimentación',
  habitos: 'Hábitos', eps: 'EPS', prepagada: 'Prepagada / Plan',
};

function buildVitaPrompt(persona, profile, messages) {
  const transcript = messages.map(m => (m.role === 'vita' ? 'Vita' : 'Paciente') + ': ' + m.text).join('\n') || '(aún no empieza)';
  const profStr = Object.keys(profile).length ? JSON.stringify(profile, null, 0) : '{}';
  return `Eres Vita, la asistente médica de PREVENT — la primera plataforma de salud preventiva por voz para América Latina. De ahora en adelante TÚ administras y actualizas la historia clínica del paciente y lo acompañas en su longevidad.

${persona.persona}

CONTEXTO: Este es el PRIMER TRIAGE del paciente, 100% hablado. Tu meta es conocerlo, construir su historia clínica base y entender su situación de salud — todo de forma cálida y natural, como una charla, no un formulario. Con esta conversación PREVENT generará su primer ASIS (Análisis de Situación de Salud).

ACENTO Y HABLA (CRÍTICO — esto se reproduce en VOZ, debe sonar a una persona real de Medellín):
- Habla con acento PAISA de Antioquia, Colombia: cálido, cercano y con esa musicalidad amable de la región.
- Usa VOSEO paisa con naturalidad y cariño: "¿vos cómo te sentís?", "contame", "¿qué tenés?", "vení te cuento", "tranquilo/a", "¿sí o qué?".
- Salpica expresiones paisas con buen gusto y sin exagerar: "pues", "¿cierto?", "con mucho gusto", "de una", "¡qué bueno!", "hágale", "ave maría", "parce" (rara vez). NUNCA groserías ni exceso de jerga.
- Escribe como se HABLA: frases cortas, contracciones, ritmo natural. Sé profesional y clara, pero suena humana, nunca robótica.

REGLAS ESTRICTAS:
- UNA sola pregunta por turno. Respuestas MUY breves: 1 o 2 frases. Nada de listas ni viñetas.
- Reacciona con calidez a lo que dijo antes de preguntar lo siguiente.
- No diagnostiques ni recetes. Si surge algo serio, sugiere con suavidad hablar con un médico humano del equipo.
- Cubre progresivamente, sin abrumar: nombre, edad, objetivo de salud, condiciones crónicas, medicamentos, alergias, antecedentes familiares, sueño, actividad física, alimentación, hábitos (tabaco/alcohol/estrés) y su SEGURO DE SALUD (EPS, y si tiene medicina prepagada o plan complementario — usa las llaves eps y prepagada).
- DOCUMENTOS: en algún momento natural (cuando hables de exámenes, condiciones o medicamentos), invita con calidez al paciente a compartir lo que tenga — exámenes de laboratorio, fórmulas médicas, su historia clínica, carné de vacunas. Dile que puede adjuntarlos con el clip 📎. No insistas más de una o dos veces.
- Si el paciente aún no ha dicho su nombre, empieza presentándote brevemente y preguntándolo.
- Cuando ya tengas un panorama base (8 o más datos del perfil INCLUYENDO su EPS o seguro), agradece con calidez, dile que ahora le vas a pedir que suba sus documentos y pendientes médicos para encargarte de su seguimiento, y marca "done": true.

PERFIL RECOGIDO HASTA AHORA: ${profStr}

CONVERSACIÓN:
${transcript}

Devuelve SOLO un objeto JSON válido, sin texto extra, con esta forma exacta:
{"reply":"lo que Vita dice ahora (1-2 frases)","profile":{campos nuevos o actualizados detectados en la última respuesta del paciente; usa las llaves: nombre, edad, sexo, objetivo, condiciones, medicamentos, alergias, antecedentes, sueno, actividad, alimentacion, habitos},"progress": número 0-100 que estima qué tan completa está la historia,"done": true o false}`;
}

async function askVita(persona, profile, messages) {
  const prompt = buildVitaPrompt(persona, profile, messages);
  let raw = '';
  try {
    raw = await window.claude.complete({ messages: [{ role: 'user', content: prompt }] });
  } catch (e) {
    return { reply: 'Ave maría, se me fue la señal un momentico. ¿Me lo repetís, pues?', profile: {}, progress: null, done: false, error: true };
  }
  return parseVita(raw);
}

// ── Generate the ASIS (Análisis de Situación de Salud) ──────────
function parseJSON(raw, fallback) {
  if (!raw) return fallback;
  let s = String(raw).trim().replace(/^```(json)?/i, '').replace(/```$/i, '').trim();
  const a = s.indexOf('{'), b = s.lastIndexOf('}');
  if (a !== -1 && b !== -1) s = s.slice(a, b + 1);
  try { return JSON.parse(s); } catch (e) { return fallback; }
}

async function generateAsis(persona, profile, messages, docs, opts) {
  opts = opts || {};
  const prev = opts.prev || null;          // previous ASIS, for the changelog
  const nuevo = opts.nuevoInput || null;   // description of the new info just added
  const seguimiento = opts.seguimiento || []; // current pending items
  const transcript = messages.map(m => (m.role === 'vita' ? 'Vita' : 'Paciente') + ': ' + m.text).join('\n');
  const fallback = {
    saludo: '¡Listo!', resumen: 'Ya tengo una primera foto de tu salud. Vamos a cuidarte paso a paso, sin afanes.',
    estadoGeneral: 'amarillo', estadoTexto: 'Vas por buen camino, con cosas por cuidar',
    dimensiones: [
      { nombre: 'Corazón', icono: 'heart', estado: 'bien', nota: 'Sin alertas por ahora' },
      { nombre: 'Sueño', icono: 'sueno', estado: 'cuidar', nota: 'Podemos mejorar tu descanso' },
      { nombre: 'Actividad', icono: 'actividad', estado: 'cuidar', nota: 'Movernos un poco más' },
      { nombre: 'Alimentación', icono: 'alimentacion', estado: 'bien', nota: 'Buen punto de partida' },
      { nombre: 'Ánimo', icono: 'animo', estado: 'bien', nota: 'Te noto con buena actitud' },
    ],
    prioridades: ['Dormir mejor cada noche', 'Caminar un poquito más', 'Tomar más agua'],
    cambios: [],
  };
  const reanalisis = !!(prev && nuevo);
  const prompt = `Eres Vita, asistente médica de PREVENT. ${reanalisis
    ? 'El paciente acaba de ALIMENTAR su historia con información NUEVA. Reanaliza y ACTUALIZA su ASIS (Análisis de Situación de Salud) teniendo en cuenta lo nuevo.'
    : 'Acabas de hacer el PRIMER TRIAGE hablado a un paciente. Genera su primer ASIS (Análisis de Situación de Salud).'}

REGLA DE ORO: lenguaje SÚPER simple y cálido, que entienda CUALQUIER persona sin importar su nivel educativo. Cero tecnicismos médicos. Frases cortas. Tono paisa cálido, tuteo/voseo. Nada alarmista.

DATOS DEL PACIENTE: ${JSON.stringify(profile)}
DOCUMENTOS QUE HA COMPARTIDO: ${docs && docs.length ? docs.join(', ') : 'ninguno'}
PENDIENTES EN SEGUIMIENTO: ${seguimiento.length ? seguimiento.map(s => s.titulo + ' (' + s.estado + ')').join(', ') : 'ninguno'}
${reanalisis ? `ASIS ANTERIOR: ${JSON.stringify({ estadoGeneral: prev.estadoGeneral, dimensiones: prev.dimensiones, prioridades: prev.prioridades })}
INFORMACIÓN NUEVA QUE ACABA DE SUBIR: ${nuevo}` : ''}
CONVERSACIÓN DEL TRIAGE:
${transcript}

Devuelve SOLO JSON válido con esta forma exacta:
{
"saludo":"saludo corto con su nombre",
"resumen":"2 o 3 frases muy simples sobre cómo está su salud hoy y qué sigue",
"estadoGeneral":"verde | amarillo | rojo",
"estadoTexto":"frase corta y amable que resume el semáforo",
"dimensiones":[5 objetos: Corazón, Sueño, Actividad, Alimentación, Ánimo, cada uno {"nombre","icono" (heart, sueno, actividad, alimentacion, animo),"estado":"bien|cuidar|atencion","nota":"frase de máx 5 palabras"}],
"prioridades":["3 acciones simples y concretas en lenguaje cotidiano"],
${reanalisis
  ? '"cambios":["2 a 4 frases MUY simples y cálidas de QUÉ CAMBIÓ respecto al análisis anterior gracias a lo que subió, ej. \'Con tu examen vi que tu azúcar está bien, ¡qué alegría!\' o \'Tu corazón pasó de alerta a cuidado, vamos mejorando\'"]'
  : '"cambios":[]'}
}`;
  let raw = '';
  try { raw = await window.claude.complete({ messages: [{ role: 'user', content: prompt }] }); }
  catch (e) { return reanalisis ? { ...prev, cambios: ['Registré lo que subiste y lo sumé a tu historia. Ya lo estoy cuidando.'] } : fallback; }
  const o = parseJSON(raw, null);
  if (!o || !o.dimensiones) return reanalisis ? { ...prev, cambios: ['Guardé tu información nueva en tu historia.'] } : fallback;
  return { ...fallback, ...o, cambios: Array.isArray(o.cambios) ? o.cambios : [] };
}

// Generate a personalized nutrition plan from the patient's adjusted clinical history
async function generateNutricion(profile, asis) {
  const fallback = {
    enfoque: 'Comer rico y sano para cuidar tu corazón y tener más energía, sin dietas imposibles.',
    objetivos: ['Bajar la sal poco a poco', 'Más frutas y verduras', 'Tomar más agua'],
    comidas: [
      { momento: 'Desayuno', idea: 'Huevo, arepa asada y fruta', nota: 'Evita lo frito' },
      { momento: 'Almuerzo', idea: 'Proteína, ensalada y poquito arroz', nota: 'Mitad del plato en verduras' },
      { momento: 'Cena', idea: 'Sopa liviana o pollo con vegetales', nota: 'Liviano en la noche' },
    ],
    evitar: ['Frituras', 'Gaseosa', 'Exceso de sal'],
  };
  const prompt = `Eres el módulo de nutrición de PREVENT. Con base en la historia clínica del paciente, crea un plan nutricional SIMPLE y cálido (lenguaje cotidiano colombiano, tuteo/voseo). Nada de tecnicismos.

DATOS: ${JSON.stringify(profile)}
ESTADO DE SALUD (ASIS): ${asis ? JSON.stringify({ estado: asis.estadoGeneral, dimensiones: asis.dimensiones }) : 'n/a'}

Devuelve SOLO JSON válido, conciso:
{
"enfoque":"1 frase cálida del objetivo nutricional",
"objetivos":["3 metas simples y alcanzables"],
"comidas":[{"momento":"Desayuno|Almuerzo|Cena","idea":"comida concreta colombiana","nota":"tip corto"}],
"evitar":["3 cosas a reducir, en lenguaje cotidiano"]
}`;
  let raw = '';
  try { raw = await window.claude.complete({ messages: [{ role: 'user', content: prompt }], max_tokens: 900 }); }
  catch (e) { return fallback; }
  let s = String(raw || '').trim().replace(/^```(json)?/i, '').replace(/```$/i, '').trim();
  const a = s.indexOf('{'), b = s.lastIndexOf('}');
  if (a !== -1 && b !== -1) s = s.slice(a, b + 1);
  try { const o = JSON.parse(s); if (!o.comidas) return fallback; return { ...fallback, ...o }; }
  catch (e) { return fallback; }
}

// Build the formal clinical history (Historia Clínica) per Colombian norms (Res. 1995/1999, 866/2021, CIE-10)
function arr(v) { return Array.isArray(v) ? v : (v ? [v] : []); }
async function generateHistoriaClinica(profile, asis, messages, docs, seguimiento) {
  const transcript = messages.map(m => (m.role === 'vita' ? 'Vita' : 'Paciente') + ': ' + m.text).join('\n');
  const fallback = {
    motivoConsulta: 'Valoración inicial de salud preventiva (primer triage asistido por Vita).',
    enfermedadActual: profile.objetivo ? ('El paciente consulta con interés en ' + profile.objetivo + '. ' + (profile.condiciones ? 'Refiere ' + arr(profile.condiciones).join(', ') + '.' : '')) : 'Paciente en valoración preventiva inicial sin sintomatología aguda referida.',
    revisionSistemas: 'Sin hallazgos agudos referidos durante el triage.',
    antecedentes: {
      personales: arr(profile.condiciones),
      familiares: arr(profile.antecedentes),
      alergicos: arr(profile.alergias),
      farmacologicos: arr(profile.medicamentos),
      habitos: arr(profile.habitos).concat(profile.sueno ? ['Sueño: ' + profile.sueno] : [], profile.actividad ? ['Actividad física: ' + profile.actividad] : [], profile.alimentacion ? ['Alimentación: ' + profile.alimentacion] : []),
    },
    diagnosticos: arr(profile.condiciones).map(c => ({ cie10: '—', descripcion: c, tipo: 'Impresión diagnóstica' })),
    planManejo: (asis && asis.prioridades) ? asis.prioridades.slice() : ['Promoción de hábitos saludables y seguimiento preventivo.'],
    ordenesPendientes: (seguimiento || []).map(s => s.titulo),
  };
  const prompt = `Eres un sistema de historia clínica electrónica de PREVENT que cumple la normativa colombiana (Resolución 1995 de 1999 y Resolución 866 de 2021, interoperabilidad HL7 FHIR, codificación CIE-10). Con base en el triage del paciente, estructura su HISTORIA CLÍNICA en lenguaje clínico-técnico profesional (para personal médico).

SÉ CONCISO: frases breves, máximo 4 diagnósticos, máximo 4 ítems en plan y órdenes. No te extiendas.

DATOS DEL PACIENTE: ${JSON.stringify(profile)}
ANÁLISIS (ASIS): ${asis ? JSON.stringify({ estado: asis.estadoGeneral, dimensiones: asis.dimensiones, prioridades: asis.prioridades }) : 'n/a'}
DOCUMENTOS APORTADOS: ${docs && docs.length ? docs.join(', ') : 'ninguno'}
PENDIENTES: ${(seguimiento || []).map(s => s.titulo).join(', ') || 'ninguno'}
TRANSCRIPCIÓN DEL TRIAGE:
${transcript}

Devuelve SOLO JSON válido (sin texto extra), conciso, lenguaje CLÍNICO formal:
{
"motivoConsulta":"1 frase",
"enfermedadActual":"2 frases máximo",
"revisionSistemas":"1 frase",
"antecedentes":{"personales":[],"familiares":[],"alergicos":[],"farmacologicos":[],"habitos":[]},
"diagnosticos":[{"cie10":"código CIE-10 real (ej I10, E66.9, R53)","descripcion":"nombre corto","tipo":"Confirmado|Impresión diagnóstica|Factor de riesgo"}],
"planManejo":["frases cortas"],
"ordenesPendientes":["órdenes cortas"]
}
Asigna códigos CIE-10 correctos a cada diagnóstico o factor de riesgo.`;
  let raw = '';
  try { raw = await window.claude.complete({ messages: [{ role: 'user', content: prompt }], max_tokens: 1500 }); }
  catch (e) { return fallback; }
  let s = String(raw || '').trim().replace(/^```(json)?/i, '').replace(/```$/i, '').trim();
  const a = s.indexOf('{'), b = s.lastIndexOf('}');
  if (a !== -1 && b !== -1) s = s.slice(a, b + 1);
  let o = null;
  try { o = JSON.parse(s); } catch (e) { o = salvageHC(s); }
  if (!o || !o.diagnosticos) {
    // last resort: salvage just the diagnoses (CIE-10) from a truncated response
    const dia = salvageHC(s);
    if (dia && dia.diagnosticos && dia.diagnosticos.length) return { ...fallback, ...dia, diagnosticos: dia.diagnosticos };
    return fallback;
  }
  return {
    motivoConsulta: o.motivoConsulta || fallback.motivoConsulta,
    enfermedadActual: o.enfermedadActual || fallback.enfermedadActual,
    revisionSistemas: o.revisionSistemas || fallback.revisionSistemas,
    antecedentes: Object.assign({ personales: [], familiares: [], alergicos: [], farmacologicos: [], habitos: [] }, o.antecedentes || {}),
    diagnosticos: Array.isArray(o.diagnosticos) && o.diagnosticos.length ? o.diagnosticos : fallback.diagnosticos,
    planManejo: Array.isArray(o.planManejo) && o.planManejo.length ? o.planManejo : fallback.planManejo,
    ordenesPendientes: Array.isArray(o.ordenesPendientes) ? o.ordenesPendientes : fallback.ordenesPendientes,
  };
}

// Recover partial fields from a truncated JSON string (best-effort)
function salvageHC(s) {
  const out = {};
  const grab = (re) => { const m = s.match(re); return m ? m[1] : null; };
  const mc = grab(/"motivoConsulta"\s*:\s*"([^"]*)"/); if (mc) out.motivoConsulta = mc;
  const ea = grab(/"enfermedadActual"\s*:\s*"([^"]*)"/); if (ea) out.enfermedadActual = ea;
  const rs = grab(/"revisionSistemas"\s*:\s*"([^"]*)"/); if (rs) out.revisionSistemas = rs;
  // diagnoses: pull each {cie10, descripcion, tipo}
  const diags = [];
  const re = /\{\s*"cie10"\s*:\s*"([^"]*)"\s*,\s*"descripcion"\s*:\s*"([^"]*)"\s*,\s*"tipo"\s*:\s*"([^"]*)"/g;
  let m;
  while ((m = re.exec(s)) !== null) diags.push({ cie10: m[1], descripcion: m[2], tipo: m[3] });
  if (diags.length) out.diagnosticos = diags;
  return Object.keys(out).length ? out : null;
}

// Build the PIC (Programa de Intervención / Plan Preventivo) once the ASIS has minimum info
async function generatePic(persona, profile, asis, opts) {
  opts = opts || {};
  const prev = opts.prev || null;
  const fallback = {
    titulo: 'Tu plan preventivo', enfoque: 'Vamos a sumar pequeños hábitos que cuidan tu corazón y te dan energía.',
    areas: [
      { nombre: 'Movimiento', icono: 'actividad', meta: 'Caminar a diario', acciones: ['15 min de caminata', 'Subir escaleras'] },
      { nombre: 'Descanso', icono: 'sueno', meta: 'Dormir mejor', acciones: ['Acostarte más temprano', 'Apagar pantallas antes'] },
      { nombre: 'Alimentación', icono: 'alimentacion', meta: 'Comer más liviano', acciones: ['Más agua', 'Menos frito'] },
    ],
    metas: [
      { titulo: 'Bajar tu presión un poco', plazo: 'en 3 meses', icono: 'heart' },
      { titulo: 'Dormir 7 horas seguidas', plazo: 'en 1 mes', icono: 'sueno' },
    ],
  };
  const dims = (asis && asis.dimensiones) ? asis.dimensiones.map(d => d.nombre + ': ' + d.estado).join(', ') : '';
  const prompt = `Eres Vita, de PREVENT. El ASIS del paciente ya tiene lo mínimo necesario. Ahora arma su PIC: su Programa de Intervención / PLAN PREVENTIVO de salud personalizado para cuidar su longevidad.

REGLA DE ORO: lenguaje SÚPER simple y cálido, que entienda cualquier persona. Cero tecnicismos. Tono paisa, tuteo/voseo. Enfócate en PREVENIR y en hábitos alcanzables, no en tratar enfermedad.

DATOS: ${JSON.stringify(profile)}
ESTADO DE SALUD (ASIS): semáforo ${asis ? asis.estadoGeneral : 'amarillo'} — ${dims}
${prev ? 'PLAN ANTERIOR (ajústalo con lo nuevo): ' + JSON.stringify(prev.areas) : ''}

Devuelve SOLO JSON válido:
{
"titulo":"Tu plan preventivo",
"enfoque":"1 frase cálida de hacia dónde vamos juntos",
"areas":[2 a 4 áreas de intervención, prioriza lo que está en 'cuidar' o 'atencion', cada una {"nombre","icono" (actividad, sueno, alimentacion, heart, animo),"meta":"meta simple en 2-3 palabras","acciones":["2 acciones semanales muy concretas y fáciles"]}],
"metas":[2 o 3 metas medibles {"titulo":"meta en lenguaje simple","plazo":"ej 'en 3 meses'","icono":"heart|sueno|actividad|alimentacion|animo"}]
}`;
  let raw = '';
  try { raw = await window.claude.complete({ messages: [{ role: 'user', content: prompt }] }); }
  catch (e) { return fallback; }
  let s = String(raw || '').trim().replace(/^```(json)?/i, '').replace(/```$/i, '').trim();
  const a = s.indexOf('{'), b = s.lastIndexOf('}');
  if (a !== -1 && b !== -1) s = s.slice(a, b + 1);
  try {
    const o = JSON.parse(s);
    if (!o.areas) return fallback;
    return { ...fallback, ...o };
  } catch (e) { return fallback; }
}

// Suggest follow-up items (citas, exámenes, labs, meds) from the triage — Vita's to-do for the patient
async function suggestSeguimiento(profile, messages) {
  const transcript = messages.map(m => (m.role === 'vita' ? 'Vita' : 'Paciente') + ': ' + m.text).join('\n');
  const fallback = [];
  const prompt = `Eres Vita, de PREVENT. Según este triage, lista los PENDIENTES médicos que Vita debe gestionar y hacerle seguimiento al paciente: citas por agendar, exámenes pendientes, laboratorios y control de medicamentos. Lenguaje simple y cálido.

DATOS: ${JSON.stringify(profile)}
CONVERSACIÓN:
${transcript}

Devuelve SOLO un array JSON (máximo 4 elementos), cada uno:
{"tipo":"cita|examen|laboratorio|medicamento","titulo":"corto","detalle":"frase simple de por qué","estado":"pendiente"}
Si no hay nada claro, devuelve [].`;
  let raw = '';
  try { raw = await window.claude.complete({ messages: [{ role: 'user', content: prompt }] }); }
  catch (e) { return fallback; }
  let s = String(raw || '').trim().replace(/^```(json)?/i, '').replace(/```$/i, '').trim();
  const a = s.indexOf('['), b = s.lastIndexOf(']');
  if (a !== -1 && b !== -1) s = s.slice(a, b + 1);
  try {
    const arr = JSON.parse(s);
    return Array.isArray(arr) ? arr.slice(0, 4).map((x, i) => ({ id: 'sg' + Date.now() + i, tipo: x.tipo || 'cita', titulo: x.titulo || 'Pendiente', detalle: x.detalle || '', estado: 'pendiente' })) : [];
  } catch (e) { return fallback; }
}

function parseVita(raw) {
  if (!raw) return { reply: 'Aquí sigo con vos. ¿Cómo te sentís hoy?', profile: {}, progress: null, done: false };
  let s = String(raw).trim();
  // strip code fences
  s = s.replace(/^```(json)?/i, '').replace(/```$/i, '').trim();
  const start = s.indexOf('{');
  const end = s.lastIndexOf('}');
  if (start !== -1 && end !== -1) s = s.slice(start, end + 1);
  try {
    const o = JSON.parse(s);
    return {
      reply: (o.reply || '').toString().trim() || 'Contame un poquito más, pues.',
      profile: o.profile && typeof o.profile === 'object' ? o.profile : {},
      progress: typeof o.progress === 'number' ? Math.max(0, Math.min(100, o.progress)) : null,
      done: !!o.done,
    };
  } catch (e) {
    // fall back: treat whole thing as speech
    return { reply: s.replace(/[{}"]/g, '').slice(0, 200) || 'Contame un poquito más, pues.', profile: {}, progress: null, done: false };
  }
}

// merge profile updates (arrays union, scalars overwrite)
function mergeProfile(prev, updates) {
  const next = { ...prev };
  for (const [k, v] of Object.entries(updates || {})) {
    if (v == null || v === '' || v === 'null') continue;
    if (Array.isArray(v)) {
      const arr = Array.isArray(next[k]) ? [...next[k]] : (next[k] ? [next[k]] : []);
      v.forEach(item => { if (item && !arr.includes(item)) arr.push(item); });
      next[k] = arr;
    } else if (Array.isArray(next[k])) {
      if (!next[k].includes(v)) next[k] = [...next[k], v];
    } else {
      next[k] = v;
    }
  }
  return next;
}

// ── Voice quality helpers ───────────────────────────────────────
// Clean text so the synthesizer reads it like a person, not markup.
function cleanForSpeech(text) {
  return String(text || '')
    .replace(/[\*\_\#\`>]/g, '')                       // markdown
    .replace(/[\u{1F000}-\u{1FAFF}\u{2600}-\u{27BF}]/gu, '') // emoji
    .replace(/\s+/g, ' ')
    .replace(/\s+([,.;:!?…])/g, '$1')
    .trim();
}

// Split into natural breath-groups for smoother prosody.
function speechChunks(text) {
  const parts = cleanForSpeech(text).match(/[^.!?…]+[.!?…]*/g) || [text];
  const out = [];
  parts.forEach(p => {
    p = p.trim();
    if (!p) return;
    if (out.length && (out[out.length - 1].length + p.length) < 90) out[out.length - 1] += ' ' + p;
    else out.push(p);
  });
  return out.length ? out : [cleanForSpeech(text)];
}

// Score a voice: Colombian + neural/natural voices win big.
function scoreVoice(v) {
  let s = 0;
  const lang = (v.lang || '').toLowerCase();
  const name = (v.name || '').toLowerCase();
  if (/es[-_]co/.test(lang)) s += 100;          // Colombia
  else if (/es[-_]mx/.test(lang)) s += 70;      // México (close, neutral LatAm)
  else if (/es[-_](us|419|pe|ve|ec|cl|ar)/.test(lang)) s += 55;
  else if (/^es/.test(lang)) s += 20;
  if (/es[-_]es/.test(lang)) s -= 25;           // Spain accent (avoid)
  if (/natural|neural|online|premium|enhanced/.test(name)) s += 60;
  if (/google/.test(name)) s += 35;             // Google voices sound smooth
  if (/salome|ximena|paulina|sabina|dalia|catalina|mónica|monica|lupe|esperanza/.test(name)) s += 25; // female LatAm
  if (/gonzalo|jorge|juan|diego/.test(name)) s += 8;
  return s;
}

// Friendly label for a voice in the picker.
function voiceLabel(v) {
  const lang = (v.lang || '').toLowerCase();
  const country = /co/.test(lang) ? 'Colombia' : /mx/.test(lang) ? 'México'
    : /us|419/.test(lang) ? 'Latinoamérica' : /ar/.test(lang) ? 'Argentina'
    : /cl/.test(lang) ? 'Chile' : /pe/.test(lang) ? 'Perú' : /ve/.test(lang) ? 'Venezuela'
    : /es/.test(lang) ? 'España' : (v.lang || '');
  let name = (v.name || 'Voz').replace(/microsoft|google|\(.*?\)|online|natural|español|spanish|-|_/gi, ' ').replace(/\s+/g, ' ').trim();
  if (name.length > 18) name = name.slice(0, 18);
  const nat = /natural|neural|online|premium|enhanced/i.test(v.name) ? ' ✨' : '';
  return `${name || 'Voz'} · ${country}${nat}`;
}

// ── Voice I/O hook ──────────────────────────────
function useVitaVoice() {
  const [speaking, setSpeaking] = React.useState(false);
  const [listening, setListening] = React.useState(false);
  const [interim, setInterim] = React.useState('');
  const [spanishVoices, setSpanishVoices] = React.useState([]);
  const recRef = React.useRef(null);
  const voicesRef = React.useRef([]);
  const preferredRef = React.useRef('');   // chosen voiceURI ('' = auto-best)

  const recSupported = typeof window !== 'undefined' && (window.SpeechRecognition || window.webkitSpeechRecognition);
  const ttsSupported = typeof window !== 'undefined' && 'speechSynthesis' in window;

  React.useEffect(() => {
    if (!ttsSupported) return;
    const load = () => {
      const all = window.speechSynthesis.getVoices();
      voicesRef.current = all;
      const es = all
        .filter(v => /es([-_]|$)/i.test(v.lang) || /spanish|español/i.test(v.name))
        .map(v => ({ uri: v.voiceURI, label: voiceLabel(v), lang: v.lang, score: scoreVoice(v) }))
        .sort((a, b) => b.score - a.score);
      setSpanishVoices(es);
    };
    load();
    window.speechSynthesis.onvoiceschanged = load;
  }, []);

  const setPreferredVoice = React.useCallback((uri) => { preferredRef.current = uri || ''; }, []);

  const pickVoice = () => {
    const vs = voicesRef.current.length ? voicesRef.current : (ttsSupported ? window.speechSynthesis.getVoices() : []);
    if (!vs.length) return null;
    if (preferredRef.current) {
      const chosen = vs.find(v => v.voiceURI === preferredRef.current);
      if (chosen) return chosen;
    }
    const es = vs.filter(v => /es([-_]|$)/i.test(v.lang) || /spanish|español/i.test(v.name));
    const pool = es.length ? es : vs;
    return pool.slice().sort((a, b) => scoreVoice(b) - scoreVoice(a))[0] || null;
  };

  const speak = React.useCallback((text, persona, onEnd) => {
    if (!ttsSupported || !text) { onEnd && onEnd(); return; }
    try {
      window.speechSynthesis.cancel();
      const v = pickVoice();
      const chunks = speechChunks(text);
      const baseRate = (persona && persona.voice.rate) || 0.96;
      const pitch = (persona && persona.voice.pitch) || 1;
      let i = 0;
      let started = false;
      const sayNext = () => {
        if (i >= chunks.length) { setSpeaking(false); onEnd && onEnd(); return; }
        const u = new SpeechSynthesisUtterance(chunks[i]);
        if (v) u.voice = v;
        u.lang = (v && v.lang) || 'es-MX';
        // tiny natural rate variation per phrase so it doesn't feel robotic
        u.rate = Math.max(0.7, baseRate + (i % 2 === 0 ? 0 : -0.03));
        u.pitch = pitch;
        u.onstart = () => { if (!started) { started = true; setSpeaking(true); } };
        u.onend = () => { i += 1; sayNext(); };
        u.onerror = () => { i += 1; sayNext(); };
        window.speechSynthesis.speak(u);
      };
      sayNext();
    } catch (e) { setSpeaking(false); onEnd && onEnd(); }
  }, []);

  const stopSpeaking = React.useCallback(() => {
    if (ttsSupported) window.speechSynthesis.cancel();
    setSpeaking(false);
  }, []);

  const startListening = React.useCallback((onFinal) => {
    if (!recSupported) return false;
    try {
      const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
      const rec = new SR();
      rec.lang = 'es-CO';
      rec.interimResults = true;
      rec.continuous = false;
      rec.maxAlternatives = 1;
      let finalText = '';
      rec.onresult = (ev) => {
        let intr = '';
        for (let i = ev.resultIndex; i < ev.results.length; i++) {
          const r = ev.results[i];
          if (r.isFinal) finalText += r[0].transcript;
          else intr += r[0].transcript;
        }
        setInterim(intr);
      };
      rec.onerror = () => { setListening(false); setInterim(''); };
      rec.onend = () => {
        setListening(false);
        const txt = finalText.trim();
        setInterim('');
        if (txt) onFinal && onFinal(txt);
      };
      recRef.current = rec;
      rec.start();
      setListening(true);
      return true;
    } catch (e) { setListening(false); return false; }
  }, []);

  const stopListening = React.useCallback(() => {
    try { recRef.current && recRef.current.stop(); } catch (e) {}
    setListening(false);
  }, []);

  return { speaking, listening, interim, speak, stopSpeaking, startListening, stopListening, recSupported, ttsSupported, spanishVoices, setPreferredVoice };
}

// ── Telemedicine: Vita prepares the patient's "cheat sheet" + doctor brief ──
async function prepConsulta(profile, asis, motivo) {
  const fallback = {
    doctorBrief: [
      'Paciente con HTA en manejo con Losartán 50mg',
      'Adherencia farmacológica buena (12 días constante)',
      'Antecedente familiar de evento cardiovascular',
      'Sueño insuficiente (~5h) y sedentarismo',
    ],
    paraContar: [
      { sintoma: 'Dolor de cabeza en las mañanas', detalle: 'Estos últimos días' },
      { sintoma: 'Te has sentido cansada', detalle: 'Sobre todo en la tarde' },
      { sintoma: 'Dormiste mal', detalle: 'Unas 5 horas por noche' },
    ],
    preguntasSugeridas: ['¿Mi presión está controlada?', '¿Puedo seguir con la misma dosis?', '¿El dolor de cabeza es por la presión?'],
  };
  const prompt = `Eres Vita, asistente médica de PREVENT. El paciente va a entrar a una teleconsulta. Prepara DOS cosas: (1) un brief clínico breve para el médico, (2) una "chuleta" cálida para que el paciente NO olvide contar sus síntomas.

DATOS: ${JSON.stringify(profile)}
ASIS: ${asis ? JSON.stringify({ estado: asis.estadoGeneral, dimensiones: asis.dimensiones, prioridades: asis.prioridades }) : 'n/a'}
MOTIVO: ${motivo || 'control'}

Devuelve SOLO JSON conciso:
{
"doctorBrief":["4 puntos clínicos clave para el médico, lenguaje técnico breve"],
"paraContar":[{"sintoma":"síntoma en lenguaje cotidiano","detalle":"cuándo/cómo, breve"}],
"preguntasSugeridas":["3 preguntas que el paciente podría hacerle al médico, en primera persona"]
}`;
  let raw = '';
  try { raw = await window.claude.complete({ messages: [{ role: 'user', content: prompt }], max_tokens: 900 }); }
  catch (e) { return fallback; }
  let s = String(raw || '').trim().replace(/^```(json)?/i, '').replace(/```$/i, '').trim();
  const a = s.indexOf('{'), b = s.lastIndexOf('}');
  if (a !== -1 && b !== -1) s = s.slice(a, b + 1);
  try { const o = JSON.parse(s); if (!o.paraContar) return fallback; return { ...fallback, ...o }; }
  catch (e) { return fallback; }
}

// ── Telemedicine: generate the post-consult clinical note + orders (real AI) ──
async function generateConsultaNota(profile, asis, transcript, motivo) {
  const fallback = {
    resumen: 'Consulta de control de hipertensión. Presión arterial en metas. Se refuerza adherencia y hábitos.',
    diagnosticos: [{ cie10: 'I10', descripcion: 'Hipertensión esencial', tipo: 'Confirmado' }],
    indicaciones: ['Continuar Losartán 50mg cada día', 'Reducir consumo de sal', 'Caminar 30 min, 5 veces por semana', 'Dormir mínimo 7 horas'],
    ordenes: [
      { tipo: 'laboratorio', titulo: 'Perfil lipídico y creatinina', detalle: 'Control en 1 mes' },
      { tipo: 'cita', titulo: 'Control cardiología', detalle: 'En 3 meses' },
    ],
  };
  const prompt = `Eres Vita. Acaba de terminar la teleconsulta del paciente. Genera la nota clínica y las órdenes, cumpliendo norma colombiana (CIE-10, Res. 1995/1999). El médico no escribió nada: tú estructuras todo a partir de la conversación.

PACIENTE: ${JSON.stringify(profile)}
MOTIVO: ${motivo || 'control'}
CONVERSACIÓN DE LA CONSULTA:
${transcript}

Devuelve SOLO JSON conciso, lenguaje clínico formal:
{
"resumen":"2-3 frases de lo que pasó en la consulta y la conducta",
"diagnosticos":[{"cie10":"código real","descripcion":"nombre","tipo":"Confirmado|Impresión diagnóstica"}],
"indicaciones":["indicaciones para el paciente, claras y cortas"],
"ordenes":[{"tipo":"laboratorio|examen|cita|medicamento","titulo":"qué","detalle":"cuándo/cómo"}]
}`;
  let raw = '';
  try { raw = await window.claude.complete({ messages: [{ role: 'user', content: prompt }], max_tokens: 1200 }); }
  catch (e) { return fallback; }
  let s = String(raw || '').trim().replace(/^```(json)?/i, '').replace(/```$/i, '').trim();
  const a = s.indexOf('{'), b = s.lastIndexOf('}');
  if (a !== -1 && b !== -1) s = s.slice(a, b + 1);
  try { const o = JSON.parse(s); if (!o.diagnosticos) return fallback; return { ...fallback, ...o }; }
  catch (e) { return fallback; }
}

Object.assign(window, { askVita, mergeProfile, useVitaVoice, PROFILE_LABELS, parseVita, cleanForSpeech, generateAsis, suggestSeguimiento, generatePic, generateHistoriaClinica, generateNutricion, prepConsulta, generateConsultaNota });
