#!/usr/bin/env python3
"""
scraper_bancario_v2.py — SAVE Company / Cristiano Vasconcelos

Reconstrução do scraper bancário corrigindo o bug encontrado na base anterior
(banco_perde.db): o scraper original filtrava só por assuntos.codigo + movimentos.codigo,
sem restringir classe.codigo a ações de mérito — isso deixou passar Cartas Precatórias,
Cumprimento de Sentença isolado e até processos de outra natureza (ex.: ação penal).

Esta versão restringe classe.codigo a uma whitelist de classes onde o movimento
favorável realmente representa julgamento de mérito contra o banco.

Classes válidas (testadas em campo, junho/2026):
  7     Procedimento Comum Cível
  40    Monitória
  22    Procedimento Sumário
  436   Procedimento do Juizado Especial Cível
  460   Recurso Inominado Cível
  159   Execução de Título Extrajudicial
  12154 Execução de Título Extrajudicial
  172   Embargos à Execução
  81    Busca e Apreensão em Alienação Fiduciária
  45    Ação de Exigir Contas
  32    Consignação em Pagamento
  228   Exibição de Documento ou Coisa Cível
  1707  Reintegração / Manutenção de Posse
  198   Apelação Cível (instância recursal — checar direção do recurso)
  202   Agravo de Instrumento (instância recursal — checar direção do recurso)

Classes excluídas por não serem decisão de mérito:
  156, 157, 12078 (Cumprimento de Sentença — fase de execução, não julgamento)
  241 (Petição Cível — genérico)
  65  (Ação Civil Pública — fora do escopo PJ individual)
  193 (Produção Antecipada de Prova — preparatório)
  1294 (jurisdição voluntária — genérico)
  12135 (Tutela Antecipada Antecedente — interlocutório)
  12119 (IDPJ — pilar separado, trabalhista)
"""
import json
import time
import requests
from pathlib import Path
from datetime import datetime, timedelta, timezone

OUTPUT = Path("/home/cristiano/meus-projetos/pesquisa-juridica/banco_perde_v2.json")
LOG    = Path("/home/cristiano/meus-projetos/pesquisa-juridica/banco_perde_v2_run.log")

DATAJUD_KEY = "cDZHYzlZa0JadVREZDJCendQbXY6SkJlTzNjLV9TRENyQk1RdnFKZGRQdw=="
BASE = "https://api-publica.datajud.cnj.jus.br"
HEADERS = {"Authorization": f"APIKey {DATAJUD_KEY}", "Content-Type": "application/json"}

TJS = {
    "TJSP": "api_publica_tjsp", "TJRJ": "api_publica_tjrj", "TJMG": "api_publica_tjmg",
    "TJRS": "api_publica_tjrs", "TJPR": "api_publica_tjpr", "TJSC": "api_publica_tjsc",
    "TJBA": "api_publica_tjba", "TJPE": "api_publica_tjpe", "TJCE": "api_publica_tjce",
    "TJGO": "api_publica_tjgo", "TJMT": "api_publica_tjmt", "TJDFT": "api_publica_tjdft",
}

ASSUNTOS_BANCARIOS = [10028, 9607, 9981, 4980, 7697, 7779, 7698, 11783, 10585,
                      7699, 6228, 6229, 3534, 9957, 6236, 7633, 6232, 10027]

CLASSES_VALIDAS = [7, 40, 22, 436, 460, 159, 12154, 172, 81, 45, 32, 228, 1707, 198, 202]

MOV_EMPRESA_VENCE = {
    220: "Improcedência (banco perdeu como autor)",
    459: "Extinção sem Resolução do Mérito",
    219: "Procedência (empresa venceu como autora)",
    221: "Procedência em Parte",
    238: "Provimento (recurso da empresa provido)",
    866: "Concessão de Liminar",
    867: "Concessão de Tutela Antecipada",
}

DATA_INICIO = (datetime.now(timezone.utc) - timedelta(days=3 * 365)).strftime("%Y-%m-%d")
PAGE_SIZE = 100
MAX_PAGES = 100


def log(msg: str):
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"[{ts}] {msg}"
    print(line)
    with open(LOG, "a") as f:
        f.write(line + "\n")


def buscar_tribunal(endpoint: str, sigla: str, existentes: set) -> list:
    resultados = []
    for offset in range(0, PAGE_SIZE * MAX_PAGES, PAGE_SIZE):
        query = {
            "size": PAGE_SIZE,
            "from": offset,
            "query": {
                "bool": {
                    "must": [
                        {"terms": {"assuntos.codigo": ASSUNTOS_BANCARIOS}},
                        {"terms": {"classe.codigo": CLASSES_VALIDAS}},
                        {"terms": {"movimentos.codigo": list(MOV_EMPRESA_VENCE.keys())}},
                        {"range": {"dataHoraUltimaAtualizacao": {"gte": DATA_INICIO}}},
                    ]
                }
            },
            "_source": [
                "numeroProcesso", "classe", "assuntos", "movimentos",
                "orgaoJulgador", "dataAjuizamento", "dataHoraUltimaAtualizacao",
                "tribunal", "grau",
            ],
            "sort": [{"dataHoraUltimaAtualizacao": {"order": "desc"}}],
        }
        url = f"{BASE}/{endpoint}/_search"
        r = None
        for tentativa in range(4):
            try:
                r = requests.post(url, headers=HEADERS, json=query, timeout=30)
                if r.status_code == 200:
                    break
            except requests.exceptions.RequestException as e:
                log(f"  {sigla} | erro tentativa {tentativa+1}: {e}")
            time.sleep(4)

        if r is None or r.status_code != 200:
            log(f"  {sigla} | falhou offset {offset}, status {getattr(r,'status_code',None)}")
            break

        hits = r.json().get("hits", {}).get("hits", [])
        if not hits:
            break

        for h in hits:
            src = h["_source"]
            num = src.get("numeroProcesso")
            if not num or num in existentes:
                continue
            existentes.add(num)

            movs_codigos = {m.get("codigo") for m in src.get("movimentos", []) if isinstance(m, dict)}
            mov_principal = next((c for c in MOV_EMPRESA_VENCE if c in movs_codigos), None)

            resultados.append({
                "numero": num,
                "tribunal": sigla,
                "grau": src.get("grau"),
                "classe": src.get("classe"),
                "orgao_julgador": (src.get("orgaoJulgador") or {}).get("nome") if isinstance(src.get("orgaoJulgador"), dict) else src.get("orgaoJulgador"),
                "data_ajuizamento": src.get("dataAjuizamento"),
                "ultima_atualizacao": src.get("dataHoraUltimaAtualizacao"),
                "assuntos": [a.get("nome") for a in src.get("assuntos", []) if isinstance(a, dict)],
                "movimentos_codigos": sorted(movs_codigos),
                "movimento_principal": MOV_EMPRESA_VENCE.get(mov_principal),
                "resultado": "empresa_venceu_classe_validada",
            })

        log(f"  {sigla} | offset {offset} | +{len(hits)} hits | total acumulado {len(resultados)}")
        if len(hits) < PAGE_SIZE:
            break

    return resultados


def main():
    log("=== INÍCIO scraper_bancario_v2 (classe validada) ===")
    log(f"Filtro de recência: dataHoraUltimaAtualizacao >= {DATA_INICIO}")

    todos = []
    existentes = set()
    if OUTPUT.exists():
        try:
            todos = json.loads(OUTPUT.read_text())
            existentes = {d["numero"] for d in todos}
            log(f"Carregado output existente: {len(todos)} casos")
        except Exception as e:
            log(f"Não foi possível carregar output existente: {e}")

    for sigla, endpoint in TJS.items():
        log(f"--- {sigla} ({endpoint}) ---")
        try:
            novos = buscar_tribunal(endpoint, sigla, existentes)
            todos.extend(novos)
            log(f"{sigla} concluído: +{len(novos)} casos novos | total geral {len(todos)}")
        except Exception as e:
            log(f"{sigla} ERRO GERAL: {e}")
        OUTPUT.write_text(json.dumps(todos, ensure_ascii=False, indent=2))
        time.sleep(2)

    log(f"=== FIM. Total: {len(todos)} casos salvos em {OUTPUT} ===")


if __name__ == "__main__":
    main()
