"""
Exportação da síntese RSL para o modelo LaTeX UPE (LATEX_TESE_DOUTORADO_2024).
Tabelas com legenda explicativa, layout ABNT e sem numeração espúria em subníveis.
"""

from __future__ import annotations

import re
import zipfile
from io import BytesIO
from pathlib import Path

from rsl.services.export_latex import (
    MARKER_VERSION,
    RSL_SYSTEM_NAME,
    _bibtex_entry,
    _marker_block,
    _marker_section,
    _marker_table,
    latex_escape_text,
    latex_header_cell,
    latex_label_id,
    latex_normalize_label_id,
    latex_table_ref,
    normalize_unicode_for_latex,
    sanitize_plain_text,
)

PROJECT_ROOT = Path(__file__).resolve().parents[2]
UPE_TEMPLATE_DIR = PROJECT_ROOT / "LATEX_TESE_DOUTORADO_2024"
UPE_TEMPLATE_ZIP = PROJECT_ROOT / "LATEX_TESE_DOUTORADO_2024.zip"
MAIN_TEX = "ABNTEX/ppgec-abntex2-modelo.tex"

RSL_PACKAGES = r"""
% --- Pacotes para tabelas da RSL (InnoRSL) ---
\usepackage{booktabs}
\usepackage{longtable}
\usepackage{array}
\usepackage{tabularx}
\usepackage{float}
\newcolumntype{R}[1]{>{\raggedright\arraybackslash}p{#1}}
"""

UPE_SECTIONS = (
    ("resumo_executivo", "Resumo da revisão"),
    ("conformidade_prisma", "Conformidade com PRISMA 2020"),
    ("introducao", "Introdução e contextualização"),
    ("metodos", "Protocolo de pesquisa"),
    ("resultados_selecao", "Processo de seleção"),
    ("caracterizacao", "Caracterização dos estudos incluídos"),
    ("sintese_tematica", "Síntese das evidências"),
    ("lacunas_literatura", "Lacunas de pesquisa"),
    ("discussao", "Discussão"),
    ("lacunas_rsl", "Limitações da revisão"),
    ("qualidade_metodologica_rsl", "Qualidade metodológica da RSL"),
    ("conclusao", "Considerações finais do capítulo"),
)


def _ensure_upe_template() -> Path:
    if UPE_TEMPLATE_DIR.is_dir() and (UPE_TEMPLATE_DIR / MAIN_TEX).is_file():
        return UPE_TEMPLATE_DIR
    if UPE_TEMPLATE_ZIP.is_file():
        UPE_TEMPLATE_DIR.mkdir(parents=True, exist_ok=True)
        with zipfile.ZipFile(UPE_TEMPLATE_ZIP, "r") as zf:
            zf.extractall(UPE_TEMPLATE_DIR)
        return UPE_TEMPLATE_DIR
    raise FileNotFoundError(
        "Modelo UPE não encontrado. Coloque LATEX_TESE_DOUTORADO_2024.zip na raiz do projeto."
    )


def _patch_main_tex(tex: str) -> str:
    if "usepackage{booktabs}" in tex:
        return tex
    anchor = r"\usepackage{microtype}"
    if anchor in tex:
        return tex.replace(anchor, anchor + "\n" + RSL_PACKAGES)
    return tex.replace(
        r"\usepackage{graphicx}",
        r"\usepackage{graphicx}" + "\n" + RSL_PACKAGES,
    )


def _sanitize_narrative(text: str) -> str:
    """Limpa rascunho antigo com \\ref quebrado, «» e \\% literais."""
    s = normalize_unicode_for_latex(text or "")
    s = re.sub(r"\\?\{\}\\?ref\{[^}]*\}", "", s)
    s = re.sub(r"Tabela~\\ref\{[^}]+\}", "a tabela correspondente", s)
    s = s.replace(r"\%", " por cento")
    return s.strip()


def _latex_body_paragraphs(text: str) -> list[str]:
    blocks = [b.strip() for b in _sanitize_narrative(text).split("\n\n") if b.strip()]
    if not blocks:
        return [rf"% (texto vazio --- edite no {RSL_SYSTEM_NAME} ou aqui)"]
    out = []
    for block in blocks:
        out.append(latex_escape_text(block))
        out.append("")
    return out


def _truncate_cell(text: str, max_len: int = 48) -> str:
    s = str(text or "").strip()
    if len(s) > max_len:
        return s[: max_len - 1] + "…"
    return s


def _table_source_note(review, package: dict, detail: str) -> str:
    n = package["meta"]["n_final"]
    return (
        r"{\footnotesize\textit{Fonte}: elaboração do autor com base na extração "
        f"estruturada da RSL (revisão {review.id}, seleção final $n={n}$). "
        + latex_escape_text(detail)
        + r"}"
    )


def _table_intro(explanation_plain: str, ref_label: str | None = None) -> list[str]:
    """Texto explicativo; referência LaTeX inserida sem escape destrutivo."""
    body = latex_escape_text(explanation_plain)
    if ref_label:
        prefix = latex_table_ref(ref_label) + " "
        return ["", prefix + body, ""]
    return ["", body, ""]


def _cell_latex(value: Any, col_index: int, ncols: int) -> str:
    """Célula de tabela; coluna percentual recebe \\% correto."""
    if col_index == ncols - 1 and ncols >= 3:
        num = str(value).replace("%", "").strip()
        try:
            float(num)
            return latex_escape_text(num) + r"\%"
        except ValueError:
            pass
    return latex_escape_text(
        _truncate_cell(sanitize_plain_text(value), 55 if col_index == 0 else 14)
    )


def _tabular_spec(ncols: int) -> str:
    if ncols == 2:
        return "l r"
    if ncols == 3:
        return "l r r"
    if ncols == 4:
        return "R{0.35\\textwidth} r r r"
    # muitas colunas: primeira coluna larga, demais centradas
    return "R{0.28\\textwidth}" + " c" * (ncols - 1)


def _upe_table_float(
    headers: list[str],
    rows: list[list],
    caption: str,
    label: str,
    explanation_plain: str,
    review,
    package: dict,
    source_detail: str,
    *,
    ref_label: str | None = None,
) -> list[str]:
    ncols = len(headers)
    label = latex_normalize_label_id(label)
    ref_label = latex_normalize_label_id(ref_label or label)
    table_id = ref_label.replace("tab:", "", 1)
    lines = _table_intro(explanation_plain, ref_label=ref_label)
    lines.extend(_marker_table(table_id, [f"% caption: {latex_escape_text(caption[:80])}"]))
    lines += [
        r"\begin{table}[H]",
        r"\centering",
        r"\footnotesize",
        r"\caption{" + latex_escape_text(caption) + "}",
        r"\label{" + label + "}",
    ]
    spec = _tabular_spec(ncols)
    if ncols > 5:
        lines.append(r"\resizebox{\linewidth}{!}{%")
    lines += [r"\begin{tabular}{" + spec + "}", r"\toprule"]
    lines.append(" & ".join(latex_header_cell(h) for h in headers) + r" \\")
    lines.append(r"\midrule")
    for row in rows:
        cells = [_cell_latex(c, i, ncols) for i, c in enumerate(row)]
        lines.append(" & ".join(cells) + r" \\")
    lines += [r"\bottomrule", r"\end{tabular}"]
    if ncols > 5:
        lines.append(r"}")
    lines += [r"\end{table}", _table_source_note(review, package, source_detail), ""]
    return lines


def _upe_longtable_characterization(
    rows: list[list],
    review,
    package: dict,
    n_final: int,
) -> list[str]:
    explanation = (
        f"resume os {n_final} estudos da seleção final com atributos bibliométricos e "
        f"técnicos extraídos no {RSL_SYSTEM_NAME} (domínio, modelo, contexto edge e uso de LLM). "
        "Cada linha corresponde a um estudo incluído após triagem."
    )
    matrix_label = latex_normalize_label_id("tab:rsl-matrix-upe")
    lines = _table_intro(explanation, ref_label=matrix_label)
    lines += [
        r"\footnotesize",
        r"\begin{longtable}{@{}R{2.8cm} c R{2.2cm} R{2.2cm} c c@{}}",
        rf"\caption{{Caracterização dos estudos incluídos na seleção final}} \label{{{matrix_label}}} \\",
        r"\toprule",
        r"Autores & Ano & Domínio & Modelo & Edge & LLM \\",
        r"\midrule",
        r"\endfirsthead",
        rf"\multicolumn{{6}}{{c}}{{\footnotesize\continuação da Tabela \ref{{{matrix_label}}}}} \\",
        r"\toprule",
        r"Autores & Ano & Domínio & Modelo & Edge & LLM \\",
        r"\midrule",
        r"\endhead",
        r"\midrule",
        r"\multicolumn{6}{r}{\footnotesize Continua na próxima página} \\",
        r"\endfoot",
        r"\bottomrule",
        r"\endlastfoot",
    ]
    for row in rows:
        cells = [
            latex_escape_text(_truncate_cell(sanitize_plain_text(c), 42 if i == 0 else 22))
            for i, c in enumerate(row)
        ]
        lines.append(" & ".join(cells) + r" \\")
    lines += [
        r"\end{longtable}",
        r"\normalsize",
        _table_source_note(
            review,
            package,
            "Campos Application Domain, Model Type, Edge AI Context e LLM-based do formulário de extração.",
        ),
        "",
    ]
    return lines


def _protocol_upe_lines(review, package: dict) -> list[str]:
    picoc = package["picoc"]
    lines = [
        latex_escape_text(
            "O protocolo registrado no sistema consolida o escopo PICOC, as perguntas de pesquisa "
            "e as bases consultadas, conforme a seguir."
        ),
        "",
        r"\begin{description}",
    ]
    for key, label in [
        ("objective", "Objetivo"),
        ("population", "População"),
        ("intervention", "Intervenção"),
        ("comparison", "Comparação"),
        ("outcome", "Desfecho"),
        ("context", "Contexto"),
    ]:
        val = picoc.get(key, "") or ""
        lines.append(f"\\item[{latex_escape_text(label)}] {latex_escape_text(val) or '---'}")
    lines.append(r"\end{description}")
    if package.get("questions"):
        lines.append("")
        lines.append(
            latex_escape_text("As perguntas de pesquisa que orientam a síntese são listadas abaixo.")
        )
        lines.append(r"\begin{enumerate}")
        for q in package["questions"]:
            lines.append(f"  \\item {latex_escape_text(q)}")
        lines.append(r"\end{enumerate}")
    lines.append("")
    lines.append(latex_escape_text("As bases de dados vinculadas à revisão foram:"))
    lines.append(r"\begin{itemize}")
    for source in review.sources.all():
        lines.append(f"  \\item {latex_escape_text(source.name)}")
    lines.append(r"\end{itemize}")
    return lines


def _build_evidence_annex(package: dict) -> str:
    """Corpo do anexo (sem \\chapter; incluído em apendices.tex)."""
    parts = [
        r"% RSL:BLOCK:EVIDENCE:BEGIN",
        latex_escape_text(
            "Transcrição dos campos narrativos da extração por estudo incluído. "
            "Os blocos abaixo usam título em negrito, sem criar subseções numeradas."
        ),
        "",
    ]
    for row in package.get("dataset", []):
        aid = row["id"]
        parts.append(f"% RSL:STUDY:{aid}:BEGIN")
        parts.append(r"\noindent\rule{\textwidth}{0.4pt}")
        parts.append(
            r"\noindent\textbf{"
            + latex_escape_text(_truncate_cell(row.get("title", ""), 90))
            + r"} "
            + f"(ID~{aid}, {latex_escape_text(str(row.get('year', '')))})."
        )
        parts.append("")
        for fname in ("Main Contribution", "Research Gap", "Identified Limitations"):
            val = row.get("fields", {}).get(fname, "")
            if isinstance(val, list):
                val = "; ".join(val)
            if val:
                parts.append(r"\noindent\textit{" + latex_escape_text(fname) + r":} ")
                parts.append(latex_escape_text(str(val)[:2000]))
                parts.append("")
        parts.append(f"% RSL:STUDY:{aid}:END")
        parts.append("")
    parts.append(r"% RSL:BLOCK:EVIDENCE:END")
    return "\n".join(parts)


def build_upe_rsl_tex(review, package: dict) -> str:
    meta = package["meta"]
    funnel = package["funnel"]
    sections = {
        k: _sanitize_narrative(v) for k, v in package.get("sections", {}).items()
    }
    n_final = meta["n_final"]

    parts: list[str] = [
        r"\chapter{Revisão Sistemática da Literatura}",
        r"\label{cap:rsl}",
        f"% {MARKER_VERSION} | UPE | review_id={review.id} | n_final={n_final}",
        latex_escape_text(
            f"Este capítulo apresenta a síntese da revisão sistemática conduzida no {RSL_SYSTEM_NAME}. "
            "Cada tabela é precedida de texto que explica seu propósito, a origem dos dados e "
            "a forma de leitura dos valores, em conformidade com a apresentação tabular da ABNT."
        ),
        "",
    ]

    parts.extend(
        _marker_block(
            "METADATA",
            [
                f"% RSL:DATA:review_slug={review.slug}",
                f"% RSL:DATA:generated_at={meta.get('generated_at', '')}",
            ],
        )
    )

    for key, title in UPE_SECTIONS:
        parts.append(f"\\section{{{latex_escape_text(title)}}}")
        parts.append(f"\\label{{sec:rsl-{key}}}")
        parts.extend(_marker_section(key, []))
        parts.extend(_latex_body_paragraphs(sections.get(key, "")))

        if key == "metodos":
            parts.extend(_marker_block("PROTOCOL", []))
            parts.extend(_protocol_upe_lines(review, package))

        if key == "conformidade_prisma":
            parts.extend(
                _upe_table_float(
                    ["Etapa do fluxo", "Contagem"],
                    [
                        ["Registros identificados", funnel["identificados"]],
                        ["Estudos aceitos na triagem", funnel["aceitos"]],
                        ["Estudos rejeitados", funnel["rejeitados"]],
                        ["Registros duplicados", funnel["duplicados"]],
                        ["Não classificados (no momento da exportação)", funnel["nao_classificados"]],
                        ["Seleção final para síntese", funnel["selecao_final"]],
                    ],
                    "Funil de seleção alinhado ao PRISMA 2020",
                    latex_normalize_label_id("tab:rsl-prisma-upe"),
                    "documenta as etapas de identificação e triagem da revisão ativa. "
                    "A seleção final é o conjunto sobre o qual se calculam percentuais e cruzamentos.",
                    review,
                    package,
                    f"Contagens do módulo de seleção de estudos do {RSL_SYSTEM_NAME}.",
                    ref_label=latex_normalize_label_id("tab:rsl-prisma-upe"),
                )
            )

        if key == "resultados_selecao" and package.get("year_distribution"):
            parts.extend(
                _upe_table_float(
                    ["Ano de publicação", "n", "Percentual"],
                    [
                        [r["year"], r["count"], r["percent"]]
                        for r in package["year_distribution"]
                    ],
                    "Distribuição temporal dos estudos incluídos",
                    latex_normalize_label_id("tab:rsl-ano"),
                    "mostra a concentração de publicações por ano na seleção final. "
                    f"Os percentuais são calculados sobre n={n_final} estudos.",
                    review,
                    package,
                    "Campo Year da extração e metadados bibliográficos.",
                    ref_label=latex_normalize_label_id("tab:rsl-ano"),
                )
            )
            if package.get("source_distribution"):
                parts.extend(
                    _upe_table_float(
                        ["Base de dados", "n", "Percentual"],
                        [
                            [r["source"], r["count"], r["percent"]]
                            for r in package["source_distribution"]
                        ],
                        "Distribuição dos estudos por base de busca",
                        latex_normalize_label_id("tab:rsl-base"),
                        "indica de quais bases provêm os estudos sintetizados, "
                        "o que ajuda a avaliar possível viés de cobertura.",
                        review,
                        package,
                        f"Base associada a cada artigo no {RSL_SYSTEM_NAME}.",
                        ref_label=latex_normalize_label_id("tab:rsl-base"),
                    )
                )

        if key == "caracterizacao" and package.get("summary_table"):
            rows = [
                [
                    r["authors"],
                    r["year"],
                    r["domain"],
                    r["model"],
                    r["edge"],
                    r["llm"],
                ]
                for r in package["summary_table"]
            ]
            parts.extend(_upe_longtable_characterization(rows, review, package, n_final))

        if key == "sintese_tematica":
            parts.append(
                latex_escape_text(
                    "As tabelas desta seção sintetizam campos categóricos da extração. "
                    "Em campos de seleção múltipla, um estudo pode contribuir em mais de uma "
                    "categoria; os percentuais referem-se ao número de estudos na seleção final."
                )
            )
            parts.append("")
            for ft in package.get("frequency_tables", []):
                field = ft["field"]
                rows = [[r["label"], r["count"], r["percent"]] for r in ft.get("rows", [])]
                if not rows:
                    continue
                label = latex_label_id("freq", field)
                parts.extend(
                    _upe_table_float(
                        ["Categoria", "n", "Percentual"],
                        rows,
                        f"Distribuição do campo {field}",
                        label,
                        f"apresenta a frequência de categorias do campo \"{field}\" entre os "
                        f"estudos incluídos (n={ft.get('total_studies', n_final)}). "
                        "Valores absolutos e percentuais indicam padrões dominantes na amostra.",
                        review,
                        package,
                        f"Agregação do campo {field} do formulário de extração.",
                        ref_label=label,
                    )
                )

            for ct in package.get("crosstabs", []):
                cols = list(ct["columns"])
                if len(cols) > 6:
                    continue
                label = latex_label_id("x", ct["field_a"], ct["field_b"])
                headers = [_truncate_cell(ct["field_a"], 22)] + [_truncate_cell(c, 10) for c in cols]
                xrows = []
                for row in ct.get("rows", []):
                    line = [_truncate_cell(row["row_label"], 28)]
                    for cell in row["cells"]:
                        line.append(str(cell["count"]))
                    xrows.append(line)
                parts.extend(
                    _upe_table_float(
                        headers,
                        xrows,
                        f"Cruzamento {ct['field_a']} e {ct['field_b']}",
                        label,
                        f"exibe co-ocorrências entre os campos \"{ct['field_a']}\" e \"{ct['field_b']}\". "
                        "Cada célula indica quantos estudos da seleção final apresentam simultaneamente "
                        "o par de categorias; leitura conjunta com as tabelas de frequência marginais.",
                        review,
                        package,
                        "Cruzamento automático a partir da extração; múltipla seleção pode inflar contagens.",
                        ref_label=label,
                    )
                )

        if key == "lacunas_literatura" and package.get("gaps"):
            parts.extend(
                _upe_table_float(
                    ["Tema de lacuna (resumo)", "n estudos"],
                    [[g["theme"][:70], g["count"]] for g in package["gaps"][:15]],
                    "Temas de lacunas reportadas (Research Gap)",
                    latex_normalize_label_id("tab:rsl-gaps-upe"),
                    "agrupa lacunas declaradas pelos autores primários no campo Research Gap, "
                    "por similaridade textual. Temas com n baixo exigem leitura qualitativa.",
                    review,
                    package,
                    "Campo Research Gap da extração estruturada.",
                    ref_label=latex_normalize_label_id("tab:rsl-gaps-upe"),
                )
            )

    parts.append(r"\section{Corpus bibliográfico da seleção final}")
    parts.append(
        latex_escape_text(
            f"A seleção final compreende {n_final} estudos. A listagem abaixo vincula cada item "
            "à chave BibTeX gerada (prefixo rsl_art_) para citação no texto."
        )
    )
    parts.append("")
    parts.append(r"\begin{enumerate}")
    for row in package.get("dataset", []):
        key = f"rsl_art_{row['id']}"
        auth = latex_escape_text(_truncate_cell((row.get("authors") or "Autor").split(",")[0], 50))
        year = latex_escape_text(str(row.get("year", "")))
        parts.append(f"  \\item \\cite{{{key}}} — {auth} ({year}).")
    parts.append(r"\end{enumerate}")
    parts.append("")

    return "\n".join(parts)


def build_upe_bib_append(package: dict) -> str:
    lines = [
        "",
        f"% --- RSL {RSL_SYSTEM_NAME}: estudos da seleção final ---",
        "% RSL:BLOCK:BIBTEX:BEGIN",
        "",
    ]
    for row in package.get("dataset", []):
        lines.append(_bibtex_entry(row))
        lines.append("")
    lines.append("% RSL:BLOCK:BIBTEX:END")
    return "\n".join(lines)


def _overleaf_readme(review, package: dict) -> str:
    return f"""# Modelo UPE + síntese RSL (Overleaf)

**Main document:** `ABNTEX/ppgec-abntex2-modelo.tex`

## Alterações {RSL_SYSTEM_NAME}

- `conteudo/rsl.tex` — capítulo com texto explicativo **antes** de cada tabela
- `conteudo/rsl-evidencias.tex` — anexo (sem subseções numeradas)
- `referencias.bib` — chaves `rsl_art_<id>`
- Pacotes `booktabs`, `longtable`, `tabularx` injetados no modelo principal

Revisão ID {review.id} | Seleção final: {package['meta']['n_final']} estudos

## Compilar

pdfLaTeX → **Recompile (2–3×)** para resolver `Tabela~\ref{...}` no texto introdutório
(antes de cada tabela). Se ainda aparecer «Tabela ??», reexporte o ZIP — versões antigas
tinham `\\label{{tab:rsl-base}}` e `\\ref{{tab:rsl_base}}` divergentes.

Evidências no anexo após o capítulo RSL.
"""


def export_upe_thesis_zip(review, package: dict) -> BytesIO:
    template = _ensure_upe_template()
    slug = review.slug or f"review-{review.id}"
    root_name = f"{slug}-tese-upe-rsl"
    evidence_tex = _build_evidence_annex(package)

    buf = BytesIO()
    with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
        for path in template.rglob("*"):
            if path.is_dir():
                continue
            rel = path.relative_to(template)
            arc = f"{root_name}/{rel.as_posix()}"
            data = path.read_bytes()

            if rel.as_posix() == "conteudo/rsl.tex":
                data = build_upe_rsl_tex(review, package).encode("utf-8")
            elif rel.as_posix() == "referencias.bib":
                text = path.read_text(encoding="utf-8", errors="replace")
                if "% RSL:BLOCK:BIBTEX:BEGIN" in text:
                    text = re.sub(
                        r"% RSL:BLOCK:BIBTEX:BEGIN.*?% RSL:BLOCK:BIBTEX:END",
                        build_upe_bib_append(package).strip(),
                        text,
                        flags=re.DOTALL,
                    )
                else:
                    text = text.rstrip() + build_upe_bib_append(package)
                data = text.encode("utf-8")
            elif rel.as_posix() == MAIN_TEX.replace("\\", "/"):
                text = path.read_text(encoding="utf-8", errors="replace")
                data = _patch_main_tex(text).encode("utf-8")
            elif rel.as_posix() == "postextuais/apendices.tex":
                text = path.read_text(encoding="utf-8", errors="replace")
                if "rsl-evidencias" not in text:
                    insert = (
                        r"\chapter{Evidências primárias da extração (RSL)}"
                        "\n\\label{anexo:rsl-evidencias}\n"
                        r"\input{conteudo/rsl-evidencias}"
                        "\n\n"
                    )
                    text = text.replace(
                        r"\begin{apendicesenv}",
                        r"\begin{apendicesenv}" + "\n" + insert,
                    )
                data = text.encode("utf-8")

            zf.writestr(arc, data)

        zf.writestr(
            f"{root_name}/conteudo/rsl-evidencias.tex",
            evidence_tex.encode("utf-8"),
        )

        zf.writestr(
            f"{root_name}/OVERLEAF-UPE.md",
            _overleaf_readme(review, package).encode("utf-8"),
        )
    buf.seek(0)
    return buf
