#!/usr/bin/env python3
"""ObesityTriangulate-Kor — CLI 진입점.

Streamlit 의존성 없이 작동. pandas/numpy/scipy/statsmodels도 optional.
"""
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

# triangulation 패키지가 같은 폴더에 있다고 가정
sys.path.insert(0, str(Path(__file__).resolve().parent))

from triangulation import ontology, grid, lawlor, mvmr, mediation, bias, designs, report

DISCLAIMER_SHORT = (
    "⚠️ 연구·교육용. 임상의사결정 금지. "
    "비만 약물·수술은 임상의와 상담."
)


def cmd_grid(args):
    g = grid.build_grid()
    print(f"# 6-design grid — {len(g)} (intervention × outcome) pairs")
    print(DISCLAIMER_SHORT)
    print()
    print(f"{'intervention':<25} {'outcome':<35} {'category':<10} {'score':>6} {'dir':>4} {'n_des':>5}")
    print("-" * 95)
    for row in g[:50]:
        print(f"{row['intervention'][:24]:<25} {row['outcome'][:34]:<35} "
              f"{row['outcome_category'][:9]:<10} "
              f"{row['score']:>6.3f} {row.get('majority_direction','?')[:4]:>4} "
              f"{row['n_designs']:>5}")


def cmd_pair(args):
    iv, oc = args.pair
    rows = grid.get_pair_rows(iv, oc)
    if not rows:
        print(f"no data for ({iv}, {oc})")
        return 1
    conc = grid.concordance_score(rows)
    law = lawlor.lawlor_score(iv, oc)
    print(f"# {iv} × {oc}")
    print(DISCLAIMER_SHORT)
    print()
    print(f"## Evidence ({len(rows)} rows)")
    for r in rows:
        print(f"  - {r['design']:<25} estimate={r['effect_estimate']:>6} "
              f"CI=[{r['ci_low']}, {r['ci_high']}]  n={r['sample_size']}  "
              f"FU={r['follow_up_years']}y  src={r['source_citation']}")
    print()
    print("## Concordance")
    print(json.dumps(conc, ensure_ascii=False, indent=2))
    print()
    print("## Lawlor 5-criterion")
    print(json.dumps(law, ensure_ascii=False, indent=2))


def cmd_mediation(args):
    iv, oc = args.mediation
    print(f"# Weight-loss mediation — {iv} × {oc}")
    print(DISCLAIMER_SHORT)
    print()
    med = mediation.mediation_for_pair(iv, oc)
    print(json.dumps(med, ensure_ascii=False, indent=2))


def cmd_discordant(args):
    d = grid.find_discordant(top_n=args.top)
    print(f"# Top {args.top} discordant pairs")
    print(DISCLAIMER_SHORT)
    print()
    for item in d:
        print(f"  {item['intervention']:<25} × {item['outcome']:<30} "
              f"agreement={item['direction_agreement']:.2f} "
              f"score={item['score']:.2f} n_des={item['n_designs']}")
        diag = bias.diagnose_discordance(item["intervention"], item["outcome"])
        if diag.get("discordant"):
            print(f"    → {diag['recommendation'][:120]}")


def cmd_mvmr(args):
    iv, oc = args.mvmr
    print(f"# MVMR — {iv} × {oc}")
    print(DISCLAIMER_SHORT)
    print()
    print(json.dumps(mvmr.mvmr_for_pair(iv, oc), ensure_ascii=False, indent=2))


def cmd_designs(args):
    iv, oc = args.designs
    print(f"# 8 design cards — {iv} × {oc}")
    print(DISCLAIMER_SHORT)
    print()
    cards = designs.recommend_designs(iv, oc)
    for i, c in enumerate(cards, 1):
        print(f"## {i}. {c['type']}")
        print(f"  rationale: {c['rationale']}")
        print(f"  primary endpoint: {c.get('primary_endpoint')}")
        print(f"  sample size: {c.get('sample_size_estimate')}")
        print(f"  follow-up: {c.get('follow_up_years')}년")
        print(f"  hypothesis: {c.get('key_hypothesis')}")
        print()


def cmd_report(args):
    out = args.report
    # report 명령은 default pair을 잡거나 args.pair 우선
    if args.pair:
        iv, oc = args.pair
    else:
        iv, oc = "semaglutide", "CV death"
    saved = report.save_report(iv, oc, out_path=out)
    print(f"saved: {saved}")


def cmd_summary(args):
    """온톨로지 + grid 통계 요약."""
    ivs = ontology.load_interventions()
    outs = ontology.load_outcomes()
    eff = ontology.load_effects()
    iarc = ontology.iarc_obesity_related_cancers()
    print("# ObesityTriangulate-Kor — summary")
    print(DISCLAIMER_SHORT)
    print()
    print(f"interventions: {len(ivs)}")
    print(f"outcomes: {len(outs)}  (IARC obesity-related cancer: {len(iarc)})")
    print(f"effect rows: {len(eff)}")
    print(f"design types: {len(set(r['design'] for r in eff))}")
    print(f"unique pairs: {len(set((r['intervention'], r['outcome']) for r in eff))}")


def main():
    p = argparse.ArgumentParser(
        prog="obesity-triangulate",
        description="항비만 intervention 인과추론 triangulation CLI"
    )
    p.add_argument("--grid", action="store_true", help="6-design grid 출력")
    p.add_argument("--pair", nargs=2, metavar=("INTERVENTION", "OUTCOME"),
                   help="단일 (intervention, outcome) 분석")
    p.add_argument("--mediation", nargs=2, metavar=("INTERVENTION", "OUTCOME"),
                   help="weight-loss mediation 분리")
    p.add_argument("--mvmr", nargs=2, metavar=("INTERVENTION", "OUTCOME"),
                   help="multivariable MR (BMI/WHR/body-fat-pct)")
    p.add_argument("--designs", nargs=2, metavar=("INTERVENTION", "OUTCOME"),
                   help="8 design 카드 추천")
    p.add_argument("--discordant", action="store_true", help="discordant pair top-N")
    p.add_argument("--top", type=int, default=10)
    p.add_argument("--report", metavar="OUT_PATH",
                   help="통합 리포트 저장 (.json / .md / .docx)")
    p.add_argument("--summary", action="store_true", help="온톨로지 요약")
    args = p.parse_args()

    if args.summary:
        cmd_summary(args); return 0
    if args.grid:
        cmd_grid(args); return 0
    if args.discordant:
        cmd_discordant(args); return 0
    if args.mediation:
        cmd_mediation(args); return 0
    if args.mvmr:
        cmd_mvmr(args); return 0
    if args.designs:
        cmd_designs(args); return 0
    if args.report:
        cmd_report(args); return 0
    if args.pair:
        cmd_pair(args); return 0

    p.print_help()
    return 0


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