#!/usr/bin/env python3
"""SarcopeniaMuscleTrack — CLI entrypoint.

End-to-end demo:
    python3 main.py --demo --report-dir ./output \
        --decompose tirzepatide+bimagrumab --language ko --openclaw-export

Real cohort:
    python3 main.py --cohort-dir ./mycohort --report-dir ./output \
        --decompose tirzepatide+bimagrumab --openclaw-export

NOTE: 동물실험 데이터 분석용 — 임상 의사결정 직접 사용 금지.
"""
from __future__ import annotations

import argparse
import json
import os
import sys
from typing import Dict

import pandas as pd

from lib import loaders, trajectory, decompose as dec, report
from lib import demo_data


FINAL_WEEK = 24


def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(
        prog="sarcopenia-muscle-track",
        description=(
            "Sarcopenic obesity 동물 cohort 분석: baseline-locked %change trajectory + "
            "lean-mass-normalized 기능 + myofiber type composition + muscle-sparing decompose + "
            "한국어 보고서 + OpenClaw schema export."
        ),
    )
    p.add_argument("--demo", action="store_true", help="Run end-to-end with synthetic cohort")
    p.add_argument("--cohort-dir", type=str, default=None, help="Cohort raw csv dir")
    p.add_argument("--report-dir", type=str, default="./output", help="Output dir")
    p.add_argument(
        "--decompose",
        type=str,
        default=None,
        help="Combo arm to decompose, e.g. tirzepatide+bimagrumab",
    )
    p.add_argument(
        "--openclaw-export",
        action="store_true",
        help="Emit Parquet/JSON export for OpenClaw 비만/근감소 약물 재조합 DB",
    )
    p.add_argument(
        "--language",
        choices=["ko", "en"],
        default="ko",
        help="Report language (default: ko)",
    )
    p.add_argument("--seed", type=int, default=42, help="Demo data seed")
    p.add_argument("--final-week", type=int, default=FINAL_WEEK, help="Endpoint week")
    return p.parse_args()


def run(args: argparse.Namespace) -> int:
    os.makedirs(args.report_dir, exist_ok=True)

    # 1. Resolve cohort_dir
    if args.demo:
        cohort_dir = os.path.join(args.report_dir, "demo_cohort")
        print(f"[demo] writing synthetic cohort -> {cohort_dir}")
        demo_data.write_demo(cohort_dir, seed=args.seed)
    else:
        if not args.cohort_dir:
            print("ERROR: --cohort-dir required when --demo not set", file=sys.stderr)
            return 2
        cohort_dir = args.cohort_dir

    # 2. Load + integrity
    print(f"[load] cohort dir = {cohort_dir}")
    modalities = loaders.load_cohort(cohort_dir)
    issues = loaders.integrity_check(modalities)
    if issues:
        print("[integrity] warnings:")
        for i in issues:
            print(f"  - {i}")
    else:
        print("[integrity] clean")

    meta = modalities["cohort_meta"]
    bw = modalities["body_weight"]
    comp = modalities["body_composition"]
    grip = modalities["grip_strength"]
    treadmill = modalities["treadmill"]
    fiber = modalities["myofiber_hcs"]
    csa_micro = modalities["microct_muscle"]
    myokine = modalities["myokine"]

    print(
        f"[cohort] n={meta.shape[0]}  arms={sorted(meta['treatment'].unique())}  "
        f"models={sorted(meta['model'].unique())}"
    )

    # 3. Baseline lock + % change
    print("[trajectory] baseline lock + % change")
    bw_lock = trajectory.baseline_lock(bw, "bw_g")
    lean_lock = trajectory.baseline_lock(comp, "lean_mass_g")
    fat_lock = trajectory.baseline_lock(comp, "fat_mass_g")
    grip_lock = trajectory.baseline_lock(grip, "force_g")
    grip_per_lean_df = trajectory.grip_per_lean(grip, comp)
    gpl_lock = trajectory.baseline_lock(grip_per_lean_df, "force_per_g_lean")

    # 4. Composite endpoint
    print("[composite] sarcopenic obesity composite endpoint")
    ce_df = trajectory.composite_endpoints(
        bw, comp, grip_per_lean_df, meta, final_week=args.final_week
    )
    ce_summary = trajectory.composite_pass_rate(ce_df)
    print(ce_summary.to_string(index=False))

    # 5. Save tables
    tbl_dir = os.path.join(args.report_dir, "tables")
    os.makedirs(tbl_dir, exist_ok=True)
    ce_df.to_csv(os.path.join(tbl_dir, "composite_per_mouse.csv"), index=False)
    ce_summary.to_csv(os.path.join(tbl_dir, "composite_pass_rate.csv"), index=False)

    # 6. Build figures
    figures = []
    figures.append(
        report.fig_pct_change_spaghetti(
            bw_lock, "pct_change_bw_g", "Body weight % change", "BW Δ% (vs W0)", meta
        )
    )
    figures.append(
        report.fig_pct_change_spaghetti(
            lean_lock,
            "pct_change_lean_mass_g",
            "Lean mass % change",
            "Lean Δ% (vs W0)",
            meta,
        )
    )
    figures.append(
        report.fig_pct_change_spaghetti(
            fat_lock,
            "pct_change_fat_mass_g",
            "Fat mass % change",
            "Fat Δ% (vs W0)",
            meta,
        )
    )
    figures.append(
        report.fig_pct_change_spaghetti(
            grip_lock,
            "pct_change_force_g",
            "Grip strength (absolute) % change",
            "Grip Δ% (vs W0)",
            meta,
        )
    )
    figures.append(
        report.fig_pct_change_spaghetti(
            gpl_lock,
            "pct_change_force_per_g_lean",
            "Grip per gram lean mass % change",
            "Grip/lean Δ% (vs W0)",
            meta,
        )
    )
    figures.append(report.fig_composite_pass(ce_summary))

    # 7. Decompose
    decompose_result = None
    fiber_pattern_result = None
    if args.decompose:
        try:
            mono_arm, _ = dec.parse_combo(args.decompose)
            print(f"[decompose] mono={mono_arm}  combo={args.decompose}")
            decompose_result = dec.decompose_muscle_sparing(
                bw, comp, meta, glp_arm=mono_arm, combo_arm=args.decompose,
                final_week=args.final_week,
            )
            if "error" not in decompose_result:
                # save per-mouse decompose table
                decompose_result["per_mouse"].to_csv(  # type: ignore
                    os.path.join(tbl_dir, "decompose_per_mouse.csv"), index=False
                )
                with open(os.path.join(tbl_dir, "decompose_summary.json"), "w") as f:
                    j = {
                        k: v for k, v in decompose_result.items()
                        if k != "per_mouse"
                    }
                    json.dump(j, f, indent=2, default=float)
                figures.append(report.fig_decompose(decompose_result))
                print(
                    f"[decompose] mean sparing = {decompose_result['mean_sparing_pct']:+.2f}pp  "
                    f"median MSI = {decompose_result['median_msi']:+.2f}"
                )
            else:
                print(f"[decompose] WARN: {decompose_result['error']}")

            fiber_pattern_result = dec.fiber_protection_pattern(
                fiber, meta, arm=args.decompose, final_week=args.final_week
            )
            if "error" not in fiber_pattern_result:
                figures.append(report.fig_fiber_pattern(fiber_pattern_result))
                print(
                    f"[fiber] arm={fiber_pattern_result['arm']}  "
                    f"fast-twitch dominant={fiber_pattern_result['fast_twitch_dominant']}  "
                    f"pattern={fiber_pattern_result['ordered_pattern']}"
                )
        except Exception as e:
            print(f"[decompose] error: {e}")

    # 8. Write report
    print(f"[report] writing PDF/PNG -> {args.report_dir}")
    pdf_path = report.write_report(
        out_dir=args.report_dir,
        cohort_id=str(meta["cohort_id"].iloc[0]) if "cohort_id" in meta.columns else "demo_cohort",
        meta=meta,
        figures=figures,
        ce_summary=ce_summary,
        decompose=decompose_result,
        fiber_pattern=fiber_pattern_result,
        language=args.language,
    )
    print(f"[report] {pdf_path}")

    # 9. OpenClaw export
    if args.openclaw_export or args.demo:
        print("[openclaw] exporting standard schema")
        path = report.openclaw_export(
            out_dir=args.report_dir,
            meta=meta,
            ce_df=ce_df,
            decompose=decompose_result,
            fiber_pattern=fiber_pattern_result,
        )
        print(f"[openclaw] {path}")

    print("[done] all outputs written.")
    return 0


def main() -> int:
    args = parse_args()
    try:
        return run(args)
    except Exception as e:
        print(f"FATAL: {e}", file=sys.stderr)
        import traceback
        traceback.print_exc()
        return 1


if __name__ == "__main__":
    raise SystemExit(main())
