"""
Avaliação metodológica da qualidade da RSL (revisão sistemática), não dos estudos primários.
Inspirado em critérios PRISMA 2020 e boas práticas de protocolo reprodutível.
"""

from __future__ import annotations

from typing import Any


def _status(ok: bool, partial: bool = False) -> str:
    if ok:
        return "ok"
    if partial:
        return "partial"
    return "missing"


def assess_review_quality(review, package: dict) -> dict[str, Any]:
    """Classifica a maturidade metodológica da revisão conduzida no InnoRSL."""
    picoc = package.get("picoc", {})
    funnel = package.get("funnel", {})
    qs = package.get("quality_summary", {})
    protocol = package.get("protocol_snapshot", {})
    lacunas = package.get("methodological_lacunas", [])
    n_final = package.get("meta", {}).get("n_final", 0)

    picoc_filled = sum(
        1
        for k in ("population", "intervention", "comparison", "outcome", "context")
        if (picoc.get(k) or "").strip()
    )
    has_objective = bool((review.objective or picoc.get("objective") or "").strip())
    has_questions = bool(package.get("questions"))
    hypotheses = package.get("hypotheses", [])
    has_hypotheses = bool(hypotheses)
    has_strong = any(h.get("type") == "S" for h in hypotheses)
    has_weak = any(h.get("type") == "W" for h in hypotheses)
    has_inclusion = bool(package.get("inclusion"))
    has_exclusion = bool(package.get("exclusion"))
    n_sources = protocol.get("n_sources", 0)
    has_baseline = bool((protocol.get("baseline_search_string") or "").strip())
    n_with_string = protocol.get("n_sources_with_string", 0)
    strings_ratio = (n_with_string / n_sources) if n_sources else 0.0
    has_checklist = qs.get("has_checklist", False)
    n_scored = qs.get("n_scored", 0)
    cutoff = qs.get("cutoff", 0) or 0
    has_extraction = bool(package.get("extraction_fields"))
    identificados = funnel.get("identificados", 0)

    critical_types = {"Protocolo", "Seleção"}
    n_critical = sum(1 for lac in lacunas if lac.get("tipo") in critical_types)

    criteria: list[dict[str, Any]] = [
        {
            "id": "objetivo_picoc",
            "label": "Objetivo e escopo PICOC",
            "status": _status(
                has_objective and picoc_filled >= 3,
                has_objective or picoc_filled >= 1,
            ),
            "detail": (
                f"Objetivo {'definido' if has_objective else 'pendente'}; "
                f"{picoc_filled}/5 dimensões PICOC preenchidas."
            ),
            "weight": 10,
        },
        {
            "id": "perguntas_pesquisa",
            "label": "Perguntas de pesquisa (RQ)",
            "status": _status(has_questions),
            "detail": (
                f"{len(package.get('questions', []))} RQ(s) cadastrada(s)."
                if has_questions
                else "Cadastre RQs no protocolo (passo 4)."
            ),
            "weight": 6,
        },
        {
            "id": "hipoteses_fortes",
            "label": "Hipóteses fortes",
            "status": _status(has_strong),
            "detail": (
                f"{sum(1 for h in hypotheses if h.get('type') == 'S')} hipótese(s) forte(s)."
                if has_strong
                else "Cadastre ao menos uma hipótese forte (passo 5 do protocolo)."
            ),
            "weight": 8,
        },
        {
            "id": "hipoteses_fracas",
            "label": "Hipóteses fracas",
            "status": _status(has_weak, has_hypotheses and not has_weak),
            "detail": (
                f"{sum(1 for h in hypotheses if h.get('type') == 'W')} hipótese(s) fraca(s)."
                if has_weak
                else (
                    "Opcional: hipóteses fracas exploratórias."
                    if has_strong
                    else "Cadastre hipóteses no protocolo."
                )
            ),
            "weight": 4,
        },
        {
            "id": "criterios_elegibilidade",
            "label": "Critérios de inclusão e exclusão",
            "status": _status(has_inclusion and has_exclusion, has_inclusion or has_exclusion),
            "detail": (
                f"Inclusão: {len(package.get('inclusion', []))}; "
                f"exclusão: {len(package.get('exclusion', []))}."
            ),
            "weight": 8,
        },
        {
            "id": "bases_dados",
            "label": "Bases de dados no protocolo",
            "status": _status(n_sources >= 2, n_sources >= 1),
            "detail": f"{n_sources} base(s) vinculada(s) à revisão.",
            "weight": 8,
        },
        {
            "id": "string_baseline",
            "label": "String de busca geral (baseline)",
            "status": _status(has_baseline),
            "detail": (
                "String baseline registrada no protocolo."
                if has_baseline
                else "Defina a string geral em Planejamento → Protocolo."
            ),
            "weight": 10,
        },
        {
            "id": "strings_por_base",
            "label": "Strings adaptadas por base",
            "status": _status(strings_ratio >= 0.8, strings_ratio >= 0.4),
            "detail": (
                f"{n_with_string}/{n_sources} bases com string específica "
                f"({round(100 * strings_ratio)}%)."
            ),
            "weight": 8,
        },
        {
            "id": "checklist_qualidade_estudos",
            "label": "Checklist de qualidade dos estudos",
            "status": _status(has_checklist),
            "detail": (
                f"{len(qs.get('questions', []))} critério(s) no checklist."
                if has_checklist
                else "Configure o checklist em Planejamento → Qualidade."
            ),
            "weight": 10,
        },
        {
            "id": "aplicacao_qa",
            "label": "Avaliação de qualidade aplicada",
            "status": _status(
                n_final > 0 and n_scored >= n_final,
                n_scored > 0,
            ),
            "detail": (
                f"{n_scored}/{n_final} estudos da seleção final com pontuação QA."
            ),
            "weight": 10,
        },
        {
            "id": "corte_qualidade",
            "label": "Corte de qualidade na seleção final",
            "status": _status(
                not has_checklist or cutoff > 0,
                has_checklist and cutoff == 0,
            ),
            "detail": (
                f"Corte configurado: {cutoff:.2f} (estudos abaixo são excluídos da síntese)."
                if cutoff > 0
                else (
                    "Sem corte numérico — todos os aceitos entram na síntese."
                    if has_checklist
                    else "N/A sem checklist."
                )
            ),
            "weight": 6,
        },
        {
            "id": "extracao_estruturada",
            "label": "Formulário de extração de dados",
            "status": _status(has_extraction),
            "detail": (
                f"{len(package.get('extraction_fields', []))} campo(s) de extração."
            ),
            "weight": 8,
        },
        {
            "id": "funil_prisma",
            "label": "Funil de seleção documentado",
            "status": _status(identificados > 0 and funnel.get("selecao_final", 0) > 0),
            "detail": (
                f"Identificados: {identificados}; seleção final: {funnel.get('selecao_final', 0)}."
            ),
            "weight": 8,
        },
        {
            "id": "corpus_sintese",
            "label": "Corpus para síntese",
            "status": _status(n_final >= 10, n_final >= 1),
            "detail": f"{n_final} estudo(s) na seleção final.",
            "weight": 6,
        },
        {
            "id": "lacunas_criticas",
            "label": "Ausência de lacunas críticas do processo",
            "status": _status(n_critical == 0, n_critical <= 1),
            "detail": (
                f"{len(lacunas)} alerta(s) metodológico(s); "
                f"{n_critical} crítico(s) (protocolo/seleção)."
            ),
            "weight": 8,
        },
    ]

    status_points = {"ok": 1.0, "partial": 0.5, "missing": 0.0}
    max_weight = sum(c["weight"] for c in criteria)
    earned = sum(c["weight"] * status_points[c["status"]] for c in criteria)
    score_pct = round(100 * earned / max_weight, 1) if max_weight else 0.0

    if score_pct >= 85:
        classification = "Alta"
        class_detail = (
            "A revisão apresenta protocolo reprodutível, busca documentada e "
            "processo de qualidade consistente para apresentação acadêmica."
        )
    elif score_pct >= 65:
        classification = "Moderada"
        class_detail = (
            "A revisão está estruturada, mas há itens a completar antes da defesa "
            "ou publicação (strings, QA ou corpus)."
        )
    elif score_pct >= 45:
        classification = "Baixa"
        class_detail = (
            "Lacunas metodológicas relevantes — complete protocolo, busca e "
            "avaliação de qualidade antes de apresentar os resultados."
        )
    else:
        classification = "Crítica"
        class_detail = (
            "O processo ainda não sustenta uma síntese confiável; priorize "
            "protocolo, triagem e checklist de qualidade."
        )

    recommendations: list[str] = []
    for c in criteria:
        if c["status"] == "missing":
            recommendations.append(f"Completar: {c['label']} — {c['detail']}")
    for lac in lacunas[:5]:
        recommendations.append(f"[{lac['tipo']}] {lac['descricao']}")
    if not recommendations:
        recommendations.append(
            "Manter rastreabilidade: exportar protocolo, atualizar buscas e registrar decisões de triagem."
        )

    return {
        "score_percent": score_pct,
        "classification": classification,
        "classification_detail": class_detail,
        "criteria": criteria,
        "recommendations": recommendations[:12],
        "framework": "PRISMA 2020 + boas práticas InnoRSL (avaliação automática)",
    }
