#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""ETL: bruto.json → limpo.csv"""

import json
import csv
import re
import sys
import argparse
from typing import Any, Dict, List, Optional


def load_json(path: str) -> Dict[str, Any]:
    with open(path, "r", encoding="utf-8") as f:
        data = json.load(f)
    if isinstance(data, list):
        if not data:
            raise ValueError("JSON vazio")
        return data[0]
    if isinstance(data, dict):
        return data
    raise ValueError("JSON deve ser objeto ou lista")


def fix_double_encode(text: str) -> str:
    """Corrige double-encode UTF-8 lido como Latin-1."""
    try:
        return text.encode("latin-1").decode("utf-8")
    except (UnicodeDecodeError, UnicodeEncodeError):
        return text


def sanitize_str(value: Any) -> str:
    """Converte qualquer valor para string limpa."""
    if value is None:
        return ""
    s = str(value)
    # Fix double-encode
    s = fix_double_encode(s)
    # Remove chars de controle, substitui \n \r \t por espaço
    s = re.sub(r"[\r\n\t]+", " ", s)
    # Remove outros chars de controle
    s = re.sub(r"[\x00-\x1f\x7f]", "", s)
    # Normaliza espaços
    s = re.sub(r" {2,}", " ", s)
    return s.strip()


def sanitize_value(value: Any) -> str:
    """Sanitiza com tratamento especial para bool."""
    if isinstance(value, bool):
        return "Sim" if value else "Não"
    return sanitize_str(value)


def flatten_record(record: Dict[str, Any]) -> Dict[str, Any]:
    flat: Dict[str, Any] = {}

    # Campos escalares da raiz
    scalar_fields = [
        "numero_cnj", "ano_inicio", "data_inicio",
        "data_ultima_movimentacao", "data_ultima_verificacao",
        "quantidade_movimentacoes", "tempo_desde_ultima_verificacao",
        "titulo_polo_ativo", "titulo_polo_passivo",
        "fontes_tribunais_estao_arquivadas",
    ]
    for field in scalar_fields:
        flat[field] = sanitize_value(record.get(field))

    # estado_origem
    estado = record.get("estado_origem") or {}
    flat["estado_origem_nome"] = sanitize_str(estado.get("nome"))
    flat["estado_origem_sigla"] = sanitize_str(estado.get("sigla"))

    # unidade_origem
    unidade = record.get("unidade_origem") or {}
    flat["unidade_origem_nome"] = sanitize_str(unidade.get("nome"))
    flat["unidade_origem_cidade"] = sanitize_str(unidade.get("cidade"))
    flat["unidade_origem_tribunal_sigla"] = sanitize_str(unidade.get("tribunal_sigla"))

    # fontes[0]
    fontes = record.get("fontes") or []
    fonte = fontes[0] if fontes else {}
    flat["fonte_id"] = sanitize_str(fonte.get("id"))
    flat["fonte_grau"] = sanitize_str(fonte.get("grau"))
    flat["fonte_grau_formatado"] = sanitize_str(fonte.get("grau_formatado"))
    flat["fonte_sistema"] = sanitize_str(fonte.get("sistema"))
    flat["fonte_sigla"] = sanitize_str(fonte.get("sigla"))
    flat["fonte_nome"] = sanitize_str(fonte.get("nome"))
    flat["fonte_descricao"] = sanitize_str(fonte.get("descricao"))
    flat["fonte_status_predito"] = sanitize_str(fonte.get("status_predito"))
    flat["fonte_segredo_justica"] = sanitize_value(fonte.get("segredo_justica"))
    flat["fonte_url"] = sanitize_str(fonte.get("url"))

    # capa
    capa = fonte.get("capa") or {}
    flat["capa_classe"] = sanitize_str(capa.get("classe"))
    flat["capa_assunto"] = sanitize_str(capa.get("assunto"))
    flat["capa_orgao_julgador"] = sanitize_str(capa.get("orgao_julgador"))
    flat["capa_data_distribuicao"] = sanitize_str(capa.get("data_distribuicao"))
    valor_causa = capa.get("valor_causa") or {}
    flat["capa_valor_causa"] = sanitize_str(valor_causa.get("valor_formatado"))

    # assuntos_normalizados
    assuntos = capa.get("assuntos_normalizados") or []
    for i, assunto in enumerate(assuntos, 1):
        flat[f"assunto_{i}"] = sanitize_str(assunto.get("nome"))
        flat[f"assunto_{i}_path"] = sanitize_str(assunto.get("path_completo"))

    # assunto_principal_normalizado
    assunto_principal = capa.get("assunto_principal_normalizado") or {}
    flat["assunto_principal"] = sanitize_str(assunto_principal.get("nome"))
    flat["assunto_principal_path"] = sanitize_str(assunto_principal.get("path_completo"))

    # informacoes_complementares
    infos = capa.get("informacoes_complementares") or []
    for i, info in enumerate(infos, 1):
        flat[f"info_complementar_{i}_tipo"] = sanitize_str(info.get("tipo"))
        flat[f"info_complementar_{i}_valor"] = sanitize_str(info.get("valor"))

    # envolvidos
    envolvidos = fonte.get("envolvidos") or []
    for i, env in enumerate(envolvidos, 1):
        flat[f"envolvido_{i}_nome"] = sanitize_str(env.get("nome"))
        flat[f"envolvido_{i}_tipo"] = sanitize_str(env.get("tipo_normalizado") or env.get("tipo"))
        flat[f"envolvido_{i}_polo"] = sanitize_str(env.get("polo"))
        flat[f"envolvido_{i}_cpf"] = sanitize_str(env.get("cpf"))
        flat[f"envolvido_{i}_cnpj"] = sanitize_str(env.get("cnpj"))
        flat[f"envolvido_{i}_tipo_pessoa"] = sanitize_str(env.get("tipo_pessoa"))
        advs = env.get("advogados") or []
        for j, adv in enumerate(advs, 1):
            flat[f"envolvido_{i}_adv_{j}_nome"] = sanitize_str(adv.get("nome"))
            flat[f"envolvido_{i}_adv_{j}_cpf"] = sanitize_str(adv.get("cpf"))
            oabs = adv.get("oabs") or []
            oab_strs = [f"{o.get('uf','')}-{o.get('numero','')}/{o.get('tipo','')}" for o in oabs]
            flat[f"envolvido_{i}_adv_{j}_oab"] = sanitize_str("; ".join(oab_strs))

    return flat


def deduplicate(records: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
    seen = set()
    result = []
    for rec in records:
        key = rec.get("numero_cnj") or id(rec)
        if key in seen:
            continue
        seen.add(key)
        result.append(rec)
    return result


# Mapa de rótulos fixos
_LABELS = {
    "numero_cnj": "Número CNJ",
    "ano_inicio": "Ano de Início",
    "data_inicio": "Data de Início",
    "data_ultima_movimentacao": "Data Última Movimentação",
    "data_ultima_verificacao": "Data Última Verificação",
    "quantidade_movimentacoes": "Qtd. Movimentações",
    "tempo_desde_ultima_verificacao": "Tempo Desde Verificação",
    "titulo_polo_ativo": "Polo Ativo",
    "titulo_polo_passivo": "Polo Passivo",
    "fontes_tribunais_estao_arquivadas": "Tribunais Arquivados",
    "estado_origem_nome": "Estado de Origem",
    "estado_origem_sigla": "UF de Origem",
    "unidade_origem_nome": "Unidade de Origem",
    "unidade_origem_cidade": "Cidade de Origem",
    "unidade_origem_tribunal_sigla": "Sigla Tribunal",
    "fonte_id": "Fonte ID",
    "fonte_grau": "Grau",
    "fonte_grau_formatado": "Grau Formatado",
    "fonte_sistema": "Sistema",
    "fonte_sigla": "Sigla Fonte",
    "fonte_nome": "Nome Tribunal Fonte",
    "fonte_descricao": "Descrição Fonte",
    "fonte_status_predito": "Status Predito",
    "fonte_segredo_justica": "Segredo de Justiça",
    "fonte_url": "URL",
    "capa_classe": "Classe",
    "capa_assunto": "Assunto Principal (Capa)",
    "capa_orgao_julgador": "Órgão Julgador",
    "capa_data_distribuicao": "Data de Distribuição",
    "capa_valor_causa": "Valor da Causa",
    "assunto_principal": "Assunto Principal Normalizado",
    "assunto_principal_path": "Assunto Principal Path Completo",
}


def _label_dynamic(key: str) -> str:
    """Gera rótulo para colunas dinâmicas (assunto_N, envolvido_N_campo)."""
    # assunto_N
    m = re.match(r"^assunto_(\d+)$", key)
    if m:
        return f"Assunto {m.group(1)}"

    m = re.match(r"^assunto_(\d+)_path$", key)
    if m:
        return f"Assunto {m.group(1)} Path Completo"

    # info_complementar_N_tipo/valor
    m = re.match(r"^info_complementar_(\d+)_(tipo|valor)$", key)
    if m:
        return f"Info Complementar {m.group(1)} {m.group(2).capitalize()}"

    # envolvido_N_adv_M_campo (must be checked before envolvido_N_campo)
    m = re.match(r"^envolvido_(\d+)_adv_(\d+)_(\w+)$", key)
    if m:
        n, j, campo = m.group(1), m.group(2), m.group(3)
        campo_labels = {"nome": "Nome", "cpf": "CPF", "oab": "OAB"}
        return f"Parte {n} Advogado {j} {campo_labels.get(campo, campo.title())}"

    # envolvido_N_campo
    m = re.match(r"^envolvido_(\d+)_(\w+)$", key)
    if m:
        n, campo = m.group(1), m.group(2)
        campo_labels = {
            "nome": "Nome", "tipo": "Tipo", "polo": "Polo",
            "cpf": "CPF", "cnpj": "CNPJ", "tipo_pessoa": "Tipo Pessoa",
        }
        return f"Parte {n} {campo_labels.get(campo, campo.replace('_', ' ').title())}"

    return key.replace("_", " ").title()


def build_labels(flat: Dict[str, Any]) -> Dict[str, str]:
    """Retorna mapa {campo_interno: rótulo PT-BR} para todos os campos do flat."""
    return {k: _LABELS.get(k, _label_dynamic(k)) for k in flat}


def write_csv(
    records: List[Dict[str, Any]],
    labels: Dict[str, str],
    output_path: str,
) -> None:
    if not records:
        return
    # Ordena colunas: fixas primeiro, dinâmicas depois (ordenadas)
    fixed_order = list(_LABELS.keys())
    all_keys = list(records[0].keys())
    dynamic_keys = [k for k in all_keys if k not in _LABELS]
    ordered_keys = [k for k in fixed_order if k in all_keys] + sorted(dynamic_keys)

    fieldnames = [labels.get(k, k) for k in ordered_keys]

    with open(output_path, "w", encoding="utf-8-sig", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
        writer.writeheader()
        for rec in records:
            row = {labels.get(k, k): rec.get(k, "") for k in ordered_keys}
            writer.writerow(row)


def parse_args():
    parser = argparse.ArgumentParser(description="ETL: bruto.json → limpo.csv")
    parser.add_argument("-i", "--input", default="bruto.json")
    parser.add_argument("-o", "--output", default="limpo.csv")
    return parser.parse_args()


def main():
    args = parse_args()
    try:
        record = load_json(args.input)
    except Exception as e:
        print(f"Erro ao ler JSON: {e}", file=sys.stderr)
        sys.exit(1)

    flat = flatten_record(record)
    records = deduplicate([flat])
    labels = build_labels(records[0])
    write_csv(records, labels, args.output)
    print(f"OK. CSV gerado: {args.output} ({len(records)} linha(s))")


if __name__ == "__main__":
    main()
