"""RevMan .rm5 导出服务 (Cochrane 系统评价原生格式)"""

import xml.etree.ElementTree as ET
from xml.dom.minidom import parseString

__all__ = ["export_review_rm5"]


def _text(elem: ET.Element, tag: str, text: str | None) -> ET.Element:
    """创建带文本的子元素"""
    child = ET.SubElement(elem, tag)
    if text:
        child.text = text
    return child


def _clean(text: str | None) -> str:
    return (text or "").replace("\n", " ").replace("\r", "").strip()


def _first_author(authors: list) -> str:
    if not authors:
        return ""
    a = authors[0]
    family = a.get("family", "")
    given = a.get("given", "")
    return f"{family}, {given}" if family else given


def _pico_get(pico: dict | None, key: str) -> str:
    if not pico:
        return ""
    val = pico.get(key)
    return _clean(str(val)) if val else ""


def _build_study_tables(root: ET.Element, literatures: list[dict]) -> dict[str, str]:
    """生成 <studies> 小节和一份 studies_by_pmid 映射"""
    studies = ET.SubElement(root, "studies")
    pmid_to_id: dict[str, str] = {}

    for idx, lit in enumerate(literatures):
        pmid = str(lit.get("pmid", idx))
        sid = f"STUDY{idx + 1}"
        pmid_to_id[pmid] = sid

        study = ET.SubElement(studies, "study")
        study.set("id", sid)
        study_name = f"{_first_author(lit.get('authors', []))} {lit.get('pub_year', '')}" if lit.get("authors") else (lit.get("title", "") or pmid)[:80]
        _text(study, "study-name", study_name)
        _text(study, "study-id", f"PMID {pmid}")

        # 参考文献
        refs = ET.SubElement(study, "refs")
        ref = ET.SubElement(refs, "ref")
        _text(ref, "ref-title", _clean(lit.get("title")))
        _text(ref, "ref-journal", _clean(lit.get("journal")))
        if lit.get("pub_year"):
            _text(ref, "ref-year", str(lit.get("pub_year")))
        if lit.get("doi"):
            _text(ref, "ref-doi", lit.get("doi"))
        authors = lit.get("authors") or []
        for a in authors:
            f = a.get("family", "")
            g = a.get("given", "")
            if f or g:
                _text(ref, "ref-author", f"{f}, {g}".strip(", "))

        # 研究特征
        pico = lit.get("pico") or {}
        chrs = ET.SubElement(study, "study-characteristics")
        _text(chrs, "characteristic", f"{_pico_get(pico, 'population')}")
        _text(chrs, "intervention", f"{_pico_get(pico, 'intervention')}")
        _text(chrs, "comparison", f"{_pico_get(pico, 'comparator')}")
        _text(chrs, "outcome", f"{_pico_get(pico, 'outcome')}")
        _text(chrs, "setting", "")

        # 样本量
        ss = pico.get("sample_size")
        if ss:
            _text(study, "sample-size", str(ss))
        # 研究设计
        sd = lit.get("study_design") or {}
        if sd.get("label_zh"):
            _text(study, "study-design", sd.get("label_zh", ""))

        # 质量评分
        quality = lit.get("quality_score", "")
        if quality:
            _text(study, "quality", quality)

    return pmid_to_id


def _build_comparisons(root: ET.Element, literatures: list[dict], pmid_to_id: dict[str, str]) -> None:
    """生成 <comparisons> 小节，自动按干预措施分组"""
    included = [l for l in literatures if l.get("stage") == "included" and l.get("pico")]
    if not included:
        return

    # 按干预措施聚合
    groups: dict[str, list[dict]] = {}
    for lit in included:
        pico = lit.get("pico") or {}
        intervention = _pico_get(pico, "intervention") or "Unspecified"
        if intervention not in groups:
            groups[intervention] = []
        groups[intervention].append(lit)

    comparisons = ET.SubElement(root, "comparisons")

    for gidx, (intervention, lits) in enumerate(groups.items()):
        # 对每组，以 comparator 作为对照名
        comparator = _pico_get(lits[0].get("pico") or {}, "comparator") or "Control"

        comp = ET.SubElement(comparisons, "comparison")
        comp.set("id", f"COMP{gidx + 1}")
        _text(comp, "comp-name", f"{intervention} vs {comparator}")

        # 收集所有 outcome 进行分组
        outcome_groups: dict[str, list[dict]] = {}
        for lit in lits:
            pico = lit.get("pico") or {}
            outcome = _pico_get(pico, "outcome") or "Primary outcome"
            if outcome not in outcome_groups:
                outcome_groups[outcome] = []
            outcome_groups[outcome].append(lit)

        for oidx, (outcome_name, o_lits) in enumerate(outcome_groups.items()):
            outcome = ET.SubElement(comp, "outcome")
            outcome.set("id", f"COMP{gidx + 1}_OUT{oidx + 1}")
            _text(outcome, "outcome-name", outcome_name[:200])

            # 为每个纳入该 outcome 的研究添加数据
            for _lit in o_lits:
                pmid = str(_lit.get("pmid", ""))
                sid = pmid_to_id.get(pmid)
                if not sid:
                    continue

                pico = _lit.get("pico") or {}
                es = pico.get("effect_size") or {}
                ss = pico.get("sample_size")

                study_data = ET.SubElement(outcome, "study-data")
                _text(study_data, "study-id", sid)

                # 效应量数据
                if es or ss:
                    data_elem = ET.SubElement(study_data, "data")
                    _text(data_elem, "total", str(ss) if ss else "")

                    hr = es.get("hr")
                    or_val = es.get("or")
                    rr = es.get("rr")
                    lo = es.get("ci_lower")
                    hi = es.get("ci_upper")
                    pv = es.get("p_value")

                    if hr:
                        _text(data_elem, "hr", str(hr))
                    if or_val:
                        _text(data_elem, "or", str(or_val))
                    if rr:
                        _text(data_elem, "rr", str(rr))
                    if lo:
                        _text(data_elem, "ci-lower", str(lo))
                    if hi:
                        _text(data_elem, "ci-upper", str(hi))
                    if pv is not None:
                        _text(data_elem, "p-value", str(pv))


def export_review_rm5(review: dict, literatures: list[dict]) -> str:
    """导出系统评价为 RevMan .rm5 XML 格式。

    生成完整的 .rm5 XML 文档，包含评审元数据、纳排标准、检索策略、
    纳入研究特征（PICO）和比较/结局数据。
    """
    # 根元素
    root = ET.Element("review")
    root.set("version", "5.0.0")

    # ── 基本信息 ──
    auth = ET.SubElement(root, "auth")

    _text(auth, "revtitle", _clean(review.get("title", "")))
    _text(auth, "revshorttitle", _clean(review.get("title", ""))[:80])

    # 研究目的 / 背景
    objective = review.get("objective") or ""
    if objective:
        _text(auth, "objectives", _clean(objective))

    dates = ET.SubElement(auth, "dates")
    if review.get("created_at"):
        _text(dates, "dt", review["created_at"][:10])
        _text(dates, "dt-type", "Created")
    if review.get("updated_at"):
        _text(dates, "dt", review["updated_at"][:10])
        _text(dates, "dt-type", "Edited")

    # 检索来源
    _text(auth, "sources", "Electronic database(s)" if review.get("databases_searched") else "")
    for db in (review.get("databases_searched") or []):
        _text(auth, "source", db)

    # ── 方法学 ──
    methods = ET.SubElement(auth, "methods")

    criteria = ET.SubElement(methods, "criteria")
    inc = review.get("inclusion_criteria") or []
    exc = review.get("exclusion_criteria") or []
    criteria_lines = []
    if inc:
        criteria_lines.append("Inclusion criteria:")
        criteria_lines.extend(f"- {c}" for c in inc)
    if exc:
        criteria_lines.append("Exclusion criteria:")
        criteria_lines.extend(f"- {c}" for c in exc)
    _text(criteria, "text", "\n".join(criteria_lines))

    search_methods = ET.SubElement(methods, "search-methods")
    strategy = review.get("search_strategy") or {}
    strategy_text = strategy.get("queries") or strategy.get("pubmed") or ""
    if isinstance(strategy_text, list):
        strategy_text = "; ".join(
            q.get("query", "") for q in strategy_text if isinstance(q, dict)
        )
    _text(search_methods, "text", _clean(str(strategy_text)))

    # ── 研究数据 ──
    body = ET.SubElement(root, "body")
    pmid_to_id = _build_study_tables(body, literatures)
    _build_comparisons(body, literatures, pmid_to_id)

    # ── 序列化为格式化 XML ──
    raw = ET.tostring(root, encoding="unicode", xml_declaration=True)
    dom = parseString(raw)
    pretty = dom.toprettyxml(indent="  ", encoding="utf-8")
    return pretty.decode("utf-8")
