"""
Input template: DataJud CNJ API → canonical dict

Documentação oficial: https://datajud-wiki.cnj.jus.br/api-publica/
A API retorna um objeto plano por processo (resultado de query Elasticsearch).

Diferenças estruturais em relação ao Escavador:
  - Um processo = uma instância (sem array de "fontes")
  - grau como string: "G1", "G2", "G3", "JE", "JEF", "TR", etc.
  - nivelSigilo (0-5) em vez de segredo_justica bool
  - classe e orgaoJulgador com codigo numérico (TPU)
  - movimentos nativos com dataHora e complementosTabelados
  - SEM partes, valor_causa (não disponíveis na API pública)

Entrypoint público:
    parse(payload: dict) -> dict   # canonical agnóstico de output

Payload esperado — envelope Elasticsearch (_source) ou objeto direto:
    {
        "numeroProcesso": "0000001-00.2024.8.19.0001",
        "tribunal": "TJRJ",
        "grau": "G1",
        "dataAjuizamento": "2024-01-15T10:00:00",
        "nivelSigilo": 0,
        "sistema": {"codigo": 1, "nome": "PJe"},
        "formato": {"codigo": 1, "nome": "Eletrônico"},
        "classe": {"codigo": 40, "nome": "Procedimento Comum Cível"},
        "assuntos": [{"codigo": 4945, "nome": "Bancários"}],
        "orgaoJulgador": {
            "codigo": 1234,
            "nome": "1ª Vara Cível",
            "codigoMunicipioIBGE": 3304557
        },
        "movimentos": [
            {
                "codigo": 26,
                "nome": "Distribuição",
                "dataHora": "2024-01-15T10:30:00",
                "complementosTabelados": [],
                "orgaoJulgador": {"codigo": 1234, "nome": "1ª Vara Cível"}
            }
        ]
    }
"""

import re

from pyjuris.inputs.escavador import (
    CNJ_REGEX,
    clean_date,
    clean_string,
    classify_tipo,
    classify_fase,
    classify_status_geral,
)

_CNJ_RAW_RE = re.compile(r"^\d{20}$")


def _normalize_numero_cnj(numero: str | None) -> str | None:
    """
    Normaliza número CNJ para o formato com pontuação NNNNNNN-DD.AAAA.J.TT.OOOO.
    A API DataJud às vezes retorna os 20 dígitos sem separadores.
    """
    if not numero:
        return numero
    numero = numero.strip()
    if CNJ_REGEX.match(numero):
        return numero                                   # já formatado
    if _CNJ_RAW_RE.match(numero):
        # NNNNNNN DD AAAA J TT OOOO
        return f"{numero[:7]}-{numero[7:9]}.{numero[9:13]}.{numero[13]}.{numero[14:16]}.{numero[16:]}"
    return numero                                       # retorna como veio; será marcado cnj_invalido


def _parse_data_ajuizamento(value: str | None) -> str | None:
    """
    Suporta formatos de dataAjuizamento da API DataJud:
      - "20251029143925"  → YYYYMMDDHHmmss  (formato nativo DataJud)
      - "2024-01-15"      → ISO 8601 date   (já suportado por clean_date)
      - "2024-01-15T..."  → ISO 8601 dt     (já suportado por clean_date)
    """
    if not value:
        return None
    s = str(value).strip()
    # Formato nativo DataJud: 14 dígitos sem separadores
    if re.match(r"^\d{14}$", s):
        return f"{s[:4]}-{s[4:6]}-{s[6:8]}"
    return clean_date(s)

# Mapeamento de grau string (DataJud) → inteiro canônico
_GRAU_MAP: dict[str, int] = {
    "G1":   1,  # Primeiro Grau
    "G2":   2,  # Segundo Grau
    "G3":   3,  # Terceiro Grau
    "JE":   1,  # Juizado Especial
    "JEF":  1,  # Juizado Especial Federal
    "JECC": 1,  # Juizado Especial Cível e Criminal
    "TR":   2,  # Turma Recursal
    "CJF":  2,  # Conselho da Justiça Federal
    "TNU":  3,  # Turma Nacional de Uniformização
    "SUP":  3,  # Tribunal Superior
}

_GRAU_LABEL = {1: "Primeiro Grau", 2: "Segundo Grau", 3: "Terceiro Grau"}


def _parse_grau(grau_str: str | None) -> int:
    """Converte grau string DataJud em inteiro canônico. Padrão: 1."""
    if not grau_str:
        return 1
    return _GRAU_MAP.get(grau_str.upper(), 1)


def _parse_assuntos(assuntos: list) -> tuple[str | None, str | None, list]:
    """Extrai assunto_principal, assunto_path e lista normalizada."""
    if not assuntos:
        return None, None, []

    normalizado = [
        {"id": a.get("codigo"), "nome": clean_string(a.get("nome")), "path": None}
        for a in assuntos
        if a.get("nome")
    ]
    principal = normalizado[0]["nome"] if normalizado else None
    return principal, None, normalizado


def _parse_movimento(mov: dict) -> dict:
    """Normaliza um movimento DataJud para o formato canônico."""
    orgao_raw = mov.get("orgaoJulgador") or {}
    return {
        "codigo": mov.get("codigo"),
        "nome": clean_string(mov.get("nome")),
        "data_hora": mov.get("dataHora"),
        "complementos": [
            {
                "codigo": c.get("codigo"),
                "nome": clean_string(c.get("nome") or c.get("descricao")),
                "valor": c.get("valor"),
            }
            for c in (mov.get("complementosTabelados") or [])
        ],
        "orgao_julgador": clean_string(orgao_raw.get("nome")),
    }


def _unwrap(payload: dict) -> dict:
    """Aceita envelope Elasticsearch (_source) ou objeto direto."""
    return payload.get("_source", payload)


def parse(payload: dict) -> dict:
    """
    Parseia payload da API pública DataJud CNJ → canonical dict.

    Aceita o objeto direto ou o envelope Elasticsearch com _source.
    """
    doc = _unwrap(payload)

    numero_cnj = _normalize_numero_cnj(clean_string(doc.get("numeroProcesso")))
    fenomenos: list[str] = []

    if not numero_cnj or not CNJ_REGEX.match(numero_cnj or ""):
        fenomenos.append("cnj_invalido")

    grau_str = doc.get("grau")
    grau = _parse_grau(grau_str)

    # Classe processual
    classe_obj = doc.get("classe") or {}
    classe_nome = clean_string(classe_obj.get("nome"))
    classe_codigo = classe_obj.get("codigo")
    classe_upper = classe_nome.upper() if classe_nome else None

    # Tipo da instância derivado da classe (reutiliza lógica do Escavador)
    tipo_inst = classify_tipo(classe_upper, grau)

    # Assuntos
    assunto_principal, assunto_path, assuntos = _parse_assuntos(doc.get("assuntos") or [])

    # Órgão julgador
    orgao_obj = doc.get("orgaoJulgador") or {}
    orgao_nome = clean_string(orgao_obj.get("nome"))

    # Tribunal / sistema
    tribunal_sigla = clean_string(doc.get("tribunal"))
    sistema_obj = doc.get("sistema") or {}
    _sistema_raw = clean_string(sistema_obj.get("nome"))
    # A API retorna "Inválido" quando o sistema não é identificado — trata como None
    sistema_nome = _sistema_raw if _sistema_raw and _sistema_raw.lower() != "inválido" else None

    # Sigilo: nivelSigilo 0 = público, >0 = algum grau de sigilo
    nivel_sigilo = doc.get("nivelSigilo") or 0
    segredo_justica = nivel_sigilo > 0

    # Datas
    data_ajuizamento = _parse_data_ajuizamento(doc.get("dataAjuizamento"))

    # Movimentos
    movimentos_raw = doc.get("movimentos") or []
    movimentos = [_parse_movimento(m) for m in movimentos_raw]

    # Data da última movimentação — infere dos movimentos se não vier explícita
    data_ultima_mov = clean_date(doc.get("dataUltimaAtualizacao"))
    if not data_ultima_mov and movimentos:
        datas = [m["data_hora"] for m in movimentos if m.get("data_hora")]
        if datas:
            data_ultima_mov = clean_date(sorted(datas)[-1])

    # Instância única
    instancia: dict = {
        "fonte_id": None,
        "grau": grau,
        "grau_formatado": _GRAU_LABEL.get(grau, str(grau)),
        "tipo": tipo_inst,
        "tribunal": {
            "sigla": tribunal_sigla,
            "nome": None,   # DataJud não retorna nome por extenso do tribunal
            "sistema": sistema_nome,
        },
        "classe": classe_upper,
        "classe_raw": classe_nome,
        "classe_codigo": classe_codigo,        # campo extra DataJud
        "assunto_principal": assunto_principal,
        "assunto_path": assunto_path,
        "assuntos": assuntos,
        "orgao_julgador": orgao_nome,
        "orgao_julgador_codigo": orgao_obj.get("codigo"),           # campo extra DataJud
        "orgao_julgador_municipio_ibge": orgao_obj.get("codigoMunicipioIBGE"),  # campo extra DataJud
        "valor_causa": None,   # não disponível na API pública DataJud
        "moeda": None,
        "data_inicio": data_ajuizamento,
        "data_ultima_movimentacao": data_ultima_mov,
        "status": "ATIVO",     # DataJud não expõe status diretamente
        "arquivado": None,
        "capa_disponivel": True,
        "partes": [],          # não disponível na API pública DataJud
        "audiencias": [],
        "movimentos": movimentos,
    }

    instancias = [instancia]

    tipo_principal = classify_tipo(classe_upper, grau)
    fase_atual = classify_fase(instancias)
    status_geral = classify_status_geral(instancias)

    if not movimentos_raw and not doc.get("classe"):
        fenomenos.append("sem_fontes")

    return {
        "_fonte_origem": "datajud",
        "numero_cnj": numero_cnj,
        "tipo_principal": tipo_principal,
        "fase_atual": fase_atual,
        "status_geral": status_geral,
        "total_instancias": 1,
        "segredo_justica": segredo_justica,
        "nivel_sigilo": nivel_sigilo,          # campo extra DataJud (0-5)
        "capa_disponivel": True,
        "tem_audiencia": False,
        "tem_multiplas_instancias_ativas": False,
        "fenomenos_detectados": fenomenos,
        "ano_inicio": int(data_ajuizamento[:4]) if data_ajuizamento else None,
        "data_inicio": data_ajuizamento,
        "data_ultima_movimentacao": data_ultima_mov,
        "data_ultima_verificacao": None,
        "polo_ativo": None,    # não disponível na API pública DataJud
        "polo_passivo": None,
        "instancias": instancias,
    }
