"""
Integração com a API KeepEdu — listagem de avaliações/simulados.
"""
from __future__ import annotations

import logging
import time
from typing import Any

import requests

from app.keepedu_http import post_json_comprimido
from app.settings import KEEPEDU_API_KEY
from app.tenant import get_institute_id, resolve_keepedu_url
from app.time_utils import agora_local

logger = logging.getLogger(__name__)

KEEPEDU_LISTAGEM_AVALIACAO_METODO = "POST"
KEEPEDU_LISTAGEM_AVALIACAO_PAYLOAD: dict = {}
KEEPEDU_LISTAGEM_AVALIACAO_TIMEOUT = 20.0

_CACHE_TTL_SECONDS = 120
_cache_por_escola: dict[str, dict[str, Any]] = {}


def _chave_cache_listagem(ano: str) -> str:
    from app.tenant import get_school

    return f"{str(get_school() or '').strip().lower()}|{ano}"


def _entrada_cache_listagem(ano: str) -> dict[str, Any]:
    return _cache_por_escola.setdefault(
        _chave_cache_listagem(ano),
        {
            "ts": 0.0,
            "ano": "",
            "items": [],
            "erro": "",
            "total": 0,
            "success": False,
        },
    )

CAMPOS_EXIBICAO = (
    "id",
    "tipo_prova",
    "tipo_prova_id",
    "bimestre",
    "etapa_prova",
    "estilo_prova",
    "series",
    "disciplina",
    "slug_disciplina",
    "inicio",
    "termino",
    "ano_letivo",
    "professor",
)


def _cabecalhos_keepedu() -> dict[str, str]:
    return {
        "Apikey": KEEPEDU_API_KEY,
        "Institute": get_institute_id(),
        "Content-Type": "application/json",
        "Accept": "application/json",
    }


def _ano_padrao() -> str:
    return str(agora_local().year)


def _payload_requisicao(ano_avaliacao: str | None = None) -> dict[str, Any]:
    payload = dict(KEEPEDU_LISTAGEM_AVALIACAO_PAYLOAD)
    if ano_avaliacao:
        payload.setdefault("ano_avaliacao", ano_avaliacao)
    if "ano_avaliacao" not in payload:
        payload["ano_avaliacao"] = _ano_padrao()
    return payload


def normalizar_avaliacao_item(item: dict[str, Any]) -> dict[str, str]:
    id_texto = str(item.get("id") or "").strip()
    tipo_prova = str(item.get("tipo_prova") or "").strip()
    nome = tipo_prova or f"Avaliação {id_texto}"

    normalizado: dict[str, str] = {
        "id": id_texto,
        "nome": nome,
        "tipo_prova": tipo_prova,
    }
    for campo in CAMPOS_EXIBICAO:
        if campo in ("id", "tipo_prova"):
            continue
        valor = item.get(campo)
        if valor is None:
            continue
        if campo == "tipo_prova_id":
            normalizado[campo] = str(valor).strip()
        elif isinstance(valor, bool):
            normalizado[campo] = "Sim" if valor else "Não"
        else:
            texto = str(valor).strip()
            if texto:
                normalizado[campo] = texto
    return normalizado


def _rotulo_select(item: dict[str, str]) -> str:
    partes = [item.get("tipo_prova") or item.get("nome") or f"Avaliação {item.get('id')}"]
    if item.get("bimestre"):
        partes.append(item["bimestre"])
    if item.get("disciplina"):
        partes.append(item["disciplina"])
    return f"[{item['id']}] · {' · '.join(partes)}"


def _aplicar_rotulos(itens: list[dict[str, str]]) -> list[dict[str, str]]:
    normalizados: list[dict[str, str]] = []
    for item in itens:
        copia = dict(item)
        if copia.get("id"):
            copia["rotulo_select"] = _rotulo_select(copia)
        normalizados.append(copia)
    return normalizados


def _ordenar_itens(itens: list[dict[str, str]]) -> list[dict[str, str]]:
    return sorted(
        itens,
        key=lambda item: (
            item.get("tipo_prova", "").lower(),
            item.get("bimestre", "").lower(),
            int(item["id"]) if str(item.get("id", "")).isdigit() else 0,
        ),
    )


def buscar_listagem_avaliacoes(
    *,
    ano_avaliacao: str | None = None,
    force: bool = False,
) -> dict[str, Any]:
    """
    Consulta a API KeepEdu e devolve avaliações normalizadas.
    """
    ano = str(ano_avaliacao or _ano_padrao()).strip()
    agora = time.time()
    cache = _entrada_cache_listagem(ano)

    if (
        not force
        and cache.get("items")
        and cache.get("ano") == ano
        and (agora - float(cache.get("ts") or 0)) < _CACHE_TTL_SECONDS
    ):
        return {
            "success": True,
            "ano_avaliacao": ano,
            "total": cache.get("total", len(cache["items"])),
            "avaliacoes": _aplicar_rotulos(list(cache["items"])),
            "erro": cache.get("erro", ""),
            "cache": True,
        }

    url_listagem = resolve_keepedu_url(
        "avaliacoes/listagem-avaliacao",
        "KEEPEDU_LISTAGEM_AVALIACAO_URL",
    )
    if not url_listagem:
        return {
            "success": False,
            "ano_avaliacao": ano,
            "total": 0,
            "avaliacoes": [],
            "erro": "KEEPEDU_LISTAGEM_AVALIACAO_URL não configurada.",
            "cache": False,
        }

    if not KEEPEDU_API_KEY or not get_institute_id():
        return {
            "success": False,
            "ano_avaliacao": ano,
            "total": 0,
            "avaliacoes": [],
            "erro": "Configure KEEPEDU_API_KEY e o instituto da escola ativa em KEEPEDU_INSTITUTES (ou KEEPEDU_INSTITUTE como fallback).",
            "cache": False,
        }

    payload_req = _payload_requisicao(ano)
    metodo = KEEPEDU_LISTAGEM_AVALIACAO_METODO
    erro = ""

    try:
        with requests.Session() as sessao:
            if metodo == "GET":
                resposta = sessao.get(
                    url_listagem,
                    headers=_cabecalhos_keepedu(),
                    params=payload_req,
                    timeout=KEEPEDU_LISTAGEM_AVALIACAO_TIMEOUT,
                )
            else:
                resposta = post_json_comprimido(
                    url_listagem,
                    payload_req,
                    headers=_cabecalhos_keepedu(),
                    timeout=KEEPEDU_LISTAGEM_AVALIACAO_TIMEOUT,
                    client=sessao,
                )

        if resposta.status_code >= 400:
            erro = f"HTTP {resposta.status_code} em {url_listagem}"
            raise ValueError(erro)

        dados = resposta.json()
        if not isinstance(dados, dict):
            raise ValueError("Resposta inválida da API (JSON esperado).")

        if dados.get("success") is False:
            erro = str(dados.get("mensagem") or dados.get("message") or "API retornou success=false.")
            raise ValueError(erro)

        brutos = dados.get("avaliacoes") or []
        if not isinstance(brutos, list):
            brutos = []

        itens: list[dict[str, str]] = []
        for bruto in brutos:
            if not isinstance(bruto, dict):
                continue
            item = normalizar_avaliacao_item(bruto)
            if item.get("id"):
                item["rotulo_select"] = _rotulo_select(item)
                itens.append(item)

        itens = _ordenar_itens(itens)
        total = int(dados.get("total") or len(itens))

        cache.update(
            {
                "ts": agora,
                "ano": ano,
                "items": itens,
                "erro": "",
                "total": total,
                "success": True,
            }
        )

        return {
            "success": True,
            "ano_avaliacao": str(dados.get("ano_avaliacao") or ano),
            "total": total,
            "avaliacoes": itens,
            "erro": "",
            "cache": False,
        }
    except Exception as exc:
        logger.warning("Falha na listagem KeepEdu: %s", exc)
        erro = str(exc) or "Erro ao consultar listagem de avaliações."
        cache.update(
            {
                "ts": agora,
                "ano": ano,
                "items": [],
                "erro": erro,
                "total": 0,
                "success": False,
            }
        )
        return {
            "success": False,
            "ano_avaliacao": ano,
            "total": 0,
            "avaliacoes": [],
            "erro": erro,
            "cache": False,
        }


def obter_avaliacao_por_id(
    id_avaliacao: str,
    *,
    ano_avaliacao: str | None = None,
    force: bool = False,
) -> dict[str, str] | None:
    id_texto = str(id_avaliacao or "").strip()
    if not id_texto:
        return None

    resultado = buscar_listagem_avaliacoes(ano_avaliacao=ano_avaliacao, force=force)
    for item in resultado.get("avaliacoes") or []:
        if str(item.get("id", "")).strip() == id_texto:
            return dict(item)
    return None


def indice_avaliacoes_por_id(
    *,
    ano_avaliacao: str | None = None,
    force: bool = False,
) -> dict[str, dict[str, str]]:
    resultado = buscar_listagem_avaliacoes(ano_avaliacao=ano_avaliacao, force=force)
    return {
        str(item.get("id", "")).strip(): item
        for item in resultado.get("avaliacoes") or []
        if str(item.get("id", "")).strip()
    }
