import requests
from bs4 import BeautifulSoup
from datetime import datetime
import time
import re

#                    🔐 CONFIGURAÇÃO DE USUÁRIO E SENHA - EDITE AQUI 🔐
# ═══════════════════════════════════════════════════════════════════════════════════════
USUARIO_SIS = "[INFORME SEU SMS]"  # ⬅️ EDITE AQUI: Informe seu SMS
SENHA_SIS = "[INFORME SUA SENHA]"  # ⬅️ EDITE AQUI: Coloque a senha do sistema
# ═══════════════════════════════════════════════════════════════════════════════════════
#                    📋 LISTA DE PRONTUÁRIOS - COLE AQUI 📋
# ═══════════════════════════════════════════════════════════════════════════════════════
# IMPORTANTE: Cole a lista de prontuários abaixo, um por linha
# 
# REGRAS DE FORMATAÇÃO:
# - Cada prontuário deve estar em uma linha separada
# - Separe os prontuários por VÍRGULA
# - O ÚLTIMO prontuário NÃO deve ter vírgula no final
# ═══════════════════════════════════════════════════════════════════════════════════════
PRONTUARIOS = [
1035375,
459159,
760375,
995608,
1136393
]
# ═══════════════════════════════════════════════════════════════════════════════════════
#                    FIM DA LISTA DE PRONTUÁRIOS
# ═══════════════════════════════════════════════════════════════════════════════════════

# === CONFIGURAÇÕES GERAIS ===
# O sistema adiciona automaticamente o domínio ao usuário
USUARIO_PADRAO = f"{USUARIO_SIS}@sisweb.sorocaba.sp.gov.br"
SENHA_PADRAO = SENHA_SIS
URL_BASE = "https://sisweb.sorocaba.sp.gov.br"

# Valores padrão usados quando campos obrigatórios estão vazios
COD_MUNICIPIO_PADRAO = "3552205"
UF_PADRAO = "SP"
COD_PAIS_NASC_PADRAO = "10"  # Brasil (igual exemplo do formulário)

# Lista para registrar prontuários pulados por falta de CPF
PRONTUARIOS_SEM_CPF = []

# Lista para registrar prontuários processados com sucesso (melhor visibilidade em integrações)
PRONTUARIOS_CORRIGIDOS = []

# Lista para armazenar registros detalhados das atualizações (para CSV)
REGISTROS_ATUALIZACAO = []


def fazer_login(usuario=None, senha=None):
    """
    Realiza login no SISWEB e retorna (session, csrf_token).
    Se usuário/senha não forem informados, usa os padrões.
    Adiciona automaticamente @sisweb.sorocaba.sp.gov.br se o usuário não tiver domínio.
    """
    if not usuario:
        usuario = USUARIO_PADRAO
    if not senha:
        senha = SENHA_PADRAO
    
    # Adiciona o domínio automaticamente se o usuário não tiver @
    if "@" not in usuario:
        usuario = f"{usuario}@sisweb.sorocaba.sp.gov.br"

    print(f"🔐 Fazendo login no SISWEB com o usuário {usuario}...")

    session = requests.Session()
    session.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
        "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6",
        "Connection": "keep-alive",
        "X-Requested-With": "XMLHttpRequest",
    })

    # Página de login para obter o CSRF
    res_login = session.get(f"{URL_BASE}/login")
    res_login.raise_for_status()

    try:
        csrf_token = res_login.text.split('meta name="csrf-token" content="')[1].split('"')[0]
    except Exception:
        raise RuntimeError("Não foi possível localizar o CSRF token na tela de login.")

    # A "conta" é a parte antes do @
    conta = usuario.split("@")[0] if "@" in usuario else usuario

    payload = {
        "utf8": "✓",
        "page": "",
        "page_query": "",
        "conta": conta,
        "password": senha,
        "commit": "Entrar",
    }

    resp = session.post(
        f"{URL_BASE}/login/create",
        data=payload,
        headers={
            "Referer": f"{URL_BASE}/login",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "X-CSRF-Token": csrf_token,
        },
    )
    resp.raise_for_status()
    print("✅ Login realizado.\n")
    return session, csrf_token


def buscar_paciente_id_por_prontuario(session, csrf_token, numprontuario):
    """
    Usa o endpoint amb/paciente.json para encontrar o ID interno do paciente (DT_RowId)
    a partir do número de prontuário.
    """
    params = {
        "draw": "1",
        "order[0][column]": "1",
        "order[0][dir]": "asc",
        "start": "0",
        "length": "1",
        "search[value]": "",
        "search[regex]": "false",
        "search_operator": "",
        "amb_paciente[numprontuario]": str(numprontuario),
        # datas podem ser vazias; o filtro principal é o prontuário
        "amb_paciente[datcadastro]": "",
        "amb_paciente[datalteracao]": "",
        "workMode": "wmSearchResult",
        "oldWorkMode": "wmSearch",
        "_": str(int(time.time() * 1000)),  # timestamp em ms, igual ao navegador
    }

    resp = session.get(
        f"{URL_BASE}/amb/paciente.json",
        params=params,
        headers={
            "Accept": "application/json, text/javascript, */*; q=0.01",
            "X-CSRF-Token": csrf_token,
            "X-Requested-With": "XMLHttpRequest",
            "Referer": f"{URL_BASE}/amb/paciente",
        },
    )
    resp.raise_for_status()

    try:
        data = resp.json()
    except Exception as e:
        print(f"  ❌ Erro ao decodificar JSON de amb/paciente.json (prontuário {numprontuario}): {e}")
        print(f"  📄 Resposta bruta: {resp.text[:500]}")
        return None

    registros = data.get("data", [])
    if not registros:
        print(f"  ❌ Nenhum registro encontrado em amb/paciente.json para o prontuário {numprontuario}.")
        return None

    primeiro = registros[0]
    paciente_id = primeiro.get("DT_RowId")
    if not paciente_id:
        print(f"  ❌ Campo DT_RowId não encontrado no retorno de amb/paciente.json para o prontuário {numprontuario}.")
        return None

    print(f"  🔎 Paciente encontrado em amb/paciente.json: ID interno {paciente_id}.")
    return paciente_id


def carregar_form_edicao(session, paciente_id):
    """
    Carrega a tela de edição do paciente e devolve (soup, authenticity_token_form).
    """
    url_edit = f"{URL_BASE}/amb/paciente/{paciente_id}/edit"
    params = {
        "workMode": "wmEdit",
        "oldWorkMode": "wmBrowse",
    }

    resp = session.get(
        url_edit,
        params=params,
        headers={
            "Accept": "text/html, */*; q=0.01",
            "Referer": f"{URL_BASE}/amb/paciente",
        },
    )
    resp.raise_for_status()

    soup = BeautifulSoup(resp.text, "html.parser")

    # authenticity_token do formulário (hidden field)
    token_input = soup.find("input", {"name": "authenticity_token"})
    if not token_input or not token_input.get("value"):
        raise RuntimeError("Não foi possível localizar authenticity_token no formulário de edição.")

    authenticity_token = token_input["value"]
    return soup, authenticity_token


def extrair_payload_form(soup):
    """
    Lê todos os campos do formulário principal de paciente e devolve um dict
    pronto para ser usado no POST (patch).
    """
    form = soup.find("form")
    if not form:
        raise RuntimeError("Formulário de paciente não encontrado na página de edição.")

    payload = {}

    # Inputs
    for inp in form.find_all("input"):
        name = inp.get("name")
        if not name:
            continue

        input_type = (inp.get("type") or "").lower()

        if input_type in ["checkbox", "radio"]:
            if inp.has_attr("checked"):
                value = inp.get("value", "1")
            else:
                # Não envia checkbox/radio não marcados
                continue
        else:
            value = inp.get("value", "")

        payload[name] = value

    # Textareas
    for ta in form.find_all("textarea"):
        name = ta.get("name")
        if not name:
            continue
        payload[name] = ta.text or ""

    # Selects
    for sel in form.find_all("select"):
        name = sel.get("name")
        if not name:
            continue
        option_sel = sel.find("option", selected=True)
        if option_sel is not None:
            value = option_sel.get("value", "")
        else:
            # fallback: primeira opção
            first_opt = sel.find("option")
            value = first_opt.get("value", "") if first_opt else ""
        payload[name] = value

    return payload


def buscar_cpf_cadsus(session, csrf_token, payload, numprontuario):
    """
    Tenta buscar o CPF na integração CADSUS usando nome do paciente e nome da mãe.
    Retorna o CPF (string) ou None se não encontrar.
    """
    nome = payload.get("amb_paciente[nompaciente]", "").strip()
    mae = payload.get("amb_paciente[nommae]", "").strip()

    if not nome or not mae:
        print("  ⚠️ Não há nome e/ou nome da mãe suficientes para pesquisar no CADSUS.")
        return None

    params = {
        "amb_paciente[nome_completo_cadsus]": nome,
        "amb_paciente[nome_mae_cadsus]": mae,
        "amb_paciente[nome_pai_cadsus]": "",
        "amb_paciente[data_nascimento_cadsus]": "",
        "amb_paciente[cpf_cadsus]": "",
        "amb_paciente[cns_cadsus]": "",
        "amb_paciente[tela]": "paciente",
        "_": str(int(time.time() * 1000)),
    }

    try:
        resp = session.get(
            f"{URL_BASE}/int/data_sus/cad_sus/pesquisar",
            params=params,
            headers={
                "Accept": "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01",
                "X-CSRF-Token": csrf_token,
                "X-Requested-With": "XMLHttpRequest",
                "Referer": f"{URL_BASE}/amb/paciente",
            },
            timeout=30,
        )
        resp.raise_for_status()
    except Exception as e:
        print(f"  ❌ Erro ao consultar CADSUS para prontuário {numprontuario}: {e}")
        return None

    # A resposta é um JavaScript que injeta uma tabela HTML. Procuramos os atributos data-cpf no botão.
    # O HTML vem escapado dentro de string JS, então primeiro "desescapamos" aspas.
    texto = resp.text.replace('\\"', '"')
    # Primeiro data-cpf encontrado será usado
    m = re.search(r'data-cpf="([^"]+)"', texto)
    if not m:
        print(f"  ⚠️ Nenhum CPF encontrado no CADSUS para prontuário {numprontuario}.")
        return None

    cpf = m.group(1).strip()
    print(f"  🔄 CPF obtido via CADSUS para prontuário {numprontuario}: {cpf}")
    return cpf


def adicionar_mensagem_observacao(payload, mensagem, usuario=None):
    """
    Adiciona uma mensagem no campo de observação (infoadicional), preservando o conteúdo existente.
    Se usuario não for informado, usa o padrão.
    """
    if not usuario:
        usuario = USUARIO_PADRAO
    
    # Extrai o operador (parte antes do @)
    operador = usuario.split("@")[0] if "@" in usuario else usuario
    
    # Data e hora atual
    agora = datetime.now()
    data_hora = agora.strftime("%d/%m/%Y %H:%M")
    
    # Monta a mensagem completa
    mensagem_completa = f"{data_hora} - Atualizado com Script pelo(a) Operador(a) {operador}, resultado: {mensagem}"
    
    # Pega o conteúdo atual do campo de observação
    campo_obs = "amb_paciente[infoadicional]"
    conteudo_atual = payload.get(campo_obs, "").strip()
    
    # Se já tem conteúdo, adiciona nova linha antes da nova mensagem
    if conteudo_atual:
        novo_conteudo = f"{conteudo_atual}\n{mensagem_completa}"
    else:
        novo_conteudo = mensagem_completa
    
    payload[campo_obs] = novo_conteudo
    return mensagem_completa


def ajustar_campos_obrigatorios(payload, numprontuario):
    """
    Ajusta/valida campos obrigatórios:
    - CPF não pode ficar vazio (retorna False se faltar)
    - Município de nascimento não pode ficar vazio (preenche com padrão se necessário)
    - Endereço estruturado não pode ficar vazio (CEP + logradouro + bairro)
    
    Retorna:
    - True: todos os campos obrigatórios estão OK
    - False: faltam dados obrigatórios (CPF ou endereço)
    """
    global PRONTUARIOS_SEM_CPF

    # Município de nascimento - sempre preenche se vazio
    codmun_nasc = payload.get("amb_paciente[codmunicipionasc]", "").strip()
    if not codmun_nasc:
        print(f"  ℹ️ Município de nascimento vazio, preenchendo com padrão {COD_MUNICIPIO_PADRAO}/{UF_PADRAO}.")
        payload["amb_paciente[codmunicipionasc]"] = COD_MUNICIPIO_PADRAO
        if "amb_paciente[sglufnasc]" in payload and not payload.get("amb_paciente[sglufnasc]", "").strip():
            payload["amb_paciente[sglufnasc]"] = UF_PADRAO

    # País de nascimento (mantém se já tiver; se não tiver, preenche Brasil padrão)
    if "amb_paciente[codpaisnasc]" in payload and not payload.get("amb_paciente[codpaisnasc]", "").strip():
        payload["amb_paciente[codpaisnasc]"] = COD_PAIS_NASC_PADRAO

    cpf = payload.get("amb_paciente[numcpf]", "").strip()
    if not cpf:
        print(f"  ⚠️ CPF vazio no prontuário {numprontuario}.")
        PRONTUARIOS_SEM_CPF.append(numprontuario)
        return False

    # Endereço estruturado: município de residência, CEP, logradouro e bairro
    campos_endereco = {
        "amb_paciente[codmunicipiores]": "município de residência",
        "amb_paciente[codcep]": "CEP",
        "amb_paciente[codlogradouro]": "código do logradouro",
        "amb_paciente[codbairro]": "código do bairro",
    }

    for campo, desc in campos_endereco.items():
        if campo not in payload or not str(payload.get(campo, "")).strip():
            print(
                f"  ⚠️ Campo de endereço '{desc}' ({campo}) está vazio no prontuário {numprontuario}."
            )
            return False

    return True


def tem_cns(payload):
    """
    Verifica se o payload tem algum campo de CNS preenchido.
    Retorna True se encontrar algum campo de CNS/cartão SUS com valor não vazio.
    """
    for chave in payload.keys():
        nome_lower = chave.lower()
        # pega qualquer campo de CNS e qualquer variação de numcartao/numccartao
        if "cns" in nome_lower or "numcartao" in nome_lower:
            valor = str(payload.get(chave, "")).strip()
            if valor:  # Se tem algum valor, retorna True
                return True
    return False


def obter_cns_antes(payload):
    """
    Retorna o primeiro valor de CNS encontrado no payload (antes de limpar).
    Usado para registrar no CSV.
    """
    for chave in payload.keys():
        nome_lower = chave.lower()
        if "cns" in nome_lower or "numcartao" in nome_lower:
            valor = str(payload.get(chave, "")).strip()
            if valor:
                return valor
    return ""


def limpar_cns(payload):
    """
    Zera todos os campos relacionados a CNS:
    - Qualquer campo cujo nome contenha 'cns'
    - Campos de cartão SUS usados como CNS (ex: numcartao / numccartao)
    """
    chaves_apagadas = []

    for chave in list(payload.keys()):
        nome_lower = chave.lower()
        # pega qualquer campo de CNS e qualquer variação de numcartao/numccartao
        if "cns" in nome_lower or "numcartao" in nome_lower:
            payload[chave] = ""
            chaves_apagadas.append(chave)

    return chaves_apagadas


def salvar_paciente(session, csrf_token, paciente_id, payload):
    """
    Envia o PATCH para salvar o paciente com CNS apagado.
    """
    url_save = f"{URL_BASE}/amb/paciente/{paciente_id}"

    # Garante parâmetros de modo de trabalho e método PATCH
    payload.setdefault("utf8", "✓")
    payload["_method"] = "patch"
    payload.setdefault("workMode", "wmEdit")
    payload.setdefault("oldWorkMode", "wmBrowse")

    resp = session.post(
        url_save,
        data=payload,
        headers={
            "Accept": "text/html, */*; q=0.01",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "X-CSRF-Token": csrf_token,
            "X-Requested-With": "XMLHttpRequest",
            "Referer": f"{URL_BASE}/amb/paciente",
            "Origin": URL_BASE,
        },
    )

    if resp.status_code not in (200, 302):
        print(f"  ❌ Erro ao salvar (HTTP {resp.status_code}).")
        return False

    return True


def processar_prontuario(session, csrf_token, numprontuario, indice=None, total=None, usuario=None):
    global REGISTROS_ATUALIZACAO
    
    # Mostra progresso se indice e total foram fornecidos
    if indice is not None and total is not None:
        print(f"\n📋 Prontuário {indice}/{total} - Processando prontuário {numprontuario}...")
    else:
        print(f"\n📋 Processando prontuário {numprontuario}...")

    # Inicializa registro para CSV
    registro = {
        "prontuario": numprontuario,
        "cpf_antes": "",
        "cns_antes": "",
        "municipio_antes": "",
        "estado_antes": "",
        "cpf_depois": "",
        "cns_apagado": "Não",
        "sucesso": "Não",
        "mensagem": "",
        "data_hora": datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
        "operador": ""
    }

    try:
        paciente_id = buscar_paciente_id_por_prontuario(session, csrf_token, numprontuario)
        if not paciente_id:
            registro["mensagem"] = "Paciente não encontrado"
            registro["operador"] = usuario.split("@")[0] if usuario and "@" in usuario else (USUARIO_PADRAO.split("@")[0] if "@" in USUARIO_PADRAO else USUARIO_PADRAO)
            REGISTROS_ATUALIZACAO.append(registro)
            return

        soup, authenticity_token = carregar_form_edicao(session, paciente_id)
        payload = extrair_payload_form(soup)

        # Garante que o authenticity_token correto vá no payload
        payload["authenticity_token"] = authenticity_token

        # === CAPTURA DADOS ANTES DAS ALTERAÇÕES ===
        cpf_antes = payload.get("amb_paciente[numcpf]", "").strip()
        cns_antes = obter_cns_antes(payload)
        municipio_antes = payload.get("amb_paciente[codmunicipionasc]", "").strip()
        estado_antes = payload.get("amb_paciente[sglufnasc]", "").strip()
        
        registro["cpf_antes"] = cpf_antes
        registro["cns_antes"] = cns_antes
        registro["municipio_antes"] = municipio_antes
        registro["estado_antes"] = estado_antes

        # Verifica CPF atual (antes de buscar no CADSUS)
        cpf_atual = cpf_antes
        cpf_capturado_cadsus = None  # Rastreia se o CPF foi capturado do CADSUS
        
        # Se já tem CPF e não tem CNS, pode pular para acelerar o processo
        if cpf_atual:
            if not tem_cns(payload):
                print(f"  ⏭️ Prontuário já tem CPF e não tem CNS. Pulando para acelerar o processo.")
                registro["mensagem"] = "Prontuário já estava correto (tem CPF e não tem CNS)"
                registro["operador"] = usuario.split("@")[0] if usuario and "@" in usuario else (USUARIO_PADRAO.split("@")[0] if "@" in USUARIO_PADRAO else USUARIO_PADRAO)
                REGISTROS_ATUALIZACAO.append(registro)
                return
        
        # Se CPF estiver vazio, tenta buscar via integração CADSUS
        if not cpf_atual:
            print("  🔎 CPF vazio no cadastro, tentando buscar via integração CADSUS...")
            cpf_cadsus = buscar_cpf_cadsus(session, csrf_token, payload, numprontuario)
            if cpf_cadsus:
                payload["amb_paciente[numcpf]"] = cpf_cadsus
                cpf_capturado_cadsus = cpf_cadsus  # Marca que foi capturado do CADSUS

        # === LOGA DADOS IMPORTANTES DO CADASTRO ANTES DE QUALQUER ALTERAÇÃO ===
        def pega(nome):
            return payload.get(nome, "")

        print("  🔍 Dados atuais no cadastro:")
        print(f"    - Prontuário...............: {pega('amb_paciente[numprontuario]')}")
        print(f"    - Nome paciente............: {pega('amb_paciente[nompaciente]')}")
        print(f"    - Data nascimento..........: {pega('amb_paciente[datnascimento]')}")
        print(f"    - Nome mãe.................: {pega('amb_paciente[nommae]')}")
        print(f"    - CPF......................: {pega('amb_paciente[numcpf]')}")
        print(f"    - Cartão SUS (numcartao)...: {pega('amb_paciente[numcartao]')}")
        print(f"    - Cod logradouro...........: {pega('amb_paciente[codlogradouro]')}")
        print(f"    - Cod bairro...............: {pega('amb_paciente[codbairro]')}")
        print(f"    - CEP......................: {pega('amb_paciente[codcep]')}")
        print(f"    - Cod munic. nascimento....: {pega('amb_paciente[codmunicipionasc]')}")
        print(f"    - UF nascimento............: {pega('amb_paciente[sglufnasc]')}")

        # Ajusta campos obrigatórios (sempre preenche município de nascimento se vazio)
        campos_ok = ajustar_campos_obrigatorios(payload, numprontuario)
        
        # Se faltar CPF ou endereço, ainda tenta salvar pelo menos o município de nascimento
        if not campos_ok:
            cpf = payload.get("amb_paciente[numcpf]", "").strip()
            tem_endereco_completo = all([
                payload.get("amb_paciente[codmunicipiores]", "").strip(),
                payload.get("amb_paciente[codcep]", "").strip(),
                payload.get("amb_paciente[codlogradouro]", "").strip(),
                payload.get("amb_paciente[codbairro]", "").strip(),
            ])
            
            if not cpf:
                # Falta CPF - adiciona mensagem e tenta salvar pelo menos município de nascimento
                usuario_para_msg = usuario if usuario else USUARIO_PADRAO
                mensagem = adicionar_mensagem_observacao(
                    payload, 
                    "sem sucesso por dados insuficientes para coleta de CPF via integracao",
                    usuario=usuario_para_msg
                )
                print(f"  📝 Mensagem adicionada no campo de observacao: {mensagem}")
                print("  ⚠️ Tentando salvar pelo menos municipio de nascimento...")
                
                # Tenta salvar mesmo sem CPF completo (só município de nascimento)
                ok = salvar_paciente(session, csrf_token, paciente_id, payload)
                registro["cpf_depois"] = ""
                registro["cns_apagado"] = "Sim" if cns_antes else "Não"
                registro["sucesso"] = "Parcial" if ok else "Não"
                registro["mensagem"] = "sem sucesso por dados insuficientes para coleta de CPF via integracao"
                registro["operador"] = usuario_para_msg.split("@")[0] if "@" in usuario_para_msg else usuario_para_msg
                REGISTROS_ATUALIZACAO.append(registro)
                if ok:
                    print(f"  ✅ Municipio de nascimento salvo com sucesso para o prontuario {numprontuario}.")
                else:
                    print(f"  ❌ Falha ao salvar prontuario {numprontuario}.")
                return
            
            elif not tem_endereco_completo:
                # Falta endereço - não salva para não quebrar o cadastro
                print("  ⏭️ Prontuario pulado por falta de informacao de endereco obrigatoria.")
                registro["mensagem"] = "Pulado por falta de informação de endereço obrigatória"
                registro["operador"] = usuario.split("@")[0] if usuario and "@" in usuario else (USUARIO_PADRAO.split("@")[0] if "@" in USUARIO_PADRAO else USUARIO_PADRAO)
                REGISTROS_ATUALIZACAO.append(registro)
                return
        
        # Limpa CNS (inclui numcartao)
        chaves_cns = limpar_cns(payload)
        cns_foi_apagado = len(chaves_cns) > 0
        if not chaves_cns:
            print("  ℹ️ Nenhum campo de CNS/cartao SUS encontrado para limpar (talvez ja esteja vazio).")
        else:
            print(f"  ✂️ Campos CNS/cartao SUS apagados: {', '.join(chaves_cns)}")

        # Salva
        ok = salvar_paciente(session, csrf_token, paciente_id, payload)
        if ok:
            # Adiciona mensagem de sucesso no campo de observação
            usuario_para_msg = usuario if usuario else USUARIO_PADRAO
            cpf_final = payload.get("amb_paciente[numcpf]", "").strip()
            
            # Monta a mensagem de sucesso
            if cpf_capturado_cadsus:
                # CPF foi capturado do CADSUS
                mensagem_texto = f"atualizacao realizada com sucesso, CNS apagado e inserido CPF {cpf_capturado_cadsus} capturado pela integração CADSUS"
            else:
                # CPF já existia no cadastro
                mensagem_texto = f"atualizacao realizada com sucesso, CNS apagado. CPF {cpf_final} já estava no prontuário"
            
            mensagem = adicionar_mensagem_observacao(
                payload,
                mensagem_texto,
                usuario=usuario_para_msg
            )
            print(f"  📝 Mensagem de sucesso adicionada no campo de observacao: {mensagem}")
            
            # Salva novamente com a mensagem de sucesso
            salvar_paciente(session, csrf_token, paciente_id, payload)
            
            print(f"  ✅ Salvamento reportado como sucesso pelo servidor para o prontuario {numprontuario}.")
            # Marca como corrigido (para consumo por integracoes / API)
            global PRONTUARIOS_CORRIGIDOS
            PRONTUARIOS_CORRIGIDOS.append(numprontuario)
            
            # Atualiza registro para CSV
            registro["cpf_depois"] = cpf_final
            registro["cns_apagado"] = "Sim" if cns_foi_apagado else "Não"
            registro["sucesso"] = "Sim"
            registro["mensagem"] = mensagem_texto
            registro["operador"] = usuario_para_msg.split("@")[0] if "@" in usuario_para_msg else usuario_para_msg
        else:
            print(f"  ❌ Falha ao salvar prontuario {numprontuario} (HTTP diferente de 200/302).")
            # Atualiza registro para CSV
            registro["cpf_depois"] = payload.get("amb_paciente[numcpf]", "").strip()
            registro["cns_apagado"] = "Sim" if cns_foi_apagado else "Não"
            registro["sucesso"] = "Não"
            registro["mensagem"] = "Falha ao salvar (erro HTTP)"
            registro["operador"] = usuario.split("@")[0] if usuario and "@" in usuario else (USUARIO_PADRAO.split("@")[0] if "@" in USUARIO_PADRAO else USUARIO_PADRAO)
        
        # Adiciona registro ao CSV
        REGISTROS_ATUALIZACAO.append(registro)

        # Pequena pausa entre pacientes
        time.sleep(1)

    except Exception as e:
        print(f"  ❌ Erro ao processar prontuário {numprontuario}: {e}")
        registro["mensagem"] = f"Erro ao processar: {str(e)}"
        registro["operador"] = usuario.split("@")[0] if usuario and "@" in usuario else (USUARIO_PADRAO.split("@")[0] if "@" in USUARIO_PADRAO else USUARIO_PADRAO)
        REGISTROS_ATUALIZACAO.append(registro)


def gerar_txt_relatorio(nome_arquivo=None, operador=None):
    """
    Gera um arquivo TXT simples com o relatório de atualizações.
    Formato do nome: YYYY-MM-DD HHMM ws Corrige CPF SIS [operador].txt
    Conteúdo: Prontuário e status de sucesso (similar ao terminal)
    """
    if not REGISTROS_ATUALIZACAO:
        print("\n⚠️ Nenhum registro para gerar relatório.")
        return
    
    if not nome_arquivo:
        agora = datetime.now()
        # Formato: YYYY-MM-DD HHMM (data ao contrário + hora sem dois pontos)
        data_hora = agora.strftime("%Y-%m-%d %H%M")
        
        # Extrai o operador (parte antes do @) se não foi informado
        if not operador:
            # Tenta pegar do primeiro registro ou usa padrão
            if REGISTROS_ATUALIZACAO and REGISTROS_ATUALIZACAO[0].get('operador'):
                operador = REGISTROS_ATUALIZACAO[0]['operador']
            else:
                operador = USUARIO_SIS
        
        nome_arquivo = f"{data_hora} ws Corrige CPF SIS {operador}.txt"
    
    try:
        with open(nome_arquivo, 'w', encoding='utf-8') as txtfile:
            # Escreve cabeçalho
            txtfile.write("=" * 60 + "\n")
            txtfile.write("RELATÓRIO DE ATUALIZAÇÕES\n")
            txtfile.write(f"Operador: {operador}\n")
            txtfile.write(f"Data/Hora: {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}\n")
            txtfile.write("=" * 60 + "\n\n")
            
            # Agrupa por status
            sucesso = []
            falha = []
            outros = []
            
            for registro in REGISTROS_ATUALIZACAO:
                prontuario = registro['prontuario']
                status = registro['sucesso']
                
                if status == "Sim":
                    sucesso.append(prontuario)
                elif status == "Não":
                    falha.append(prontuario)
                else:
                    outros.append((prontuario, status))
            
            # Escreve prontuários com sucesso
            if sucesso:
                txtfile.write("✅ PRONTUÁRIOS ATUALIZADOS COM SUCESSO:\n")
                for p in sucesso:
                    txtfile.write(f"  - {p}\n")
                txtfile.write(f"\nTotal: {len(sucesso)} prontuário(s)\n\n")
            
            # Escreve prontuários com falha
            if falha:
                txtfile.write("❌ PRONTUÁRIOS NÃO ATUALIZADOS:\n")
                for p in falha:
                    txtfile.write(f"  - {p}\n")
                txtfile.write(f"\nTotal: {len(falha)} prontuário(s)\n\n")
            
            # Escreve outros status (parcial, pulado, etc)
            if outros:
                txtfile.write("⚠️ OUTROS STATUS:\n")
                for p, status in outros:
                    txtfile.write(f"  - {p} ({status})\n")
                txtfile.write(f"\nTotal: {len(outros)} prontuário(s)\n\n")
            
            # Resumo final
            txtfile.write("=" * 60 + "\n")
            txtfile.write(f"RESUMO:\n")
            txtfile.write(f"  Total processado: {len(REGISTROS_ATUALIZACAO)}\n")
            txtfile.write(f"  Sucesso: {len(sucesso)}\n")
            txtfile.write(f"  Falha: {len(falha)}\n")
            if outros:
                txtfile.write(f"  Outros: {len(outros)}\n")
            txtfile.write("=" * 60 + "\n")
        
        print(f"\n📄 Relatório TXT gerado com sucesso: {nome_arquivo}")
        print(f"   Total de registros: {len(REGISTROS_ATUALIZACAO)}")
        return nome_arquivo
    except Exception as e:
        print(f"\n❌ Erro ao gerar relatório TXT: {e}")
        return None


def main():
    global REGISTROS_ATUALIZACAO
    REGISTROS_ATUALIZACAO = []  # Limpa registros anteriores
    
    inicio = datetime.now()
    print(f"🚀 Início da execução: {inicio.strftime('%d/%m/%Y %H:%M:%S')}")

    session, csrf_token = fazer_login()

    # Acessa uma vez a tela de cadastro (como no uso manual)
    try:
        session.get(
            f"{URL_BASE}/amb/paciente",
            headers={
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                "Referer": f"{URL_BASE}/desktop",
            },
        )
    except Exception:
        # Não é crítico, segue o fluxo mesmo assim
        pass

    total_prontuarios = len(PRONTUARIOS)
    for indice, num in enumerate(PRONTUARIOS, start=1):
        processar_prontuario(session, csrf_token, num, indice=indice, total=total_prontuarios, usuario=USUARIO_PADRAO)

    # Resumo de prontuários não alterados por falta de CPF
    if PRONTUARIOS_SEM_CPF:
        print("\n⚠️ Prontuários NÃO alterados por estarem sem CPF:")
        for p in PRONTUARIOS_SEM_CPF:
            print(f"  - {p}")
    else:
        print("\n✅ Nenhum prontuário foi pulado por falta de CPF.")

    # Gera relatório TXT
    operador_txt = USUARIO_SIS  # Usa o operador configurado
    gerar_txt_relatorio(operador=operador_txt)

    fim = datetime.now()
    duracao = (fim - inicio).total_seconds()
    print(f"\n🏁 Fim da execução em {duracao:.1f} segundos.")


def run_batch(prontuarios, usuario=None, senha=None):
    """
    Executa a correção para uma lista de prontuários e retorna um resumo em dict.
    Pensado para uso via API / interface web.
    """
    global PRONTUARIOS_SEM_CPF, PRONTUARIOS_CORRIGIDOS, REGISTROS_ATUALIZACAO
    PRONTUARIOS_SEM_CPF = []
    PRONTUARIOS_CORRIGIDOS = []
    REGISTROS_ATUALIZACAO = []

    inicio = datetime.now()
    session, csrf_token = fazer_login(usuario=usuario, senha=senha)

    # Acessa uma vez a tela de cadastro (como no uso manual)
    try:
        session.get(
            f"{URL_BASE}/amb/paciente",
            headers={
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                "Referer": f"{URL_BASE}/desktop",
            },
        )
    except Exception:
        pass

    total_prontuarios = len(prontuarios)
    usuario_para_processar = usuario if usuario else USUARIO_PADRAO
    for indice, num in enumerate(prontuarios, start=1):
        processar_prontuario(session, csrf_token, num, indice=indice, total=total_prontuarios, usuario=usuario_para_processar)

    fim = datetime.now()
    duracao = (fim - inicio).total_seconds()

    return {
        "total": len(prontuarios),
        "corrigidos": PRONTUARIOS_CORRIGIDOS,
        "nao_alterados_sem_cpf": PRONTUARIOS_SEM_CPF,
        "duracao": duracao,
    }


if __name__ == "__main__":
    main()


