#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MASLDOrganoCrosstalk - 매슬드오르가노크로스토크
=================================================
PubMed/Europe PMC + bioRxiv/medRxiv + ClinicalTrials.gov + WHO ICTRP +
STRING-DB/Reactome/KEGG/BioGRID(Mock) MASLD multi-organ mining 도구

- 4-tuple ontology: organ × organ × mediator × outcome
- 미탐색 cell ranking
- MASLD 약물 cross-organ mechanism mapping
- AASLD/EASL/APASL/KASL/NICE 가이드라인 cross-link
- 한국어 hypothesis card / protocol skeleton / grant·IIT proposal generator
- MASLDStager staging logic 확장 export (JSON)
- Yggdrasil 지식그래프 export (JSON-LD mock)

⚠️ 의학적 디스클레이머
이 도구는 연구 아이디어 생성을 위한 참고용·연구용 자동화 보조 도구입니다.
임상 진료, 진단, 처방 결정에 직접 사용하지 마십시오. mock literature 기반이며
외부 데이터베이스에 직접 연결하지 않습니다. 실제 활용 시 PubMed/Europe PMC 등
원천 데이터로 재검증하고, 임상의 판단과 IRB 승인을 거쳐 사용해야 합니다.
"""

from __future__ import annotations

import argparse
import datetime as dt
import itertools
import json
import os
import sys
from collections import Counter, defaultdict
from typing import Any, Dict, List, Tuple

DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")

DISCLAIMER = (
    "⚠️ 디스클레이머: 이 출력은 mock 합성 데이터 기반 연구 아이디어 보조용입니다. "
    "임상 진료에 직접 사용하지 마십시오. 실제 활용 시 PubMed/Europe PMC 원천 재검증 필수."
)


# ---------------------------------------------------------------------------
# data loading
# ---------------------------------------------------------------------------
def _load(name: str) -> Dict[str, Any]:
    path = os.path.join(DATA_DIR, name)
    try:
        with open(path, "r", encoding="utf-8") as f:
            return json.load(f)
    except FileNotFoundError:
        print(f"[WARN] data file missing: {path}", file=sys.stderr)
        return {}
    except json.JSONDecodeError as e:
        print(f"[ERR] invalid json {path}: {e}", file=sys.stderr)
        return {}


def load_all() -> Dict[str, Any]:
    return {
        "organs": _load("organs.json").get("organs", []),
        "mediators": _load("mediators.json").get("mediators", []),
        "outcomes": _load("outcomes.json").get("outcomes", []),
        "drugs": _load("drugs.json").get("drugs", []),
        "corpus": _load("corpus.json").get("papers", []),
        "interactome": _load("interactome.json").get("interactions", []),
        "korean_cohorts": _load("korean_cohorts.json").get("korean_cohorts", []),
        "guidelines": _load("guidelines.json").get("guidelines", []),
    }


# ---------------------------------------------------------------------------
# entity normalization (mock)
# ---------------------------------------------------------------------------
NORMALIZER_DICT = {
    "MeSH": "MASLD/D065626 NAFLD/D065626 NASH/D065626 Adipokine/D061566",
    "UMLS": "FGF21/C1452540 GLP-1/C0061355 IL-17A/C2986542",
    "DrugBank": "resmetirom/DB16669 semaglutide/DB13928 tirzepatide/DB15171",
    "HGNC": "FGF21/HGNC:3678 GLP1R/HGNC:4324 ADIPOR1/HGNC:24042",
    "CHEBI": "TMAO/CHEBI:15724 LPS/CHEBI:16412 Butyrate/CHEBI:17968",
    "UBERON": "liver/UBERON:0002107 gut/UBERON:0000160 BAT/UBERON:0002046",
    "OMIM": "PCOS/184700 NASH/613282",
    "SNOMED": "MASLD/442191002 MASH/1303680006",
}


# ---------------------------------------------------------------------------
# 4-tuple ontology
# ---------------------------------------------------------------------------
def build_ontology(data: Dict[str, Any]) -> List[Dict[str, Any]]:
    """Generate organ×organ×mediator×outcome 4-tuples and score them."""
    organs = [o["id"] for o in data["organs"]]
    mediators = [m["id"] for m in data["mediators"]]
    outcomes = [o["id"] for o in data["outcomes"]]
    corpus = data["corpus"]

    # published evidence count per (o1,o2,med,outcome)
    pub_count: Counter = Counter()
    quality_sum: Dict[Tuple[str, str, str, str], float] = defaultdict(float)
    for p in corpus:
        op = p.get("organ_pair", [])
        if len(op) != 2:
            continue
        a, b = sorted(op)
        key = (a, b, p.get("mediator", ""), p.get("outcome", ""))
        pub_count[key] += 1
        quality_sum[key] += float(p.get("study_quality", 0.0))

    tuples = []
    # restrict combinatorial explosion: only mediators and outcomes that ever appear in corpus
    relevant_med = {p["mediator"] for p in corpus if p.get("mediator")}
    relevant_out = {p["outcome"] for p in corpus if p.get("outcome")}
    # fallback: include all if subset too small
    if len(relevant_med) < 5:
        relevant_med = set(mediators)
    if len(relevant_out) < 5:
        relevant_out = set(outcomes)

    organ_pairs = list(itertools.combinations(sorted(organs), 2))

    for (o1, o2) in organ_pairs:
        for med in relevant_med:
            for out in relevant_out:
                key = (o1, o2, med, out)
                pubs = pub_count.get(key, 0)
                qsum = quality_sum.get(key, 0.0)
                expected = 2.0  # baseline expected publications for any tuple
                novelty = max(0.0, 1.0 - (pubs / expected))
                clinical_impact = _clinical_impact_score(out)
                korean_feasibility = _korean_feasibility(o1, o2, out, data)
                iit_fit = _iit_fit(med, out)
                priority = round(
                    novelty * clinical_impact * korean_feasibility * iit_fit, 4
                )
                if priority <= 0:
                    continue
                tuples.append(
                    {
                        "organ_a": o1,
                        "organ_b": o2,
                        "mediator": med,
                        "outcome": out,
                        "published": pubs,
                        "quality_sum": round(qsum, 2),
                        "novelty": round(novelty, 3),
                        "clinical_impact": clinical_impact,
                        "korean_feasibility": korean_feasibility,
                        "iit_fit": iit_fit,
                        "priority": priority,
                    }
                )
    tuples.sort(key=lambda x: x["priority"], reverse=True)
    return tuples


def _clinical_impact_score(outcome_id: str) -> float:
    high = {
        "MACE_3point",
        "HFpEF_admission",
        "all_cause_mortality",
        "liver_related_mortality",
        "cardiovascular_mortality",
        "fibrosis_F3",
        "cirrhosis_F4",
        "HCC",
        "DKD_progression",
        "stroke_ischemic",
    }
    mid = {
        "MASH_NAS",
        "fibrosis_F2",
        "MAFLD_resolution",
        "fibrosis_regression_1stage",
        "T2DM_incident",
        "AF_incident",
        "weight_10pct_loss",
        "albuminuria_progression",
        "extrahepatic_cancer",
    }
    if outcome_id in high:
        return 0.95
    if outcome_id in mid:
        return 0.75
    return 0.5


def _korean_feasibility(o1: str, o2: str, outcome_id: str, data: Dict[str, Any]) -> float:
    cohorts = data.get("korean_cohorts", [])
    if not cohorts:
        return 0.6
    # average feasibility weighted by relevance to liver
    if "liver" in (o1, o2):
        scores = [c["feasibility_score"] for c in cohorts if c.get("biopsy")]
        return round(sum(scores) / max(len(scores), 1), 3) if scores else 0.7
    # outcomes requiring biopsy/MRI raise threshold
    needs_biopsy = outcome_id in {"MASH_NAS", "fibrosis_F2", "fibrosis_F3", "cirrhosis_F4"}
    pool = [c["feasibility_score"] for c in cohorts if (c.get("biopsy") or not needs_biopsy)]
    return round(sum(pool) / max(len(pool), 1), 3) if pool else 0.6


def _iit_fit(mediator_id: str, outcome_id: str) -> float:
    # rough fit: mediators with measurable serum/plasma assays in Korea + outcome reachable in 12-24mo
    serum_friendly = {
        "FGF21",
        "FGF19",
        "GDF15",
        "adiponectin",
        "leptin",
        "GLP1",
        "irisin",
        "SHBG",
        "BNP",
        "klotho",
        "fetuinA",
        "lipocalin2",
        "FGF23",
        "osteocalcin",
    }
    short_outcome = {
        "ALT_normalization",
        "MRI_PDFF_30reduction",
        "FIB4",
        "ELF",
        "Pro_C3",
        "weight_5pct_loss",
        "weight_10pct_loss",
        "HbA1c_reduction",
        "MAFLD_resolution",
        "QoL_CLDQNAFLD",
    }
    base = 0.6
    if mediator_id in serum_friendly:
        base += 0.2
    if outcome_id in short_outcome:
        base += 0.15
    return round(min(base, 0.95), 3)


# ---------------------------------------------------------------------------
# rendering helpers
# ---------------------------------------------------------------------------
def _name_lookup(items: List[Dict[str, Any]], key: str = "id", field: str = "name_ko") -> Dict[str, str]:
    return {it[key]: it.get(field, it[key]) for it in items}


def render_top(data: Dict[str, Any], tuples: List[Dict[str, Any]], n: int) -> str:
    org_n = _name_lookup(data["organs"])
    med_n = _name_lookup(data["mediators"])
    out_n = _name_lookup(data["outcomes"])

    lines = [f"\n=== 미탐색 4-tuple Top {n} (organ × organ × mediator × outcome) ===\n"]
    header = f"{'순위':<4}{'장기A':<10}{'장기B':<10}{'매개체':<14}{'결과':<22}{'pub':<5}{'novelty':<8}{'priority':<9}"
    lines.append(header)
    lines.append("-" * len(header))
    for i, t in enumerate(tuples[:n], 1):
        lines.append(
            f"{i:<4}{org_n.get(t['organ_a'], t['organ_a']):<10}"
            f"{org_n.get(t['organ_b'], t['organ_b']):<10}"
            f"{med_n.get(t['mediator'], t['mediator']):<14}"
            f"{out_n.get(t['outcome'], t['outcome']):<22}"
            f"{t['published']:<5}{t['novelty']:<8}{t['priority']:<9}"
        )
    return "\n".join(lines)


def render_organ_pair(data: Dict[str, Any], tuples: List[Dict[str, Any]], o1: str, o2: str) -> str:
    a, b = sorted([o1, o2])
    matched = [t for t in tuples if t["organ_a"] == a and t["organ_b"] == b]
    if not matched:
        return f"[INFO] organ pair {a}-{b}에 매칭되는 4-tuple 없음."
    org_n = _name_lookup(data["organs"])
    med_n = _name_lookup(data["mediators"])
    out_n = _name_lookup(data["outcomes"])
    lines = [
        f"\n=== organ pair: {org_n.get(a, a)} ↔ {org_n.get(b, b)} 미탐색 매개체-결과 갭 ===\n"
    ]
    for t in matched[:25]:
        lines.append(
            f"- 매개체 {med_n.get(t['mediator'], t['mediator'])} → "
            f"결과 {out_n.get(t['outcome'], t['outcome'])}: "
            f"published={t['published']} novelty={t['novelty']} "
            f"priority={t['priority']} (Korean fit={t['korean_feasibility']}, IIT fit={t['iit_fit']})"
        )
    return "\n".join(lines)


def render_drug(data: Dict[str, Any], drug_id: str) -> str:
    drugs = data["drugs"]
    org_n = _name_lookup(data["organs"])
    drug = next((d for d in drugs if d["id"].lower() == drug_id.lower()
                 or d.get("name_ko") == drug_id), None)
    if not drug:
        names = ", ".join(d["id"] for d in drugs)
        return f"[INFO] drug not found: {drug_id}\n사용 가능: {names}"
    cross = ", ".join(org_n.get(o, o) for o in drug.get("cross_organ", []))
    lines = [
        f"\n=== {drug['name_ko']} ({drug['id']}) cross-organ mechanism mapping ===",
        f"클래스: {drug.get('class')}",
        f"주 작용 장기: {org_n.get(drug.get('primary_organ', ''), drug.get('primary_organ', ''))}",
        f"교차 작용 장기: {cross}",
        f"기전: {drug.get('mechanism')}",
        "가이드라인 cross-link:",
    ]
    # naive cross-link: for THR-beta or FGF21/GLP-1 list AASLD/EASL/KASL
    for g in data["guidelines"]:
        if any(kp_kw in (drug.get("class", "") + drug.get("mechanism", ""))
               for kp_kw in ["TR-β", "TR-beta", "FGF21", "FGF19", "GLP-1", "GIP", "FXR", "FASN", "PPAR"]):
            lines.append(f"  · {g['id']} ({g['name']})")
            break
    return "\n".join(lines)


# ---------------------------------------------------------------------------
# hypothesis card / protocol / proposal
# ---------------------------------------------------------------------------
def render_card(data: Dict[str, Any], tuples: List[Dict[str, Any]]) -> str:
    if not tuples:
        return "[ERR] 4-tuple ontology가 비어있어 hypothesis card 생성 불가."
    org_n = _name_lookup(data["organs"])
    med_n = _name_lookup(data["mediators"])
    out_n = _name_lookup(data["outcomes"])
    today = dt.date.today().isoformat()
    cards = []
    for i, t in enumerate(tuples[:5], 1):
        cards.append(
            f"""
[Hypothesis Card #{i}]  ({today})
가설: {org_n.get(t['organ_a'])}-{org_n.get(t['organ_b'])} 축에서
        매개체 {med_n.get(t['mediator'])} 변화가
        {out_n.get(t['outcome'])}와 독립적으로 연관된다.
근거 갭: 기존 출판 {t['published']}건 (낮음), novelty={t['novelty']}.
한국 적합도: {t['korean_feasibility']} / IIT 적합도: {t['iit_fit']}.
임상 영향도: {t['clinical_impact']}.
검증 가능 가설: {med_n.get(t['mediator'])} 4분위 상위군이 하위군 대비
        {out_n.get(t['outcome'])} 위험 OR/HR 1.3~2.0배 (가정).
권고 디자인: 한국 다기관 prospective cohort + serum biomarker + MRI-PDFF/MRE.
""".strip()
        )
    return "\n\n".join(["=== 한국어 Hypothesis Card (Top 5) ==="] + cards)


def render_protocol(data: Dict[str, Any], tuples: List[Dict[str, Any]]) -> str:
    if not tuples:
        return "[ERR] tuples empty"
    t = tuples[0]
    org_n = _name_lookup(data["organs"])
    med_n = _name_lookup(data["mediators"])
    out_n = _name_lookup(data["outcomes"])
    return f"""
=== Protocol Skeleton (STROBE-RCT/SPIRIT/ARRIVE/PRISMA-ScR 호환) ===

연구명 (KOR): {org_n.get(t['organ_a'])}-{org_n.get(t['organ_b'])} 매개체 {med_n.get(t['mediator'])}와
              {out_n.get(t['outcome'])} 연관성 한국 다기관 코호트 연구
연구명 (ENG): Multicenter Korean cohort study on {t['mediator']} in {t['organ_a']}-{t['organ_b']} crosstalk
              and {t['outcome']} in MASLD

[STROBE / SPIRIT 항목]
1. 배경/근거: 미탐색 4-tuple priority={t['priority']}, novelty={t['novelty']}
2. 목적: 일차/이차 outcome 사전 정의
3. 연구설계: prospective multicenter cohort (KASL multicenter MASLD basic-translation 그룹)
4. 모집단: MASLD 진단 기준(MetS≥1) 충족 18~75세 성인
5. 노출: 혈청 {t['mediator']} 4분위
6. 비교: 1분위(reference)
7. Outcome: {out_n.get(t['outcome'])}
8. Sample size: 효과크기 OR 1.5, α=0.05, 1-β=0.8 → ~1100명
9. 변수: 인구학·동반질환·약물·NIT(FIB-4/ELF/MRE/MRI-PDFF)·혈청 biomarker
10. 통계: cox PH/로지스틱 + DAG 기반 confounder 보정 + sensitivity analysis
11. 윤리: 각 기관 IRB 승인, 데이터 K-CURE 표준 ETL

[ARRIVE 항목 (preclinical 보조)] (해당 시)
- 동물모델: C57BL/6J HFD/CDAA 또는 humanized FRG
- 군 배정: 무작위 + blinding
- 통계: ARRIVE 2.0 준수

[PRISMA-ScR 항목 (사전 scoping review 시)]
- 검색 DB: PubMed·Europe PMC·bioRxiv·medRxiv·CTgov·WHO ICTRP
- 검색기간: 최근 10년
- 독립평가자 2명 + 의견불일치 시 3rd 합의
""".strip()


def render_proposal(data: Dict[str, Any], tuples: List[Dict[str, Any]]) -> str:
    if not tuples:
        return "[ERR] tuples empty"
    t = tuples[0]
    org_n = _name_lookup(data["organs"])
    med_n = _name_lookup(data["mediators"])
    out_n = _name_lookup(data["outcomes"])
    return f"""
=== 한국어 IIT/Grant Proposal Abstract (KHIDI/NRF/NIH NIDDK/AASLD Sheila Sherlock/EASL JI/기업 IIT 호환) ===

연구과제명: MASLD {org_n.get(t['organ_a'])}-{org_n.get(t['organ_b'])} 축
            {med_n.get(t['mediator'])} 매개체 기반 {out_n.get(t['outcome'])} 예측·중재 다기관 연구

배경: MASLD는 대사질환 다장기 crosstalk의 핵심이며, 본 4-tuple은 published evidence
       {t['published']}건으로 미탐색 영역(novelty {t['novelty']})에 해당한다.
가설: {med_n.get(t['mediator'])}의 변화는 한국인 MASLD 환자에서 {out_n.get(t['outcome'])}의
       독립 예측인자이며, 기존 NIT(FIB-4/ELF/MRE)와 보완적 가치를 갖는다.
방법: KASL 다기관 MASLD basic-translation 그룹·서울대·세브란스·보라매·순천향대 부천병원
       MASLD registry 통합 prospective cohort (n≈1,100). 혈청 {t['mediator']}와 NIT/MRI-PDFF
       동시 측정 후 1·2·3년 추적.
기대성과: (1) 한국인 특이 기준치 도출, (2) MASLD 임상 가이드라인(KASL/AASLD/EASL/APASL)
          반영 근거 마련, (3) MASLDStager staging logic 확장.
연관 자원: KoGES/NHIS-HEALS/K-CURE 연계 분석 가능. KDA/KSSO/KAGE 협력.
예산 (예시): 인건비 60% / 장비·시약 25% / 통계·관리 10% / 출판·확산 5%.
일정: 12개월 준비 + 36개월 모집·추적 + 6개월 분석/보고.
파급효과: MASLD 진료 가이드라인 한국형 데이터 보강, MASLDStager 임상 활용 가속.
""".strip()


# ---------------------------------------------------------------------------
# exports
# ---------------------------------------------------------------------------
def export_stager(data: Dict[str, Any], tuples: List[Dict[str, Any]], out_path: str) -> str:
    payload = {
        "schema_version": "MASLDStager-extension-v0.1",
        "generated_at": dt.datetime.now().isoformat(timespec="seconds"),
        "extension_proposals": [
            {
                "axis": f"{t['organ_a']}↔{t['organ_b']}",
                "mediator": t["mediator"],
                "outcome": t["outcome"],
                "novelty": t["novelty"],
                "priority": t["priority"],
                "korean_feasibility": t["korean_feasibility"],
                "suggested_thresholds": {
                    "Q1": "<25th percentile",
                    "Q4": ">75th percentile",
                    "high_risk_flag": "Q4 + FIB-4>2.67",
                },
                "staging_hint": (
                    "MASLDStager에 추가 cell로 통합 시 "
                    f"{t['organ_a']}↔{t['organ_b']} 축의 {t['mediator']} 4분위를 "
                    "추가 변수로 입력, F2-F3 진행 위험 보정에 사용"
                ),
            }
            for t in tuples[:30]
        ],
        "disclaimer": DISCLAIMER,
    }
    with open(out_path, "w", encoding="utf-8") as f:
        json.dump(payload, f, ensure_ascii=False, indent=2)
    return out_path


def export_yggdrasil(data: Dict[str, Any], tuples: List[Dict[str, Any]], out_path: str) -> str:
    """Mock JSON-LD export compatible with Yggdrasil knowledge graph."""
    nodes = []
    edges = []
    seen_nodes = set()

    def add_node(node_id: str, label: str, ntype: str) -> None:
        if node_id in seen_nodes:
            return
        seen_nodes.add(node_id)
        nodes.append({"@id": f"yggdrasil:{node_id}", "label": label, "type": ntype})

    for o in data["organs"]:
        add_node(f"organ/{o['id']}", o["name_ko"], "Organ")
    for m in data["mediators"]:
        add_node(f"mediator/{m['id']}", m["name_ko"], "Mediator")
    for o in data["outcomes"]:
        add_node(f"outcome/{o['id']}", o["name_ko"], "Outcome")
    for d in data["drugs"]:
        add_node(f"drug/{d['id']}", d["name_ko"], "Drug")

    for t in tuples[:200]:
        eid = f"tuple/{t['organ_a']}_{t['organ_b']}_{t['mediator']}_{t['outcome']}"
        edges.append(
            {
                "@id": f"yggdrasil:{eid}",
                "type": "MASLD_4tuple_hypothesis",
                "source_a": f"yggdrasil:organ/{t['organ_a']}",
                "source_b": f"yggdrasil:organ/{t['organ_b']}",
                "via_mediator": f"yggdrasil:mediator/{t['mediator']}",
                "to_outcome": f"yggdrasil:outcome/{t['outcome']}",
                "priority": t["priority"],
                "novelty": t["novelty"],
                "published": t["published"],
            }
        )

    payload = {
        "@context": {
            "yggdrasil": "https://example.org/yggdrasil/schema#",
            "label": "rdfs:label",
            "type": "@type",
        },
        "@graph": nodes + edges,
        "metadata": {
            "source": "MASLDOrganoCrosstalk",
            "generated_at": dt.datetime.now().isoformat(timespec="seconds"),
            "compatible_with": "Yggdrasil knowledge graph (mock JSON-LD)",
            "disclaimer": DISCLAIMER,
        },
    }
    with open(out_path, "w", encoding="utf-8") as f:
        json.dump(payload, f, ensure_ascii=False, indent=2)
    return out_path


# ---------------------------------------------------------------------------
# CLI
# ---------------------------------------------------------------------------
def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(
        prog="masld-organo-crosstalk",
        description=(
            "MASLDOrganoCrosstalk - MASLD 다장기 crosstalk 4-tuple ontology 마이닝 + "
            "한국어 hypothesis/protocol/proposal generator + Yggdrasil/MASLDStager export"
        ),
        epilog=DISCLAIMER,
    )
    p.add_argument("--top", type=int, default=0, metavar="N",
                   help="미탐색 4-tuple Top N 출력")
    p.add_argument("--organ-pair", nargs=2, metavar=("ORGAN_A", "ORGAN_B"),
                   help="특정 organ pair에 대한 매개체-결과 gap 표시 "
                        "(예: liver gut)")
    p.add_argument("--drug", metavar="DRUG",
                   help="특정 약물 cross-organ mechanism 매핑 (예: resmetirom)")
    p.add_argument("--card", action="store_true",
                   help="한국어 hypothesis card (top 5) 출력")
    p.add_argument("--protocol", action="store_true",
                   help="STROBE-RCT/SPIRIT/ARRIVE/PRISMA-ScR protocol skeleton 출력")
    p.add_argument("--proposal", action="store_true",
                   help="KHIDI/NRF/NIH NIDDK/AASLD/EASL/IIT 호환 한국어 proposal 출력")
    p.add_argument("--export-stager", metavar="PATH", nargs="?",
                   const="./masldstager_extension.json",
                   help="MASLDStager staging logic 확장 제안 JSON export")
    p.add_argument("--export-yggdrasil", metavar="PATH", nargs="?",
                   const="./yggdrasil_masld_kg.jsonld",
                   help="Yggdrasil 지식그래프 JSON-LD export")
    p.add_argument("--list-cohorts", action="store_true",
                   help="한국 코호트 목록 + suitability 출력")
    p.add_argument("--list-guidelines", action="store_true",
                   help="MASLD 가이드라인 목록 출력")
    p.add_argument("--no-disclaimer", action="store_true",
                   help="(개발자용) 디스클레이머 배너 출력 생략")
    return p


def main(argv: List[str] | None = None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)

    data = load_all()
    if not data["organs"] or not data["mediators"] or not data["outcomes"]:
        print("[ERR] data 디렉토리 초기화 실패. data/*.json 확인.", file=sys.stderr)
        return 2

    tuples = build_ontology(data)

    any_action = False

    if not args.no_disclaimer:
        print(DISCLAIMER)
        print()

    if args.top and args.top > 0:
        print(render_top(data, tuples, args.top))
        any_action = True

    if args.organ_pair:
        a, b = args.organ_pair
        print(render_organ_pair(data, tuples, a, b))
        any_action = True

    if args.drug:
        print(render_drug(data, args.drug))
        any_action = True

    if args.card:
        print(render_card(data, tuples))
        any_action = True

    if args.protocol:
        print(render_protocol(data, tuples))
        any_action = True

    if args.proposal:
        print(render_proposal(data, tuples))
        any_action = True

    if args.export_stager:
        path = export_stager(data, tuples, args.export_stager)
        print(f"[OK] MASLDStager extension exported → {path}")
        any_action = True

    if args.export_yggdrasil:
        path = export_yggdrasil(data, tuples, args.export_yggdrasil)
        print(f"[OK] Yggdrasil JSON-LD exported → {path}")
        any_action = True

    if args.list_cohorts:
        print("\n=== 한국 MASLD 코호트 (suitability) ===")
        for c in data["korean_cohorts"]:
            print(
                f"- {c['name_ko']} (n≈{c['size_n']}, biopsy={c['biopsy']}, "
                f"MRI-PDFF={c['MRI_PDFF']}, MRE={c['MRE']}, "
                f"feasibility={c['feasibility_score']}, host={c['host_org']})"
            )
        any_action = True

    if args.list_guidelines:
        print("\n=== MASLD 가이드라인 ===")
        for g in data["guidelines"]:
            print(f"- {g['id']}: {g['name']} (regions={g['regions']})")
            for kp in g["key_points"]:
                print(f"   · {kp}")
        any_action = True

    if not any_action:
        parser.print_help()
        print("\n[INFO] 옵션이 지정되지 않아 도움말을 출력했습니다.")
        return 0

    return 0


if __name__ == "__main__":
    sys.exit(main())
