# ============================================================
# 19 — A log that proves it was not rewritten
#
# Colab:  !pip install winnex-madhava winnex-tracer
#
# O QUE ESTE EXEMPLO PROVA
#   O exemplo 18 mostra que um registro assinado não pode ser
#   adulterado. Mas e se alguém apagar uma linha inteira do histórico?
#   Uma assinatura por registro não detecta ausência.
#
#   Por isso os registros encadeiam: cada um carrega o hash do anterior
#   (SHA3-256). Apagar um do meio quebra a cadeia, e o verificador
#   aponta exatamente onde.
#
#     1. grava 6 registros assinados em WORM
#     2. verifica a cadeia  → íntegra
#     3. remove um registro do meio
#     4. verifica de novo  → a cadeia quebrou
#
#   É a diferença entre "não alterado" e "não removido".
# ============================================================

# instalação automática — Colab, Jupyter e script local.
# PyNaCl é necessário para o Ed25519. O pacote PyPI é "pynacl" mas o
# módulo importável é "nacl" — por isso o mapa explícito.
import importlib
import os
import subprocess
import sys

for _pkg, _mod in [
    ("winnex-madhava", "winnex_madhava"),
    ("winnex-tracer", "winnex_tracer"),
    ("pynacl", "nacl"),
]:
    try:
        __import__(_mod)
        continue
    except ImportError:
        pass
    _r = subprocess.run(
        [sys.executable, "-m", "pip", "install", "-q", "--upgrade", _pkg],
        capture_output=True, text=True
    )
    importlib.invalidate_caches()
    try:
        __import__(_mod)
    except ImportError:
        sys.stderr.write(_r.stdout or "")
        sys.stderr.write(_r.stderr or "")
        raise SystemExit(
            "\n\nNão foi possível importar " + _pkg + " após instalar.\n"
            "No Colab: menu 'Ambiente de execução' -> 'Reiniciar sessão',\n"
            "e execute esta célula novamente.\n"
        )

# Chave fixa para o exemplo rodar em qualquer lugar. Em produção: KMS/HSM.
os.environ.setdefault("WORM_SIGNING_PRIVATE_KEY_HEX", "00" * 32)

import shutil
import tempfile

from winnex_tracer.core import sign_payload
from winnex_tracer.persistence import WormStorage

# ── 1. um serviço de auditoria médica registra 6 buscas ─────
workdir = tempfile.mkdtemp(prefix="worm_demo_")
worm = WormStorage(workdir)

buscas = [
    ("AUD-0001", "apendicite com indicação cirúrgica", 516),
    ("AUD-0002", "colecistite aguda", 402),
    ("AUD-0003", "pancreatite leve", 388),
    ("AUD-0004", "pneumonia lobar", 441),
    ("AUD-0005", "insuficiência cardíaca", 367),
    ("AUD-0006", "tromboembolismo pulmonar", 290),
]

print(f"gravando {len(buscas)} registros assinados em WORM")
print(f"  diretório: {workdir}\n")

recibos = []
for i, (audit_id, consulta, excluidos) in enumerate(buscas):
    registro = sign_payload({
        "audit_id": audit_id,
        "consulta": consulta,
        "total_provably_excluded": excluidos,
        "global_threshold": 0.75,
    })
    recibo = worm.append(registro)
    recibos.append(recibo)
    print(f"  registro {i+1}  {audit_id}  "
          f"hash={recibo['worm_hash'][:16]}...")
    if recibo["worm_prev_hash"]:
        print(f"           anterior={recibo['worm_prev_hash'][:16]}...")

# ── 2. a cadeia confere? ────────────────────────────────────
relatorio = worm.verify_integrity()
print(f"\nverificação da cadeia")
print(f"  blocos             : {relatorio['total_blocks']}")
print(f"  algoritmo          : {relatorio['algorithm']}")
print(f"  violações          : {relatorio['violations']}")
print(f"  integridade        : "
      f"{'ÍNTEGRA' if relatorio['chain_integrity_verified'] else 'QUEBRADA'}")

assert relatorio["chain_integrity_verified"], "a cadeia recém-gravada deve verificar"

# ── 3. o ataque: apagar um registro do meio ─────────────────
# Uma assinatura por registro não detecta ausência — o registro que
# sobrou continua válido. O encadeamento de hashes é o que detecta.
print(f"\no ataque — apagar o registro AUD-0004 do meio da cadeia")

arquivo = os.path.join(workdir, recibos[0]["worm_path"])
with open(arquivo, encoding="utf-8") as f:
    linhas = [l for l in f if l.strip()]

antes = len(linhas)
alvo = [i for i, l in enumerate(linhas) if "AUD-0004" in l][0]
linhas.pop(alvo)
with open(arquivo, "w", encoding="utf-8") as f:
    f.writelines(linhas)

print(f"  registros antes  : {antes}")
print(f"  registros depois : {len(linhas)}")
print(f"  removido         : AUD-0004 (bloco 4)")

# Remove também o índice, para forçar a varredura do arquivo
for extra in ("index.db", "index.sqlite", "index.sqlite3"):
    caminho = os.path.join(workdir, extra)
    if os.path.exists(caminho):
        os.remove(caminho)

# ── 4. a cadeia detecta a remoção? ──────────────────────────
depois = worm.verify_integrity()
print(f"\nverificação após a remoção")
print(f"  blocos             : {depois['total_blocks']}")
print(f"  violações          : {depois['violations']}")
print(f"  integridade        : "
      f"{'ÍNTEGRA' if depois['chain_integrity_verified'] else 'QUEBRADA'}")

for v in depois["violations_detail"][:3]:
    print(f"  → {v}")

assert not depois["chain_integrity_verified"], (
    "remover um registro do meio tinha de quebrar a cadeia"
)

# ── 5. o que isso significa ─────────────────────────────────
print("""
O que os dois relatórios provam
───────────────────────────────
1. Com os 6 registros, a cadeia verifica: cada bloco carrega o hash do
   anterior, e o SHA3-256 fecha.

2. Removendo AUD-0004, a cadeia quebra: o registro seguinte aponta para
   um hash que não existe mais. O relatório aponta qual bloco tem o
   `expected_prev` diferente do `found_prev` — ou seja, onde quebrou.

Uma assinatura por registro (exemplo 18) não pegaria isso: os outros
cinco registros continuam válidos. É a cadeia que detecta ausência.

O que isso NÃO prova
────────────────────
· Não prova que a busca foi boa. Só que o histórico do que ela fez não
  foi alterado nem truncado.
· Se o atacante apagar os ÚLTIMOS registros, a cadeia do que sobrou
  continua válida. Por isso o WORM guarda o número do bloco e o hash
  mais recente fora do arquivo — em produção, com um âncora externo
  (publicação do hash do dia, carimbo de tempo, outro sistema).
· A chave privada aqui é fixa para o exemplo rodar. Em produção vem de
  KMS/HSM.

OK — cadeia íntegra, remoção detectada e localizada.
""")

shutil.rmtree(workdir, ignore_errors=True)
