"""
Exportação LaTeX da síntese RSL com marcadores estruturados e rastreabilidade de dados.

Marcadores (comentários LaTeX, parseáveis por ferramentas externas):
  % RSL-SYNTHESIS-V1
  % RSL:BLOCK:<nome>:BEGIN|END
  % RSL:SECTION:<chave>:BEGIN|END
  % RSL:TABLE:<id>:BEGIN|END
  % RSL:DATA:<chave>=<valor>
  % RSL:BIB:<article_id>
"""

from __future__ import annotations

import re
from io import BytesIO
from typing import Any

MARKER_VERSION = "RSL-SYNTHESIS-V1"
RSL_SYSTEM_NAME = "InnoRSL"

SECTION_ORDER = (
    ("resumo_executivo", "Resumo executivo"),
    ("conformidade_prisma", "Conformidade com PRISMA 2020"),
    ("introducao", "Introdução"),
    ("metodos", "Metodologia"),
    ("resultados_selecao", "Resultados do processo de seleção"),
    ("caracterizacao", "Caracterização dos estudos incluídos"),
    ("sintese_tematica", "Síntese temática"),
    ("lacunas_literatura", "Lacunas reportadas na literatura"),
    ("discussao", "Discussão"),
    ("lacunas_rsl", "Limitações da revisão"),
    ("qualidade_metodologica_rsl", "Qualidade metodológica da RSL"),
    ("conclusao", "Conclusão"),
)

_UNICODE_REPLACEMENTS = {
    "\u00ab": '"',
    "\u00bb": '"',
    "\u201c": '"',
    "\u201d": '"',
    "\u2018": "'",
    "\u2019": "'",
    "\u2013": "--",
    "\u2014": "---",
    "\u2026": "...",
    "\u00a0": " ",
    "\u200b": "",
    "\u200c": "",
    "\u200d": "",
    "\ufeff": "",
}

# Apenas texto livre — não escapa ~ (espaço não separável em \ref) nem comandos LaTeX
_LATEX_TEXT_SPECIAL = {
    "\\": r"\textbackslash{}",
    "{": r"\{",
    "}": r"\}",
    "$": r"\$",
    "&": r"\&",
    "%": r"\%",
    "#": r"\#",
    "_": r"\_",
    "^": r"\^{}",
}


def normalize_unicode_for_latex(text: Any) -> str:
    if text is None:
        return ""
    s = str(text)
    for src, dst in _UNICODE_REPLACEMENTS.items():
        s = s.replace(src, dst)
    return s


def sanitize_plain_text(text: Any) -> str:
    """Remove resquícios de LaTeX/Unicode problemáticos de campos importados."""
    if text is None:
        return ""
    s = normalize_unicode_for_latex(text)
    s = s.replace("\\", "")
    s = re.sub(r"[{}]", "", s)
    return s.strip()


def latex_escape_text(text: Any) -> str:
    """Texto narrativo/células — não use em strings que já contêm \\ref, \\cite, etc."""
    if text is None:
        return ""
    s = sanitize_plain_text(text)
    for char, repl in _LATEX_TEXT_SPECIAL.items():
        s = s.replace(char, repl)
    return s


def latex_header_cell(header: str) -> str:
    """Cabeçalho de coluna; percentual como comando LaTeX válido."""
    h = str(header).strip()
    if h in ("Percentual", "%", r"\%", "\\%", "Perc. (%)", "n (%)"):
        return r"Perc. (\%)"
    return latex_escape_text(h)


def latex_escape(text: Any) -> str:
    """Alias para texto livre (legado)."""
    return latex_escape_text(text)


def latex_label_slug(*parts: str) -> str:
    raw = "_".join(p for p in parts if p)
    slug = re.sub(r"[^a-z0-9]+", "_", raw.lower()).strip("_")
    return slug[:40] if slug else "tabela"


def latex_normalize_label_id(label: str) -> str:
    """
    ID estável para \\label e \\ref (somente a-z, 0-9, _).
    Hífens em tab:rsl-base quebravam \\ref → aparecia «Tabela ??».
    """
    s = (label or "").strip()
    if s.startswith("tab:"):
        s = s[4:]
    slug = latex_label_slug(s) if s else "tabela"
    return f"tab:{slug}"


def latex_table_ref(label: str) -> str:
    """Referência cruzada; usa o mesmo ID normalizado que \\label."""
    lid = latex_normalize_label_id(label)
    return rf"Tabela~\ref{{{lid}}}"


def latex_label_id(*parts: str) -> str:
    return latex_normalize_label_id("tab:" + "_".join(parts))


def _marker_block(name: str, lines: list[str], *, begin_extra: str = "") -> list[str]:
    out = [f"% RSL:BLOCK:{name}:BEGIN"]
    if begin_extra:
        out.append(begin_extra)
    out.extend(lines)
    out.append(f"% RSL:BLOCK:{name}:END")
    return out


def _marker_section(key: str, body_lines: list[str]) -> list[str]:
    return [
        f"% RSL:SECTION:{key}:BEGIN",
        *body_lines,
        f"% RSL:SECTION:{key}:END",
    ]


def _marker_table(table_id: str, lines: list[str]) -> list[str]:
    return [
        f"% RSL:TABLE:{table_id}:BEGIN",
        *lines,
        f"% RSL:TABLE:{table_id}:END",
    ]


def _provenance_note(review, package: dict, detail: str) -> str:
    n = package["meta"]["n_final"]
    ts = package["meta"]["generated_at"]
    cutoff = review.quality_assessment_cutoff_score
    qa_note = (
        f"Corte de qualidade: {cutoff:.2f}."
        if review.quality_questions and cutoff > 0
        else "Sem filtro por pontuação de qualidade."
    )
    note = (
        f'Fonte: revisão sistemática "{review.title}" (ID {review.id}, slug {review.slug}). '
        f"Seleção final: n={n}. {qa_note} {detail} "
        f"Dados extraídos no {RSL_SYSTEM_NAME}; exportação {ts} UTC."
    )
    return rf"\footnote{{{latex_escape_text(note)}}}"


def _latex_paragraphs(text: str) -> list[str]:
    blocks = [b.strip() for b in (text or "").split("\n\n") if b.strip()]
    if not blocks:
        return [r"\textit{(Seção sem texto --- preencha na síntese do sistema.)}"]
    return [latex_escape_text(b) for b in blocks]


def _build_protocol_lines(review, package: dict) -> list[str]:
    lines = []
    picoc = package["picoc"]
    lines.append(r"\subsection{Protocolo registrado (PICOC)}")
    lines.append(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 getattr(review, key, "") or ""
        lines.append(f"\\item[{latex_escape(label)}] {latex_escape(val) or '---'}")
    lines.append(r"\end{description}")

    if package.get("questions"):
        lines.append(r"\subsection{Perguntas de pesquisa (RQ)}")
        lines.append(r"\begin{enumerate}")
        for q in package["questions"]:
            lines.append(f"  \\item {latex_escape(q)}")
        lines.append(r"\end{enumerate}")

    if package.get("hypotheses"):
        lines.append(r"\subsection{Hipóteses}")
        strong = [h for h in package["hypotheses"] if h.get("type") == "S"]
        weak = [h for h in package["hypotheses"] if h.get("type") == "W"]
        if strong:
            lines.append(r"\paragraph{Hipóteses fortes.}")
            lines.append(r"\begin{enumerate}")
            for i, h in enumerate(strong, 1):
                lines.append(f"  \\item {latex_escape(f'HF{i}: ' + h.get('statement', ''))}")
            lines.append(r"\end{enumerate}")
        if weak:
            lines.append(r"\paragraph{Hipóteses fracas.}")
            lines.append(r"\begin{enumerate}")
            for i, h in enumerate(weak, 1):
                lines.append(f"  \\item {latex_escape(f'HW{i}: ' + h.get('statement', ''))}")
            lines.append(r"\end{enumerate}")

    if package.get("inclusion"):
        lines.append(r"\subsection{Critérios de inclusão}")
        lines.append(r"\begin{itemize}")
        for c in package["inclusion"]:
            lines.append(f"  \\item {latex_escape(c)}")
        lines.append(r"\end{itemize}")

    if package.get("exclusion"):
        lines.append(r"\subsection{Critérios de exclusão}")
        lines.append(r"\begin{itemize}")
        for c in package["exclusion"]:
            lines.append(f"  \\item {latex_escape(c)}")
        lines.append(r"\end{itemize}")

    lines.append(r"\subsection{Bases de dados e strings de busca}")
    base = review.get_base_search_session()
    if base and base.search_string:
        lines.append(r"\paragraph{String geral.}")
        lines.append(r"\begin{verbatim}")
        lines.append(base.search_string.replace("\\", "/"))
        lines.append(r"\end{verbatim}")
    for source in review.sources.order_by("name"):
        sess = review.get_source_search_session(source.id)
        sstr = sess.search_string if sess and sess.search_string else "---"
        lines.append(f"\\paragraph{{{latex_escape(source.name)}}}.")
        lines.append(r"\begin{verbatim}")
        lines.append(sstr.replace("\\", "/"))
        lines.append(r"\end{verbatim}")

    lines.append(r"\subsection{Bases selecionadas}")
    lines.append(r"\begin{itemize}")
    for source in review.sources.all():
        lines.append(f"  \\item {latex_escape(source.name)}")
    lines.append(r"\end{itemize}")
    return lines


def _latex_tabular(headers: list[str], rows: list[list[str]]) -> list[str]:
    cols = len(headers)
    colspec = "l" * cols
    out = [
        rf"\begin{{tabular}}{{{colspec}}}",
        r"\toprule",
        " & ".join(latex_escape(h) for h in headers) + r" \\",
        r"\midrule",
    ]
    for row in rows:
        cells = [latex_escape(str(c)) for c in row]
        out.append(" & ".join(cells) + r" \\")
    out.extend([r"\bottomrule", r"\end{tabular}"])
    return out


def _bib_escape(text: str) -> str:
    return (text or "").replace("{", "").replace("}", "").replace("%", "").replace("\\", "/")


def _bib_item(row: dict) -> str:
    """Entrada \\bibitem com marcador RSL:BIB para rastreio."""
    key = f"rsl_art_{row['id']}"
    author = _bib_escape(row.get("authors", "Anon.")[:200])
    title = _bib_escape(row.get("title", "Sem título")[:300])
    year = _bib_escape(str(row.get("year", "s.d.")))
    doi = row.get("doi", "").strip()
    cite = f"{author} ({year}). \\textit{{{title}}}."
    if doi:
        cite += f" DOI: {_bib_escape(doi)}."
    if row.get("source"):
        cite += f" Base: {_bib_escape(row['source'])}."
    return "\n".join([f"% RSL:BIB:{row['id']}", f"\\bibitem{{{key}}} {cite}"])


def _bibtex_entry(row: dict) -> str:
    key = f"rsl_art_{row['id']}"
    lines = [
        f"% RSL:BIB:{row['id']}",
        f"@article{{{key},",
        f"  author = {{{_bib_escape(row.get('authors', 'Anon.')[:200])}}},",
        f"  title = {{{_bib_escape(row.get('title', 'Sem título')[:500])}}},",
        f"  year = {{{_bib_escape(str(row.get('year', ''))) or 's.d.'}}},",
    ]
    if row.get("doi"):
        lines.append(f"  doi = {{{_bib_escape(row['doi'])}}},")
    if row.get("source"):
        lines.append(f"  note = {{Base de dados: {_bib_escape(row['source'])}}},")
    lines.append("}")
    return "\n".join(lines)


def build_bibtex_file(package: dict) -> str:
    parts = [f"% {MARKER_VERSION}", "% RSL:BLOCK:BIBTEX:BEGIN", ""]
    for row in package.get("dataset", []):
        parts.append(_bibtex_entry(row))
        parts.append("")
    parts.append("% RSL:BLOCK:BIBTEX:END")
    return "\n".join(parts)


def _latex_preamble(review, package: dict, style: str) -> list[str]:
    meta = package["meta"]
    title = latex_escape(review.title)
    date = latex_escape(meta.get("generated_at", ""))
    if style == "abntex2":
        return [
            r"\documentclass[12pt, openright, twoside=true, english, french, spanish, brazil]{abntex2}",
            r"\usepackage[T1]{fontenc}",
            r"\usepackage[utf8]{inputenc}",
            r"\usepackage{indentfirst}",
            r"\usepackage{graphicx}",
            r"\usepackage{booktabs}",
            r"\usepackage{longtable}",
            r"\usepackage{array}",
            r"\usepackage{hyperref}",
            r"\usepackage{caption}",
            r"\usepackage{enumitem}",
            r"\usepackage{verbatim}",
            r"\usepackage[alf]{abntex2cite}",
            r"\title{" + title + "}",
            rf"\author{{Síntese sistemática --- {RSL_SYSTEM_NAME}}}",
            r"\date{" + date + " (UTC)}",
            r"\begin{document}",
            r"\maketitle",
            r"\begin{resumo}",
            latex_escape(
                f"Síntese de {meta['n_final']} estudos (seleção final). "
                f"Funil: {package['funnel']['identificados']} identificados, "
                f"{package['funnel']['selecao_final']} sintetizados. Dados da extração estruturada."
            ),
            r"\end{resumo}",
            r"\tableofcontents",
            r"\newpage",
        ]
    return [
        r"\documentclass[12pt,a4paper,brazil]{article}",
        r"\usepackage[utf8]{inputenc}",
        r"\usepackage[T1]{fontenc}",
        r"\usepackage[brazil]{babel}",
        r"\usepackage{geometry}",
        r"\geometry{margin=2.5cm}",
        r"\usepackage{booktabs}",
        r"\usepackage{longtable}",
        r"\usepackage{array}",
        r"\usepackage{hyperref}",
        r"\usepackage{caption}",
        r"\usepackage{enumitem}",
        r"\usepackage{verbatim}",
        r"\hypersetup{colorlinks=true,linkcolor=blue,citecolor=blue}",
        r"\title{" + title + "}",
        rf"\author{{Síntese RSL --- {RSL_SYSTEM_NAME}}}",
        r"\date{Gerado em " + date + r" (UTC)}",
        r"\begin{document}",
        r"\maketitle",
        r"\begin{abstract}",
        latex_escape(
            f"Relatório de síntese com {meta['n_final']} estudo(s) na seleção final. "
            "Valores tabulares derivam da extração da RSL ativa."
        ),
        r"\end{abstract}",
        r"\tableofcontents",
        r"\newpage",
    ]


def _latex_bibliography(review, package: dict, style: str) -> list[str]:
    if style == "abntex2":
        return [
            r"\section{Referências}",
            r"\bibliographystyle{abntex2-alf}",
            r"\bibliography{rsl-referencias}",
        ]
    bib_lines = [r"\section{Referências dos estudos incluídos}", r"\begin{thebibliography}{99}"]
    for row in package.get("dataset", []):
        bib_lines.append(_bib_item(row))
    bib_lines.append(r"\end{thebibliography}")
    return bib_lines


def build_synthesis_latex(review, package: dict, *, style: str = "article") -> str:
    """Gera documento .tex completo com marcadores e dados da RSL ativa."""
    meta = package["meta"]
    funnel = package["funnel"]
    sections = package.get("sections", {})
    n_final = meta["n_final"]

    parts: list[str] = []

    parts.append(r"% !TeX program = pdflatex")
    parts.append(f"% {MARKER_VERSION}")
    parts.append(r"% Marcadores: RSL:BLOCK, RSL:SECTION, RSL:TABLE, RSL:DATA, RSL:BIB")
    parts.append("")

    metadata_lines = [
        f"% RSL:DATA:review_id={review.id}",
        f"% RSL:DATA:review_slug={review.slug}",
        f"% RSL:DATA:review_title={meta.get('title', review.title)}",
        f"% RSL:DATA:n_final={n_final}",
        f"% RSL:DATA:generated_at={meta.get('generated_at', '')}",
        f"% RSL:DATA:funnel_identificados={funnel['identificados']}",
        f"% RSL:DATA:funnel_selecao_final={funnel['selecao_final']}",
        f"% RSL:DATA:estimated_pages={meta.get('estimated_pages', 10)}",
    ]
    parts.extend(_marker_block("METADATA", metadata_lines))
    parts.append("")
    parts.extend(_latex_preamble(review, package, style))
    parts.append("")

    # Protocolo (dados primários do cadastro)
    proto_body = _build_protocol_lines(review, package)
    parts.extend(_marker_block("PROTOCOL", proto_body))
    parts.append(r"\newpage")

    for key, title in SECTION_ORDER:
        parts.append(f"\\section{{{latex_escape(title)}}}")
        body = []
        for para in _latex_paragraphs(sections.get(key, "")):
            body.append(para)
            body.append("")
        parts.extend(_marker_section(key, [f"% conteúdo narrativo ({len(body)} blocos)"]))
        parts.extend(body)

        # Tabelas vinculadas à seção
        if key == "conformidade_prisma":
            tbl = [
                r"\begin{table}[htbp]",
                r"\centering",
                r"\caption{Contagens PRISMA (exportação auditável)}",
                r"\label{tab:rsl-prisma-resumo}",
                *_latex_tabular(
                    ["Etapa", "Contagem"],
                    [
                        ["Seleção final", funnel["selecao_final"]],
                        ["Identificados", funnel["identificados"]],
                        ["Aceitos", funnel["aceitos"]],
                    ],
                ),
                r"\end{table}",
                _provenance_note(review, package, "Alinhado a PRISMA 2020; validar diagrama visual no editor."),
            ]
            parts.extend(_marker_table("prisma_resumo", tbl))
            parts.extend(tbl)

        if key == "resultados_selecao":
            if package.get("year_distribution"):
                rows = [[r["year"], r["count"], f"{r['percent']}\\%"] for r in package["year_distribution"]]
                tbl = [
                    r"\begin{table}[htbp]",
                    r"\centering",
                    r"\caption{Distribuição por ano (seleção final)}",
                    r"\label{tab:rsl-year}",
                    *_latex_tabular(["Ano", "n", "\\%"], rows),
                    r"\end{table}",
                    _provenance_note(review, package, "Ano bibliográfico dos estudos incluídos."),
                ]
                parts.extend(_marker_table("year_dist", tbl))
                parts.extend(tbl)
            if package.get("source_distribution"):
                rows = [
                    [r["source"], r["count"], f"{r['percent']}\\%"]
                    for r in package["source_distribution"]
                ]
                tbl = [
                    r"\begin{table}[htbp]",
                    r"\centering",
                    r"\caption{Distribuição por base de dados}",
                    r"\label{tab:rsl-source}",
                    *_latex_tabular(["Base", "n", "\\%"], rows),
                    r"\end{table}",
                    _provenance_note(review, package, f"Base de importação no {RSL_SYSTEM_NAME}."),
                ]
                parts.extend(_marker_table("source_dist", tbl))
                parts.extend(tbl)
            tbl = [
                r"\begin{table}[htbp]",
                r"\centering",
                r"\caption{Funil de seleção (PRISMA simplificado)}",
                r"\label{tab:rsl-prisma}",
                *_latex_tabular(
                    ["Etapa", "Contagem"],
                    [
                        ["Registros identificados", funnel["identificados"]],
                        ["Não classificados", funnel["nao_classificados"]],
                        ["Aceitos", funnel["aceitos"]],
                        ["Rejeitados", funnel["rejeitados"]],
                        ["Duplicados", funnel["duplicados"]],
                        ["Seleção final (síntese)", funnel["selecao_final"]],
                    ],
                ),
                r"\end{table}",
                r"\footnotesize",
                _provenance_note(
                    review,
                    package,
                    "Contagens obtidas do estado de triagem dos artigos importados.",
                ),
                r"\normalsize",
            ]
            parts.extend(_marker_table("prisma", tbl))
            parts.extend(tbl)

        if key == "caracterizacao" and package.get("summary_table"):
            rows = []
            for r in package["summary_table"]:
                rows.append(
                    [
                        r["authors"],
                        r["year"],
                        r["source"],
                        r["domain"],
                        r["model"],
                        r["edge"],
                        r["llm"],
                        r["qa"] if r["qa"] is not None else "---",
                    ]
                )
            long_rows = []
            for row in rows:
                long_rows.append(" & ".join(latex_escape(c) for c in row) + r" \\")
            tbl = [
                r"\begin{longtable}{@{}p{2.2cm}cc p{1.8cm} p{1.8cm}cc c@{}}",
                r"\caption{Matriz de caracterização dos estudos incluídos} \label{tab:rsl-matrix} \\",
                r"\toprule",
                r"Autores & Ano & Base & Domínio & Modelo & Edge & LLM & QA \\",
                r"\midrule",
                r"\endfirsthead",
                r"\multicolumn{8}{c}{\tablename\ \thetable\ -- continuação} \\",
                r"\toprule",
                r"Autores & Ano & Base & Domínio & Modelo & Edge & LLM & QA \\",
                r"\midrule",
                r"\endhead",
                r"\midrule",
                r"\multicolumn{8}{r}{Continua na próxima página} \\",
                r"\endfoot",
                r"\bottomrule",
                r"\endlastfoot",
                *long_rows,
                r"\end{longtable}",
                r"\footnotesize",
                _provenance_note(
                    review,
                    package,
                    "Colunas derivadas dos campos de extração Application Domain, Model Type, Edge AI Context, LLM-based e pontuação de QA.",
                ),
                r"\normalsize",
            ]
            parts.extend(_marker_table("characterization", tbl))
            parts.extend(tbl)

        if key == "sintese_tematica":
            for i, ft in enumerate(package.get("frequency_tables", [])):
                tid = f"freq_{re.sub(r'[^a-z0-9]+', '_', ft['field'].lower())[:30]}"
                rows = [[r["label"], r["count"], f"{r['percent']}\\%"] for r in ft.get("rows", [])]
                tbl = [
                    r"\begin{table}[htbp]",
                    r"\centering",
                    rf"\caption{{Distribuição --- {latex_escape(ft['field'])}}}",
                    rf"\label{{tab:{tid}}}",
                    *_latex_tabular(["Categoria", "n", "\\%"], rows),
                    r"\end{table}",
                    r"\footnotesize",
                    _provenance_note(
                        review,
                        package,
                        f"Frequência absoluta e percentual sobre n={ft.get('total_studies', n_final)}; "
                        f"campo «{ft['field']}» do formulário de extração.",
                    ),
                    r"\normalsize",
                ]
                parts.extend(_marker_table(tid, tbl))
                parts.extend(tbl)

            for ct in package.get("crosstabs", []):
                tid = f"x_{re.sub(r'[^a-z0-9]+', '_', (ct['field_a'] + ct['field_b']).lower())[:28]}"
                headers = [ct["field_a"]] + list(ct["columns"]) + ["Total"]
                xrows = []
                for row in ct.get("rows", []):
                    line = [row["row_label"]]
                    for cell in row["cells"]:
                        line.append(str(cell["count"]))
                    line.append(str(row["row_total"]))
                    xrows.append(line)
                tbl = [
                    r"\begin{table}[htbp]",
                    r"\centering",
                    rf"\caption{{Cruzamento: {latex_escape(ct['field_a'])} $\times$ {latex_escape(ct['field_b'])}}}",
                    rf"\label{{tab:{tid}}}",
                    *_latex_tabular(headers, xrows),
                    r"\end{table}",
                    r"\footnotesize",
                    _provenance_note(
                        review,
                        package,
                        "Contagens de co-ocorrência; estudos com múltipla seleção podem contribuir em mais de uma célula.",
                    ),
                    r"\normalsize",
                ]
                parts.extend(_marker_table(tid, tbl))
                parts.extend(tbl)

        if key == "lacunas_literatura" and package.get("gaps"):
            rows = [
                [g["theme"][:80], g["count"], ", ".join(g["studies"][:4])]
                for g in package["gaps"][:25]
            ]
            tbl = [
                r"\begin{table}[htbp]",
                r"\centering",
                r"\caption{Temas de lacunas (campo Research Gap)}",
                r"\label{tab:rsl-gaps}",
                *_latex_tabular(["Tema", "n estudos", "Referências"], rows),
                r"\end{table}",
                r"\footnotesize",
                _provenance_note(
                    review,
                    package,
                    "Agrupamento por similaridade textual do campo Research Gap; temas com menor n exigem leitura crítica.",
                ),
                r"\normalsize",
            ]
            parts.extend(_marker_table("gaps", tbl))
            parts.extend(tbl)

        if key == "discussao" and package.get("hypothesis_evidence"):
            rows = [
                [
                    h["hypothesis_id"],
                    h["type_label"],
                    h["statement"][:80],
                    h["n_studies"],
                ]
                for h in package["hypothesis_evidence"]
            ]
            tbl = [
                r"\begin{table}[htbp]",
                r"\centering",
                r"\caption{Evidências por hipótese (mapeamento heurístico)}",
                r"\label{tab:rsl-hipoteses}",
                *_latex_tabular(["ID", "Tipo", "Hipótese", "n estudos"], rows),
                r"\end{table}",
                r"\footnotesize",
                _provenance_note(
                    review,
                    package,
                    "HF = hipótese forte; HW = hipótese fraca; vínculo por correspondência textual na extração.",
                ),
                r"\normalsize",
            ]
            parts.extend(_marker_table("hypothesis_evidence", tbl))
            parts.extend(tbl)

        if key == "discussao" and package.get("rq_coverage"):
            rows = [
                [rq.get("rq_id", "RQ"), rq["question"][:100], rq["n_studies"], rq["note"][:60]]
                for rq in package["rq_coverage"]
            ]
            tbl = [
                r"\begin{table}[htbp]",
                r"\centering",
                r"\caption{Cobertura por pergunta de pesquisa (mapeamento heurístico)}",
                r"\label{tab:rsl-rq}",
                *_latex_tabular(["RQ", "Pergunta", "n estudos", "Observação"], rows),
                r"\end{table}",
                r"\footnotesize",
                _provenance_note(
                    review,
                    package,
                    "Mapeamento automático por correspondência de termos entre RQ e campos Research Gap, Main Contribution e Descrição; validar manualmente.",
                ),
                r"\normalsize",
            ]
            parts.extend(_marker_table("rq_coverage", tbl))
            parts.extend(tbl)

        if key == "metodos" and package.get("quality_summary", {}).get("has_checklist"):
            qs = package["quality_summary"]
            rows = [[q[:80]] for q in qs.get("questions", [])]
            if rows:
                tbl = [
                    r"\begin{table}[htbp]",
                    r"\centering",
                    r"\caption{Checklist de avaliação de qualidade (protocolo)}",
                    r"\label{tab:rsl-qa}",
                    *_latex_tabular(["Questão"], rows),
                    r"\end{table}",
                    _provenance_note(
                        review,
                        package,
                        f"Corte configurado: {qs.get('cutoff', 0)}; "
                        f"n com pontuação={qs.get('n_scored', 0)}.",
                    ),
                ]
                parts.extend(_marker_table("qa_checklist", tbl))
                parts.extend(tbl)

        if key == "lacunas_rsl":
            if package.get("methodological_lacunas"):
                parts.append(r"\subsection{Lacunas metodológicas detectadas}")
                parts.append(r"\begin{itemize}")
                for lac in package["methodological_lacunas"]:
                    parts.append(
                        f"  \\item[\\textbf{{{latex_escape(lac['tipo'])}}}] "
                        f"{latex_escape(lac['descricao'])}"
                    )
                parts.append(r"\end{itemize}")
            if package.get("fill_rates"):
                rows = [
                    [r["field"], r["filled"], r["total"], f"{r['percent']}\\%"]
                    for r in package["fill_rates"]
                ]
                tbl = [
                    r"\begin{table}[htbp]",
                    r"\centering",
                    r"\caption{Completude da extração por campo}",
                    r"\label{tab:rsl-fill}",
                    *_latex_tabular(["Campo", "Preenchidos", "Total", "\\%"], rows),
                    r"\end{table}",
                    r"\footnotesize",
                    _provenance_note(
                        review,
                        package,
                        "Taxa de preenchimento na seleção final; campos vazios limitam generalizações.",
                    ),
                    r"\normalsize",
                ]
                parts.extend(_marker_table("fill_rates", tbl))
                parts.extend(tbl)

    # Apêndice: evidências primárias por estudo
    parts.append(r"\appendix")
    parts.append(r"\section{Evidências primárias por estudo}")
    parts.append(
        latex_escape(
            "Transcrição dos campos narrativos da extração para auditoria. Cada entrada referencia o identificador interno do artigo."
        )
    )
    evidence_lines = []
    for row in package.get("dataset", []):
        aid = row["id"]
        evidence_lines.append(f"% RSL:STUDY:{aid}:BEGIN")
        evidence_lines.append(r"\subsection*{" + latex_escape(row["title"][:120]) + "}")
        evidence_lines.append(
            rf"\noindent\textbf{{ID}} {aid}; "
            rf"\textbf{{Ano}} {latex_escape(row.get('year', ''))}; "
            rf"\textbf{{DOI}} {latex_escape(row.get('doi', '') or '---')}."
        )
        for fname in ("Main Contribution", "Identified Limitations", "Research Gap", "Descrição"):
            val = row.get("fields", {}).get(fname, "")
            if isinstance(val, list):
                val = "; ".join(val)
            if val:
                evidence_lines.append(r"\paragraph{" + latex_escape(fname) + ".}")
                evidence_lines.append(latex_escape(str(val)[:2000]))
        evidence_lines.append(f"% RSL:STUDY:{aid}:END")
    parts.extend(_marker_block("PRIMARY_EVIDENCE", evidence_lines))
    parts.extend(evidence_lines)

    bib_lines = _latex_bibliography(review, package, style)
    parts.extend(_marker_block("BIBLIOGRAPHY", bib_lines))
    parts.extend(bib_lines)
    parts.append("")
    parts.append(r"\end{document}")
    return "\n".join(parts)


def export_synthesis_latex(review, package: dict, *, style: str = "article") -> BytesIO:
    content = build_synthesis_latex(review, package, style=style)
    out = BytesIO(content.encode("utf-8"))
    out.seek(0)
    return out
