# -*- coding: utf-8 -*-
import io, sys
B = '/home/ultimo/claude-workspace/sino-ia/'

def rd(p):
    return io.open(B+p, encoding='utf-8').read()
def wr(p, s):
    io.open(B+p, 'w', encoding='utf-8').write(s)
def sub(s, old, new, label):
    assert s.count(old) == 1, 'ANCORA FALHOU (%d ocorrencias): %s' % (s.count(old), label)
    return s.replace(old, new)

# ---------- 1) agendar.mjs: validador de nome ----------
p = 'mcp-clinica/tools/agendar.mjs'
s = rd(p)

anchor = """const limparNome = (s) => String(s || '')
  .replace(/[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{FE00}-\\u{FE0F}\\u{1F1E6}-\\u{1F1FF}]/gu, '')
  .replace(/[^\\p{L}\\p{M}\\s'.-]/gu, ' ')
  .replace(/\\s+/g, ' ')
  .trim();
"""

novo = anchor + """
// NOME GENÉRICO NÃO É NOME, e primeiro nome sozinho também não.
//
// Em 04/09/2026 a Célia agendou uma paciente com o nome literal "Cliente". Ela nunca perguntou como
// a pessoa se chamava: pulou de "posso seguir com o agendamento" direto para a data de nascimento, e
// mesmo sem nome nenhum na conversa chamou `agendar` com paciente:"Cliente". O extrato foi ao
// WhatsApp com "Nome: Cliente", a paciente respondeu "Tudo certo" — ninguém confere o próprio nome
// num extrato em que data, exame e valor estão certos — e a ficha nasceu como CLIENTE, sem telefone.
//
// A causa é a mesma já documentada no `nascimento`, e estava do outro lado do mesmo erro: `paciente`
// estava em `required` no schema da ferramenta. Required obriga o modelo a mandar ALGUMA COISA, e
// alguma coisa vira invenção — data inventada lá, rótulo genérico aqui. O campo sai do `required`
// (ver server.mjs) e quem cobra passa a ser a ferramenta, que recusa e manda perguntar.
//
// O primeiro nome sozinho também é recusado: o prontuário da clínica é nome completo, e é o
// sobrenome que separa os 383 homônimos reais da base. Errar para o lado de perguntar custa uma
// pergunta; errar para o outro lado é ficha errada em prontuário, que ninguém revisa depois.
const NOME_GENERICO = /^(a |o |uma? )?(client[ea]|pacient[ea]|pessoa|usuári[oa]|contato|senhora?|dona|moça|menina|gestante|mãe|desconhecid[oa]|sem nome|n[ãa]o informad[oa]|nome completo|n\\/?a|x+)$/i;

// Devolve a MENSAGEM de recusa, ou null quando o nome serve. Texto redigido para o modelo agir:
// nenhuma das três variações diz "erro", porque o modelo repete ao paciente o tom do que recebe.
export function problemaNoNome(nome) {
  const n = String(nome || '').replace(/\\s+/g, ' ').trim();
  if (!n) {
    return 'falta o NOME de quem fará o exame. PERGUNTE ao paciente ("qual o nome completo de quem vai fazer o exame?") e chame de novo. Não use o apelido do WhatsApp e não escreva um nome genérico no lugar.';
  }
  if (NOME_GENERICO.test(n)) {
    return `"${n}" não é o nome de ninguém, é rótulo genérico — e é assim que ficaria no prontuário da clínica. PERGUNTE ao paciente o nome completo de quem vai fazer o exame e chame de novo com o nome real. Não deduza pelo apelido do WhatsApp.`;
  }
  if (n.split(' ').filter(Boolean).length < 2) {
    return `"${n}" é só o primeiro nome, e o prontuário da clínica é o nome completo — é o sobrenome que separa xarás. PERGUNTE o nome completo ao paciente e chame de novo.`;
  }
  return null;
}
"""
s = sub(s, anchor, novo, 'limparNome')

# handler
old_h = """  const faltando = ['paciente', 'exame', 'data'].filter((k) => !args[k]);
  if (faltando.length) return { erro: 'faltam dados: ' + faltando.join(', ') };
"""
new_h = """  const faltando = ['exame', 'data'].filter((k) => !args[k]);
  if (faltando.length) return { erro: 'faltam dados: ' + faltando.join(', ') };
  // NOME: cobrado aqui, não no schema — ver problemaNoNome
  const recusaNome = problemaNoNome(paciente);
  if (recusaNome) {
    console.error(`[agendar] nome recusado: "${args.paciente ?? ''}" -> ${recusaNome.slice(0, 60)}...`);
    return { erro: recusaNome, precisaNome: true };
  }
"""
s = sub(s, old_h, new_h, 'handler faltando')
wr(p, s)
print('ok agendar.mjs')

# ---------- 2) server.mjs: paciente sai do required ----------
p = 'mcp-clinica/server.mjs'
s = rd(p)
s = sub(s,
  "paciente: { type: 'string', description: 'nome completo de quem fará o exame' }, telefone: { type: 'string' }, exame:",
  "paciente: { type: 'string', description: 'nome completo de quem fará o exame, como ELE disse. NUNCA o apelido do WhatsApp, nunca um rótulo genérico (\"Cliente\", \"Paciente\", \"a moça\"): se ele ainda não disse o nome, PERGUNTE antes.' }, telefone: { type: 'string' }, exame:",
  'schema paciente')
s = sub(s,
  "Exige o campo nascimento: sem ele a ferramenta recusa e manda perguntar.', inputSchema:",
  "Exige o campo nascimento E o nome completo do paciente: sem qualquer um dos dois a ferramenta recusa e manda perguntar.', inputSchema:",
  'descricao agendar')
s = sub(s,
  "confirmar: { type: 'boolean', description: 'use true para gravar o agendamento (após a confirmação do paciente)' } }, required: ['paciente', 'exame', 'data', 'confirmar'] } },",
  "confirmar: { type: 'boolean', description: 'use true para gravar o agendamento (após a confirmação do paciente)' } }, required: ['exame', 'data', 'confirmar'] } },",
  'required agendar')
wr(p, s)
print('ok server.mjs')

# ---------- 3) identidade-gate.mjs: telefone da conversa como PADRÃO ----------
p = 'orquestrador/identidade-gate.mjs'
s = rd(p)
s = sub(s,
  "export function aplicarIdentidadeRemetente({ contactPhone, tools, toolHandlers }) {",
  """// TELEFONE DA CONVERSA COMO PADRÃO — diferente do carimbo acima.
//
// `agendar` e `remarcar` recebem o telefone do MODELO, e em 04/09/2026 o modelo simplesmente não
// mandou: a ficha da paciente foi criada com phone vazio, num atendimento em que o número estava
// à vista o tempo inteiro, no cabeçalho da conversa. Ficha sem telefone é ficha que a recepção não
// encontra e que o `meus_agendamentos` não acha depois.
//
// Aqui NÃO se sobrescreve, ao contrário do carimbo: quem marca exame para outra pessoa pode informar
// o telefone dela, e esse dado é melhor do que o da linha. O padrão só entra quando o campo veio
// vazio — que é exatamente o buraco observado.
export const TOOLS_COM_TELEFONE_PADRAO = new Map([
  ['agendar', 'telefone'],
  ['remarcar', 'telefone'],
]);

export function aplicarTelefonePadrao({ contactPhone, tools, toolHandlers }) {
  if (!contactPhone) return { tools, toolHandlers };
  const presentes = tools.filter((t) => TOOLS_COM_TELEFONE_PADRAO.has(t.name)).map((t) => t.name);
  if (!presentes.length) return { tools, toolHandlers };
  const handlers = { ...toolHandlers };
  for (const n of presentes) {
    const original = toolHandlers[n];
    if (!original) continue;
    const campo = TOOLS_COM_TELEFONE_PADRAO.get(n);
    handlers[n] = async (args = {}) => {
      const vazio = !String(args[campo] || '').trim();
      if (vazio) console.log(`[identidade-gate] ${n} sem ${campo} — usando o número da conversa (${contactPhone})`);
      return original(vazio ? { ...args, [campo]: contactPhone } : args);
    };
  }
  return { tools, toolHandlers: handlers };
}

export function aplicarIdentidadeRemetente(entrada) {
  const r = carimbarRemetente(entrada);
  return aplicarTelefonePadrao({ contactPhone: entrada.contactPhone, tools: r.tools, toolHandlers: r.toolHandlers });
}

function carimbarRemetente({ contactPhone, tools, toolHandlers }) {""",
  'aplicarIdentidadeRemetente')
wr(p, s)
print('ok identidade-gate.mjs')

# ---------- 4) teste ----------
p = 'mcp-clinica/prontuario-test.mjs'
s = rd(p)
s = sub(s, "import { caixaDeProntuario } from './tools/agendar.mjs';",
        "import { caixaDeProntuario, problemaNoNome } from './tools/agendar.mjs';", 'import test')
s = sub(s, "console.log(falhas ? `\\n${falhas} FALHA(S)\\n` : '\\nTUDO OK\\n');",
"""console.log('\\n[3] nome genérico não entra no prontuário (caso Cliente, 04/09/2026)');
ok(problemaNoNome('Cliente') !== null, '"Cliente" é recusado — foi o nome que a Célia gravou sem nunca perguntar');
ok(problemaNoNome('cliente') !== null, 'minúscula também');
ok(problemaNoNome('a paciente') !== null, '"a paciente"');
ok(problemaNoNome('não informado') !== null, '"não informado"');
ok(problemaNoNome('') !== null && problemaNoNome(null) !== null, 'vazio e nulo');
ok(problemaNoNome('Jessica') !== null, 'primeiro nome sozinho não é nome de prontuário');
ok(/nome completo/i.test(problemaNoNome('Jessica') || ''), 'e a recusa manda PERGUNTAR o nome completo');

console.log('\\n[4] nome de verdade continua passando');
ok(problemaNoNome('Leidiane Sousa e Silva Bispo') === null, 'nome completo comum');
ok(problemaNoNome('Maria Vera Lúcia Neri de Lima') === null, 'com acento');
ok(problemaNoNome('Ana Cliente') === null, 'palavra genérica como SOBRENOME não recusa o nome inteiro');
ok(problemaNoNome('Dona Rosa Lima') === null, '"Dona" no meio de um nome completo passa');

console.log(falhas ? `\\n${falhas} FALHA(S)\\n` : '\\nTUDO OK\\n');""", 'fim teste')
wr(p, s)
print('ok prontuario-test.mjs')
