"""
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.settings import (
    KEEPEDU_API_KEY,
    KEEPEDU_INSTITUTE,
    KEEPEDU_LISTAGEM_AVALIACAO_METODO,
    KEEPEDU_LISTAGEM_AVALIACAO_PAYLOAD,
    KEEPEDU_LISTAGEM_AVALIACAO_TIMEOUT,
    KEEPEDU_LISTAGEM_AVALIACAO_URL,
)
from app.time_utils import agora_local

logger = logging.getLogger(__name__)

_CACHE_TTL_SECONDS = 120
_cache: dict[str, Any] = {
    "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": KEEPEDU_INSTITUTE,
        "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()

    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,
        }

    if not KEEPEDU_LISTAGEM_AVALIACAO_URL:
        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 KEEPEDU_INSTITUTE:
        return {
            "success": False,
            "ano_avaliacao": ano,
            "total": 0,
            "avaliacoes": [],
            "erro": "Configure KEEPEDU_API_KEY e KEEPEDU_INSTITUTE no .env.",
            "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(
                    KEEPEDU_LISTAGEM_AVALIACAO_URL,
                    headers=_cabecalhos_keepedu(),
                    params=payload_req,
                    timeout=KEEPEDU_LISTAGEM_AVALIACAO_TIMEOUT,
                )
            else:
                resposta = sessao.post(
                    KEEPEDU_LISTAGEM_AVALIACAO_URL,
                    headers=_cabecalhos_keepedu(),
                    json=payload_req,
                    timeout=KEEPEDU_LISTAGEM_AVALIACAO_TIMEOUT,
                )

        if resposta.status_code >= 400:
            erro = f"HTTP {resposta.status_code} em {KEEPEDU_LISTAGEM_AVALIACAO_URL}"
            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()
    }
