"""
pyjuris.orgaos — Referência de órgãos julgadores por tribunal

Dados brutos: scrapy de tribunais brasileiros (32k+ órgãos, 64 tribunais).
Ao carregar, faz correção de encoding e parsing estruturado, com cache.

API pública:
    get_tribunais() -> list[str]
    get_orgaos(tribunal) -> list[OrgaoJulgador]
    get_todos_orgaos() -> dict[str, list[OrgaoJulgador]]
    parsear_orgao(nome) -> OrgaoJulgador
"""

from __future__ import annotations

import json
import re
from functools import lru_cache
from pathlib import Path
from typing import TypedDict


# ─── Tipos ────────────────────────────────────────────────────────────────────


class OrgaoJulgador(TypedDict, total=False):
    nome_original: str
    nome_normalizado: str
    encoding_status: str  # "ok" | "corrigido" | "irrecuperavel"
    tipo: str
    numero: int | None
    especialidade: str | None
    localidade: str | None
    cargo: str | None
    nome_pessoa: str | None


# ─── Encoding ─────────────────────────────────────────────────────────────────


def _fix_encoding(s: str) -> tuple[str, str]:
    """
    Corrige Mojibake Latin-1 → UTF-8 produzido pelo scraper.

    Retorna (texto, status) onde status é 'ok', 'corrigido' ou 'irrecuperavel'.
    """
    try:
        fixed = s.encode("latin-1").decode("utf-8")
    except (UnicodeEncodeError, UnicodeDecodeError):
        return s, "ok"

    if fixed == s:
        return s, "ok"

    return fixed, "irrecuperavel" if "\ufffd" in fixed else "corrigido"


# ─── Parsing estruturado ──────────────────────────────────────────────────────


def _norm(s: str) -> str:
    """Uppercase sem acentos para matching insensível a encoding residual."""
    trans = str.maketrans(
        "ÁÀÂÃÄÉÈÊËÍÌÎÏÓÒÔÕÖÚÙÛÜÇÑ",
        "AAAAAEEEEIIIIOOOOOUUUUCN",
    )
    return s.upper().strip().translate(trans)


_RE_NUMERO = re.compile(r"^\s*0*(\d+)\s*[ªº°]?")

_ESPECIALIDADES: list[tuple[str, str]] = [
    (r"FAZ(ENDA)?\s*P[UÚ]BLICA|FAZENDA P", "FAZENDA_PUBLICA"),
    (r"EXEC[UC].*FISCAL|EXECUCAO FISCAL", "EXECUCAO_FISCAL"),
    (r"PREVIDENCI", "PREVIDENCIARIO"),
    (r"ACIDENTE.*TRABALHO|ACIDENTES TRABALHO", "ACIDENTES_TRABALHO"),
    (r"\bCRIMIN", "CRIMINAL"),
    (r"FAMIL[IÍ]A|FAMILIA", "FAMILIA"),
    (r"ORF[AÃ]OS|SUCESS[OÕ]ES|INTERDITOS", "ORFAOS_SUCESSOES"),
    (r"INF[AÂ]NCIA|JUVENTUDE|\bECA\b", "INFANCIA_JUVENTUDE"),
    (r"CONSUMIDOR|RELAC.*CONSUMO", "CONSUMIDOR"),
    (r"C[IÍ\ufffd]VEL|CIVEL|C.VEL", "CIVEL"),
    (r"TRABALHISTA|\bTRABALHO\b", "TRABALHISTA"),
    (r"ELEITOR", "ELEITORAL"),
    (r"MILITAR", "MILITAR"),
    (r"PLANT[AÃ]O", "PLANTAO"),
    (r"AMBIENTAL", "AMBIENTAL"),
    (r"EMPRESARIAL|COMERC", "EMPRESARIAL"),
    (r"PROPRIEDADE INTELECTUAL|PROPRIEDADE INDUSTRIAL", "PROPRIEDADE_INTELECTUAL"),
    (r"RECUPERA[CÇ].*JUDICIAL|FALEN", "RECUPERACAO_JUDICIAL"),
    (r"AGRAR[IÍ]", "AGRARIO"),
    (r"TRIBUT", "TRIBUTARIO"),
]

_RE_LOCALIDADE: list[str] = [
    r"da Comarca de ([A-ZÀ-Úa-zà-ú][A-ZÀ-Úa-zà-ú\s]+?)(?:\s*[-–(]|$)",
    r"do Foro (?:Central )?(?:da Comarca )?de ([A-ZÀ-Úa-zà-ú][A-ZÀ-Úa-zà-ú\s]+?)(?:\s*[-–(]|$)",
    r"\s[-–]\s([A-ZÀ-Úa-zà-ú][A-ZÀ-Úa-zà-ú\s]+?)\s*$",
    r"(?:^|\s)DE ([A-ZÁÀÂÃÉÈÊÍÌÎÓÒÔÕÚÙÛÇ]{3,}(?:\s+[A-ZÁÀÂÃÉÈÊÍÌÎÓÒÔÕÚÙÛÇ]{2,})*)(?:\s+(?:COM\b|E\s+|\()|\s*$)",
]

_STOP_LOCALIDADE = {
    "VARA", "CÂMARA", "CAMARA", "TURMA", "JUIZADO", "ESPECIAL",
    "FEDERAL", "CIVEL", "CRIMINAL", "FAMILIA", "TRABALHO",
}

_CARGOS_ORDENADOS = [
    "DESEMBARGADORA CONVOCADA",
    "DESEMBARGADOR CONVOCADO",
    "DESEMBARGADORA",
    "DESEMBARGADOR",
    "MINISTRA",
    "MINISTRO",
    "JUÍZA CONVOCADA",
    "JUIZ CONVOCADO",
    "JUÍZA FEDERAL",
    "JUIZ FEDERAL",
    "JUÍZA",
    "JUIZ",
    "CONSELHEIRA",
    "CONSELHEIRO",
    "PROCURADORA",
    "PROCURADOR",
]

_RE_TRIBUNAL_ORIGEM = re.compile(
    r"^(?:DO|DA)\s+(?:TJ/\w+|TRF\d?|TRE/\w+|TST|STJ)\s+",
    re.IGNORECASE,
)
_RE_TRIBUNAL_ORIGEM2 = re.compile(
    r"^(?:TJ/\w+|TRF\d?|TRE/\w+)\s+",
    re.IGNORECASE,
)

# TJSP: vara implícita — "01 CIVEL DE AMERICANA" (sem a palavra VARA)
_RE_TJSP_VARA = re.compile(
    r"^\d+\s+(?:CIVEL|CRIMINAL|FAMILIA|FAZENDA|ACIDENTE|TRABALHO|"
    r"INFANCIA|CONSUMIDOR|PRECATORIO|EXECUCAO|CUMULATIVA|ACIDENTES)\b",
    re.IGNORECASE,
)


def _extrair_numero(s: str) -> int | None:
    m = _RE_NUMERO.match(s)
    if m:
        n = int(m.group(1))
        return n if n > 0 else None
    return None


def _extrair_especialidade(s: str) -> str | None:
    norm = _norm(s)
    for pattern, esp in _ESPECIALIDADES:
        if re.search(pattern, norm):
            return esp
    return None


def _extrair_localidade(s: str) -> str | None:
    for pattern in _RE_LOCALIDADE:
        m = re.search(pattern, s, re.IGNORECASE)
        if m:
            loc = m.group(1).strip().rstrip("-– ").strip()
            if len(loc) >= 3 and not any(kw in loc.upper() for kw in _STOP_LOCALIDADE):
                return loc.title()
    return None


def _extrair_cargo_e_nome(s: str) -> tuple[str | None, str | None]:
    norm = s.upper().strip()
    norm = re.sub(r"^(?:GABINETE?|GAB\.?)\s+(?:DO|DA)\s+", "", norm).strip()
    norm = re.sub(r"^0\s+", "", norm).strip()

    # Abreviações: "Des. Nome" → Desembargador
    m_abrev = re.match(r"^(DES[ÃA]?ª?\.?|DESEMB\.?)\s+(.*)", norm)
    if m_abrev:
        nome_raw = re.sub(r"\s*\(.*\)\s*$", "", m_abrev.group(2)).strip()
        return "Desembargador", nome_raw.title() if nome_raw else None

    for cargo in _CARGOS_ORDENADOS:
        if norm.startswith(cargo):
            rest = norm[len(cargo):].strip()
            rest = _RE_TRIBUNAL_ORIGEM.sub("", rest).strip()
            rest = _RE_TRIBUNAL_ORIGEM2.sub("", rest).strip()
            rest = re.sub(r"\s*\([^)]+\)\s*$", "", rest).strip()
            return cargo.title(), rest.title() if rest else None

    return None, None


def _detectar_tipo(s: str) -> str:
    n = _norm(s)

    if re.search(r"\bGAB(INETE?)?\b", n):
        return "GABINETE"
    if re.search(r"^DES[ÃA]?ª?\.?\s|^DESEMB\.?\s", n):
        return "GABINETE"
    if re.search(r"TURMA RECURSAL", n):
        return "TURMA_RECURSAL"
    if re.search(r"CEJUSC|CENTRO JUD|CENTRO JUDICIARIO|SOLUCAO DE CONFLITOS", n):
        return "CEJUSC"
    if re.search(r"^JEF\b", n):
        return "JUIZADO_ESPECIAL_FEDERAL"
    if re.search(r"^JECC\b", n):
        return "JUIZADO_ESPECIAL"
    if re.search(r"JUIZADO ESPECIAL FEDERAL", n) and not re.search(r"ADJUNTO", n):
        return "JUIZADO_ESPECIAL_FEDERAL"
    if re.search(r"JUIZADO ESPECIAL", n) and not re.search(r"ADJUNTO", n):
        return "JUIZADO_ESPECIAL"
    if re.search(r"\bJUIZADO\b", n) and not re.search(r"ADJUNTO", n):
        return "JUIZADO"
    if re.search(r"\bVARA\b", n):
        return "VARA"
    if re.search(r"CAMARA|C.MARA", n):
        return "CAMARA"
    if re.search(r"\bTURMA\b", n):
        return "TURMA"
    if re.search(r"UNIDADE JURISDICIONAL", n):
        return "UNIDADE_JURISDICIONAL"
    if re.search(r"\bGRUPO\b", n):
        return "GRUPO"
    if re.search(r"SUBSEC|SUBSECAO", n):
        return "SUBSECAO"
    if re.search(r"\bNUCLEO\b", n):
        return "NUCLEO"
    if re.search(r"CORREGEDORIA|CORREGEDOR", n):
        return "CORREGEDORIA"
    if re.search(r"PRESIDENCIA|PRESIDENTE", n):
        return "PRESIDENCIA"
    if re.search(r"\bDIRETORIA\b", n):
        return "DIRETORIA"
    if re.search(r"\bSECRETARIA\b", n):
        return "SECRETARIA"
    if re.search(r"\bPLENO\b|\bPLENARI", n):
        return "PLENARIO"
    if re.search(r"\bSECAO\b", n):
        return "SECAO"
    if _RE_TJSP_VARA.match(s):
        return "VARA"

    return "OUTRO"


def parsear_orgao(nome: str) -> OrgaoJulgador:
    """
    Faz o parsing estruturado de um nome de órgão julgador.

    Retorna um OrgaoJulgador com: tipo, numero, especialidade,
    localidade, cargo, nome_pessoa. Não inclui nome_original nem
    encoding_status (use get_orgaos/get_todos_orgaos para isso).

    Exemplo::

        parsear_orgao("10ª Vara Cível - Porto Alegre")
        # {"tipo": "VARA", "numero": 10, "especialidade": "CIVEL",
        #  "localidade": "Porto Alegre", "cargo": None, "nome_pessoa": None}
    """
    tipo = _detectar_tipo(nome)
    numero = _extrair_numero(nome)
    especialidade = _extrair_especialidade(nome)
    localidade = None if tipo == "GABINETE" else _extrair_localidade(nome)
    cargo, nome_pessoa = (None, None)

    if tipo == "GABINETE":
        cargo, nome_pessoa = _extrair_cargo_e_nome(nome)

    return OrgaoJulgador(
        tipo=tipo,
        numero=numero,
        especialidade=especialidade,
        localidade=localidade,
        cargo=cargo,
        nome_pessoa=nome_pessoa,
    )


# ─── Carregamento lazy ────────────────────────────────────────────────────────


def _load_raw() -> dict[str, list[str]]:
    path = Path(__file__).parent / "data" / "orgaos_julgadores_por_tribunal.json"
    with path.open(encoding="utf-8") as f:
        return json.load(f)


@lru_cache(maxsize=1)
def _load_parsed() -> dict[str, list[OrgaoJulgador]]:
    """Carrega, corrige encoding e faz parsing. Resultado em cache."""
    raw = _load_raw()
    result: dict[str, list[OrgaoJulgador]] = {}

    for tribunal, orgaos in raw.items():
        entries: list[OrgaoJulgador] = []
        for nome_original in orgaos:
            nome_original = nome_original.strip()
            if not nome_original:
                continue

            nome_normalizado, encoding_status = _fix_encoding(nome_original)
            parsed = parsear_orgao(nome_normalizado)

            entries.append(
                OrgaoJulgador(
                    nome_original=nome_original,
                    nome_normalizado=nome_normalizado,
                    encoding_status=encoding_status,
                    **parsed,
                )
            )

        result[tribunal] = entries

    return result


# ─── API pública ──────────────────────────────────────────────────────────────


def get_tribunais() -> list[str]:
    """Retorna a lista de siglas de tribunais disponíveis."""
    return list(_load_parsed().keys())


def get_orgaos(tribunal: str) -> list[OrgaoJulgador]:
    """
    Retorna a lista de órgãos julgadores de um tribunal, já estruturados.

    Exemplo::

        orgaos = get_orgaos("TJRS")
        # [{"nome_original": "10ª Câmara Cível - Porto Alegre",
        #   "tipo": "CAMARA", "numero": 10, ...}, ...]
    """
    tribunal = tribunal.upper()
    parsed = _load_parsed()
    if tribunal not in parsed:
        raise KeyError(f"Tribunal desconhecido: {tribunal!r}. Use get_tribunais() para listar.")
    return parsed[tribunal]


def get_todos_orgaos() -> dict[str, list[OrgaoJulgador]]:
    """Retorna todos os tribunais e seus órgãos estruturados."""
    return dict(_load_parsed())
