"""
FrailDMDeprescribe-Kor
프레일디엠디프리스크라이브코어 — 노인 당뇨 코호트 모니터링 대시보드

도메인: DM (노인 당뇨 + frailty + deprescribing)
카테고리: 웹기반 대시보드 (Streamlit)
실행: streamlit run app.py

⚠️ 의학 디스클레이머
   본 대시보드는 연구·교육·quality improvement 보조용이며,
   실제 임상 의사결정은 담당 의사 판단 하에 이루어져야 합니다.
   합성 데이터(생성 스크립트: data/generate_synthetic.py)로 동작하며,
   실제 환자 데이터를 포함하지 않습니다.
"""
from __future__ import annotations

import io
import json
import os
from datetime import datetime
from typing import Optional

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st

APP_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_PATH = os.path.join(APP_DIR, "data", "synthetic_frail_dm_cohort.csv")
GUIDELINES_PATH = os.path.join(APP_DIR, "assets", "guidelines.json")

DISCLAIMER = (
    "본 대시보드는 연구·교육·quality improvement 보조용이며, "
    "실제 임상 의사결정은 담당 의사 판단 하에 이루어져야 합니다. "
    "표시되는 데이터는 합성 시뮬레이션 데이터입니다."
)

PHENO_LABEL_KO = {
    "robust": "건강 (robust)",
    "pre_frail": "전노쇠 (pre-frail)",
    "frail": "노쇠 (frail)",
}
PHENO_ORDER = ["robust", "pre_frail", "frail"]
PHENO_COLOR = {"robust": "#2ca02c", "pre_frail": "#ff7f0e", "frail": "#d62728"}

STAGE_LABEL_KO = {
    "not_applicable": "비대상",
    "candidate": "후보",
    "recommended": "권고완료",
    "tapering": "감량중",
    "completed": "감량완료",
}
STAGE_ORDER = ["candidate", "recommended", "tapering", "completed"]


# ---------------------------------------------------------------------------
# 데이터 로딩
# ---------------------------------------------------------------------------
@st.cache_data(show_spinner=False)
def load_cohort(csv_path: str) -> pd.DataFrame:
    df = pd.read_csv(csv_path)
    # 빈 lower bound는 NaN으로
    df["hba1c_target_lower"] = pd.to_numeric(df["hba1c_target_lower"], errors="coerce")
    df["hba1c_target_upper"] = pd.to_numeric(df["hba1c_target_upper"], errors="coerce")
    df["enroll_date"] = pd.to_datetime(df["enroll_date"], errors="coerce")
    return df


@st.cache_data(show_spinner=False)
def load_guidelines(json_path: str) -> dict:
    with open(json_path, "r", encoding="utf-8") as f:
        return json.load(f)


def filter_cohort(df: pd.DataFrame, phenos, age_range, su_only: bool, insulin_only: bool, overtreated_only: bool) -> pd.DataFrame:
    out = df.copy()
    if phenos:
        out = out[out["phenotype"].isin(phenos)]
    out = out[(out["age"] >= age_range[0]) & (out["age"] <= age_range[1])]
    if su_only:
        out = out[out["uses_sulfonylurea"] == 1]
    if insulin_only:
        out = out[out["uses_insulin"] == 1]
    if overtreated_only:
        out = out[out["overtreated_baseline"] == 1]
    return out


# ---------------------------------------------------------------------------
# 핵심기능 1: 노쇠 다층 phenotype 패널
# ---------------------------------------------------------------------------
def render_frailty_panel(df: pd.DataFrame) -> None:
    st.header("1. 노쇠 다층 phenotype 패널")
    st.caption("K-FRAIL · CFS · K-MNA · ADL/IADL · MMSE-K · polypharmacy 통합 보기")

    if df.empty:
        st.info("필터 조건에 맞는 환자가 없습니다.")
        return

    col1, col2, col3, col4 = st.columns(4)
    col1.metric("총 환자 수", f"{len(df):,}")
    col2.metric("Robust", f"{(df['phenotype']=='robust').sum():,}",
                f"{(df['phenotype']=='robust').mean()*100:.1f}%")
    col3.metric("Pre-frail", f"{(df['phenotype']=='pre_frail').sum():,}",
                f"{(df['phenotype']=='pre_frail').mean()*100:.1f}%")
    col4.metric("Frail", f"{(df['phenotype']=='frail').sum():,}",
                f"{(df['phenotype']=='frail').mean()*100:.1f}%")

    # Phenotype 분포 도넛
    pheno_counts = df["phenotype"].value_counts().reindex(PHENO_ORDER).fillna(0).reset_index()
    pheno_counts.columns = ["phenotype", "count"]
    pheno_counts["label"] = pheno_counts["phenotype"].map(PHENO_LABEL_KO)
    fig_donut = px.pie(pheno_counts, names="label", values="count", hole=0.5,
                       color="phenotype", color_discrete_map=PHENO_COLOR,
                       title="Phenotype 분포")
    st.plotly_chart(fig_donut, use_container_width=True)

    # Radar: phenotype 별 핵심 지표 평균 (정규화)
    metrics = {
        "K-FRAIL total": ("k_frail_total", 5, False),
        "CFS": ("cfs", 9, False),
        "K-MNA(역)": ("k_mna_short", 14, True),   # 높을수록 좋음 → 역으로
        "ADL(역)": ("adl_katz", 6, True),
        "IADL(역)": ("iadl_lawton", 8, True),
        "Polypharmacy": ("polypharmacy_n", 15, False),
        "MMSE-K(역)": ("mmse_k", 30, True),
    }
    rows = []
    for pheno in PHENO_ORDER:
        sub = df[df["phenotype"] == pheno]
        if sub.empty:
            continue
        for label, (col, denom, invert) in metrics.items():
            val = sub[col].mean() / denom
            if invert:
                val = 1 - val
            rows.append({"phenotype": PHENO_LABEL_KO[pheno], "metric": label, "value": round(val, 3)})
    radar_df = pd.DataFrame(rows)
    if not radar_df.empty:
        fig_radar = go.Figure()
        for pheno_kor in radar_df["phenotype"].unique():
            sub = radar_df[radar_df["phenotype"] == pheno_kor]
            fig_radar.add_trace(go.Scatterpolar(
                r=sub["value"].tolist() + [sub["value"].iloc[0]],
                theta=sub["metric"].tolist() + [sub["metric"].iloc[0]],
                fill="toself",
                name=pheno_kor,
            ))
        fig_radar.update_layout(
            polar=dict(radialaxis=dict(range=[0, 1], visible=True)),
            title="Phenotype별 노쇠 지표 프로파일 (0=최선, 1=최악)",
            showlegend=True,
        )
        st.plotly_chart(fig_radar, use_container_width=True)

    # Polypharmacy 분포
    fig_poly = px.box(df, x="phenotype", y="polypharmacy_n", points="outliers",
                      color="phenotype", color_discrete_map=PHENO_COLOR,
                      category_orders={"phenotype": PHENO_ORDER},
                      labels={"polypharmacy_n": "복용 약물 수", "phenotype": "Phenotype"},
                      title="Phenotype별 다약제 부담")
    st.plotly_chart(fig_poly, use_container_width=True)

    with st.expander("환자별 상세 (상위 50명)"):
        cols = ["patient_id", "age", "sex", "phenotype", "k_frail_total", "cfs",
                "k_mna_short", "adl_katz", "iadl_lawton", "mmse_k", "polypharmacy_n"]
        st.dataframe(df[cols].head(50), use_container_width=True)


# ---------------------------------------------------------------------------
# 핵심기능 2: KDA 노인당뇨 target 완화 부합률
# ---------------------------------------------------------------------------
def render_kda_target(df: pd.DataFrame, guidelines: dict) -> None:
    st.header("2. KDA 노인당뇨 HbA1c target 완화 부합률")
    st.caption("KDA 2023 / ADA 2025 Older Adults 기준 — phenotype별 권고 band 대비 실제 HbA1c 분포")

    if df.empty:
        st.info("필터 조건에 맞는 환자가 없습니다.")
        return

    # Band별 분류
    def classify(row):
        lo = row["hba1c_target_lower"]
        hi = row["hba1c_target_upper"]
        v = row["hba1c_baseline_pct"]
        if pd.isna(lo):
            return "내부(EoL)" if v < hi else "초과"
        if v < lo:
            return "Overtreatment (lower 미만)"
        if v > hi:
            return "Undertreatment (upper 초과)"
        return "On target"

    df_cls = df.assign(target_class=df.apply(classify, axis=1))
    cls_counts = df_cls["target_class"].value_counts().reset_index()
    cls_counts.columns = ["분류", "환자수"]
    cls_counts["비율(%)"] = (cls_counts["환자수"] / cls_counts["환자수"].sum() * 100).round(1)
    st.subheader("Target band 부합 분류")
    st.dataframe(cls_counts, use_container_width=True)

    # Phenotype별 부합률 stacked bar
    pivot = (df_cls.groupby(["phenotype", "target_class"]).size().reset_index(name="n"))
    fig_stack = px.bar(pivot, x="phenotype", y="n", color="target_class",
                       category_orders={"phenotype": PHENO_ORDER},
                       title="Phenotype × target band 분류",
                       labels={"n": "환자 수", "phenotype": "Phenotype"})
    st.plotly_chart(fig_stack, use_container_width=True)

    # HbA1c 분포 (band 표시)
    st.subheader("HbA1c 분포 (phenotype별)")
    fig_hist = px.box(df_cls, x="phenotype", y="hba1c_baseline_pct", color="phenotype",
                      color_discrete_map=PHENO_COLOR,
                      category_orders={"phenotype": PHENO_ORDER},
                      labels={"hba1c_baseline_pct": "Baseline HbA1c (%)", "phenotype": "Phenotype"},
                      points="outliers")
    # 가이드라인 band 라인 표시
    for pheno, band in zip(["robust", "pre_frail", "frail"],
                            [(6.5, 7.5), (7.5, 8.0), (7.5, 8.5)]):
        fig_hist.add_hline(y=band[0], line=dict(dash="dash", color=PHENO_COLOR[pheno]),
                           annotation_text=f"{pheno} lower {band[0]}%",
                           annotation_position="top left")
        fig_hist.add_hline(y=band[1], line=dict(dash="dot", color=PHENO_COLOR[pheno]),
                           annotation_text=f"{pheno} upper {band[1]}%",
                           annotation_position="bottom right")
    st.plotly_chart(fig_hist, use_container_width=True)

    overtreated_n = int((df_cls["target_class"] == "Overtreatment (lower 미만)").sum())
    on_target_n = int((df_cls["target_class"] == "On target").sum())
    total = len(df_cls)
    c1, c2, c3 = st.columns(3)
    c1.metric("Overtreatment", f"{overtreated_n}", f"{overtreated_n/total*100:.1f}%")
    c2.metric("On target", f"{on_target_n}", f"{on_target_n/total*100:.1f}%")
    c3.metric("Undertreatment + EoL 초과",
              f"{total - overtreated_n - on_target_n}",
              f"{(total - overtreated_n - on_target_n)/total*100:.1f}%")

    with st.expander("KDA 권고 (출처)"):
        kda = guidelines["kda_elderly_dm_2023"]
        st.markdown(f"**{kda['title_ko']}**")
        st.caption(kda["source"])
        for tg in kda["hba1c_targets"]:
            st.markdown(f"- **{tg['group_ko']}** — 목표 {tg['target_pct']}% — {tg['note_ko']}")


# ---------------------------------------------------------------------------
# 핵심기능 3: SU·insulin deprescribing 워크플로
# ---------------------------------------------------------------------------
def render_deprescribing(df: pd.DataFrame, guidelines: dict) -> None:
    st.header("3. SU·인슐린 deprescribing 워크플로")
    st.caption("후보 식별 → 권고 → tapering → 완료 단계별 추적")

    if df.empty:
        st.info("필터 조건에 맞는 환자가 없습니다.")
        return

    cand = df[df["deprescribe_candidate"] == 1]
    st.metric("Deprescribing 후보 (SU/insulin 사용 + overtreatment 또는 frail)", f"{len(cand)}",
              f"{len(cand)/len(df)*100:.1f}% of cohort")

    # 단계별 funnel
    stage_counts = cand["deprescribe_stage"].value_counts().reindex(STAGE_ORDER).fillna(0).reset_index()
    stage_counts.columns = ["stage", "n"]
    stage_counts["라벨"] = stage_counts["stage"].map(STAGE_LABEL_KO)
    fig_funnel = go.Figure(go.Funnel(
        y=stage_counts["라벨"],
        x=stage_counts["n"],
        textinfo="value+percent initial",
    ))
    fig_funnel.update_layout(title="Deprescribing 진행 단계 funnel")
    st.plotly_chart(fig_funnel, use_container_width=True)

    # 약물별 분포
    su_dist = cand[cand["uses_sulfonylurea"] == 1]["sulfonylurea_drug"].value_counts().reset_index()
    su_dist.columns = ["sulfonylurea", "n"]
    ins_dist = cand[cand["uses_insulin"] == 1]["insulin_drug"].value_counts().reset_index()
    ins_dist.columns = ["insulin", "n"]
    c1, c2 = st.columns(2)
    with c1:
        st.subheader("SU 약물 분포 (후보)")
        st.dataframe(su_dist, use_container_width=True)
    with c2:
        st.subheader("Insulin 약물 분포 (후보)")
        st.dataframe(ins_dist, use_container_width=True)

    # Phenotype × 단계 heatmap
    pivot = cand.pivot_table(index="phenotype", columns="deprescribe_stage",
                             values="patient_id", aggfunc="count", fill_value=0)
    pivot = pivot.reindex(index=PHENO_ORDER, columns=STAGE_ORDER, fill_value=0)
    pivot.columns = [STAGE_LABEL_KO.get(c, c) for c in pivot.columns]
    pivot.index = [PHENO_LABEL_KO.get(i, i) for i in pivot.index]
    fig_heat = px.imshow(pivot.values, x=pivot.columns, y=pivot.index,
                         text_auto=True, aspect="auto",
                         color_continuous_scale="RdYlGn_r",
                         title="Phenotype × deprescribing 단계 (환자 수)")
    st.plotly_chart(fig_heat, use_container_width=True)

    with st.expander("Deprescribing 원칙 (KDA / AGS Beers / STOPP)"):
        st.markdown("**KDA 2023**")
        for p in guidelines["kda_elderly_dm_2023"]["deprescribing_principles_ko"]:
            st.markdown(f"- {p}")
        st.markdown("**AGS Beers 2023 (DM 관련)**")
        for p in guidelines["ags_beers_2023"]["dm_relevant_ko"]:
            st.markdown(f"- {p}")
        st.markdown("**STOPP/START v3 (2023)**")
        for p in guidelines["stopp_start_v3"]["dm_stop_ko"]:
            st.markdown(f"- STOP — {p}")
        for p in guidelines["stopp_start_v3"]["dm_start_ko"]:
            st.markdown(f"- START — {p}")

    with st.expander("후보 환자 리스트 (상위 50명)"):
        cols = ["patient_id", "age", "phenotype", "hba1c_baseline_pct",
                "hba1c_target_lower", "hba1c_target_upper",
                "sulfonylurea_drug", "insulin_drug", "deprescribe_stage"]
        st.dataframe(cand[cols].head(50), use_container_width=True)


# ---------------------------------------------------------------------------
# 핵심기능 4: 저혈당·낙상·재입원 outcome 추적
# ---------------------------------------------------------------------------
def _km_estimate(times: np.ndarray, events: np.ndarray) -> pd.DataFrame:
    """간이 Kaplan-Meier estimator (lifelines 미설치 시 fallback)."""
    order = np.argsort(times)
    times = times[order]
    events = events[order]
    unique_times = np.unique(times)
    n_at_risk = len(times)
    survival = 1.0
    rows = [{"time": 0, "survival": 1.0, "n_at_risk": n_at_risk}]
    for t in unique_times:
        d = int(((times == t) & (events == 1)).sum())
        c = int(((times == t) & (events == 0)).sum())
        if n_at_risk > 0 and d > 0:
            survival *= 1 - d / n_at_risk
        rows.append({"time": int(t), "survival": survival, "n_at_risk": n_at_risk})
        n_at_risk -= (d + c)
    return pd.DataFrame(rows)


def render_outcomes(df: pd.DataFrame) -> None:
    st.header("4. Outcome 추적 (저혈당·낙상·재입원·사망)")
    st.caption("Deprescribing 전후, phenotype별 12개월 outcome 비교")

    if df.empty:
        st.info("필터 조건에 맞는 환자가 없습니다.")
        return

    # Outcome 요약 표
    outcomes = ["severe_hypo_ed_12mo", "fall_12mo", "fracture_12mo",
                "readmission_12mo", "death_12mo"]
    labels = ["중증 저혈당 ED", "낙상", "골절", "재입원", "사망"]

    summary_rows = []
    for pheno in PHENO_ORDER:
        sub = df[df["phenotype"] == pheno]
        if sub.empty:
            continue
        row = {"phenotype": PHENO_LABEL_KO[pheno], "n": len(sub)}
        for oc, lab in zip(outcomes, labels):
            row[lab] = f"{sub[oc].sum()} ({sub[oc].mean()*100:.1f}%)"
        summary_rows.append(row)
    st.subheader("Phenotype별 12개월 outcome")
    st.dataframe(pd.DataFrame(summary_rows), use_container_width=True)

    # Deprescribing 전후 비교 (HbA1c, 저혈당, 낙상)
    cand = df[df["deprescribe_candidate"] == 1]
    if not cand.empty:
        st.subheader("Deprescribing 전후 비교 (후보 환자)")
        progressed = cand[cand["deprescribe_stage"].isin(["tapering", "completed"])]
        not_progressed = cand[cand["deprescribe_stage"].isin(["candidate", "recommended"])]

        comp = pd.DataFrame({
            "지표": ["환자 수", "Baseline HbA1c (mean)", "Follow-up HbA1c (mean)",
                   "ΔHbA1c", "중증 저혈당 ED (%)", "낙상 (%)", "재입원 (%)"],
            "감량 진행/완료": [
                len(progressed),
                f"{progressed['hba1c_baseline_pct'].mean():.2f}" if not progressed.empty else "-",
                f"{progressed['hba1c_followup_pct'].mean():.2f}" if not progressed.empty else "-",
                f"{(progressed['hba1c_followup_pct'].mean() - progressed['hba1c_baseline_pct'].mean()):+.2f}" if not progressed.empty else "-",
                f"{progressed['severe_hypo_ed_12mo'].mean()*100:.1f}" if not progressed.empty else "-",
                f"{progressed['fall_12mo'].mean()*100:.1f}" if not progressed.empty else "-",
                f"{progressed['readmission_12mo'].mean()*100:.1f}" if not progressed.empty else "-",
            ],
            "감량 미진행": [
                len(not_progressed),
                f"{not_progressed['hba1c_baseline_pct'].mean():.2f}" if not not_progressed.empty else "-",
                f"{not_progressed['hba1c_followup_pct'].mean():.2f}" if not not_progressed.empty else "-",
                f"{(not_progressed['hba1c_followup_pct'].mean() - not_progressed['hba1c_baseline_pct'].mean()):+.2f}" if not not_progressed.empty else "-",
                f"{not_progressed['severe_hypo_ed_12mo'].mean()*100:.1f}" if not not_progressed.empty else "-",
                f"{not_progressed['fall_12mo'].mean()*100:.1f}" if not not_progressed.empty else "-",
                f"{not_progressed['readmission_12mo'].mean()*100:.1f}" if not not_progressed.empty else "-",
            ],
        })
        st.dataframe(comp, use_container_width=True)

    # KM-style: 저혈당 ED-free survival by phenotype (간이)
    st.subheader("저혈당 ED-free survival (간이 KM)")
    try:
        from lifelines import KaplanMeierFitter  # type: ignore
        kmf = KaplanMeierFitter()
        fig_km = go.Figure()
        for pheno in PHENO_ORDER:
            sub = df[df["phenotype"] == pheno]
            if sub.empty:
                continue
            kmf.fit(sub["time_to_event_days"], sub["severe_hypo_ed_12mo"], label=PHENO_LABEL_KO[pheno])
            sf = kmf.survival_function_.reset_index()
            sf.columns = ["time", "survival"]
            fig_km.add_trace(go.Scatter(x=sf["time"], y=sf["survival"], mode="lines",
                                         name=PHENO_LABEL_KO[pheno],
                                         line=dict(color=PHENO_COLOR[pheno])))
        fig_km.update_layout(xaxis_title="일", yaxis_title="저혈당 ED-free 비율",
                             yaxis=dict(range=[0, 1.05]),
                             title="저혈당 ED 발생-free 곡선 (lifelines)")
        st.plotly_chart(fig_km, use_container_width=True)
    except Exception:
        # fallback
        fig_km = go.Figure()
        for pheno in PHENO_ORDER:
            sub = df[df["phenotype"] == pheno]
            if sub.empty:
                continue
            km = _km_estimate(sub["time_to_event_days"].to_numpy(),
                              sub["severe_hypo_ed_12mo"].to_numpy())
            fig_km.add_trace(go.Scatter(x=km["time"], y=km["survival"], mode="lines",
                                         name=PHENO_LABEL_KO[pheno],
                                         line=dict(color=PHENO_COLOR[pheno], shape="hv")))
        fig_km.update_layout(xaxis_title="일", yaxis_title="저혈당 ED-free 비율",
                             yaxis=dict(range=[0, 1.05]),
                             title="저혈당 ED 발생-free 곡선 (내장 KM)")
        st.plotly_chart(fig_km, use_container_width=True)


# ---------------------------------------------------------------------------
# 핵심기능 5: 가이드 호환 리포트 export (docx)
# ---------------------------------------------------------------------------
def build_docx_report(df: pd.DataFrame, guidelines: dict) -> Optional[bytes]:
    try:
        from docx import Document  # type: ignore
    except Exception:
        return None

    doc = Document()
    doc.add_heading("FrailDMDeprescribe-Kor — 노인 당뇨 코호트 QI 리포트", level=1)
    doc.add_paragraph(f"생성일: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
    doc.add_paragraph(DISCLAIMER)

    doc.add_heading("1. 코호트 개요", level=2)
    doc.add_paragraph(f"총 환자 수: {len(df)}")
    pcounts = df["phenotype"].value_counts().to_dict()
    for p in PHENO_ORDER:
        doc.add_paragraph(f" - {PHENO_LABEL_KO[p]}: {pcounts.get(p,0)} "
                           f"({pcounts.get(p,0)/max(len(df),1)*100:.1f}%)",
                           style="List Bullet")

    doc.add_heading("2. KDA target 부합", level=2)
    ot = int(df["overtreated_baseline"].sum())
    doc.add_paragraph(f"Overtreatment(목표 lower 미만) 환자: {ot} ({ot/max(len(df),1)*100:.1f}%)")

    doc.add_heading("3. Deprescribing", level=2)
    cand = df[df["deprescribe_candidate"] == 1]
    doc.add_paragraph(f"후보 환자: {len(cand)}")
    for s in STAGE_ORDER:
        n = int((cand["deprescribe_stage"] == s).sum())
        doc.add_paragraph(f" - {STAGE_LABEL_KO[s]}: {n}", style="List Bullet")

    doc.add_heading("4. 12개월 outcome", level=2)
    for col, lab in [("severe_hypo_ed_12mo", "중증 저혈당 ED"),
                     ("fall_12mo", "낙상"),
                     ("fracture_12mo", "골절"),
                     ("readmission_12mo", "재입원"),
                     ("death_12mo", "사망")]:
        doc.add_paragraph(f" - {lab}: {int(df[col].sum())} "
                           f"({df[col].mean()*100:.1f}%)",
                           style="List Bullet")

    doc.add_heading("5. 인용 가이드라인", level=2)
    doc.add_paragraph(guidelines["kda_elderly_dm_2023"]["source"])
    doc.add_paragraph(guidelines["ada_2025_older_adults"]["source"])
    doc.add_paragraph(guidelines["ags_beers_2023"]["source"])
    doc.add_paragraph(guidelines["stopp_start_v3"]["source"])
    doc.add_paragraph(guidelines["kafm_polypharmacy"]["source"])

    doc.add_paragraph("")
    doc.add_paragraph(DISCLAIMER)

    buf = io.BytesIO()
    doc.save(buf)
    return buf.getvalue()


def build_text_report(df: pd.DataFrame, guidelines: dict) -> str:
    lines = []
    lines.append("FrailDMDeprescribe-Kor — 노인 당뇨 코호트 QI 리포트")
    lines.append(f"생성일: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
    lines.append("")
    lines.append(DISCLAIMER)
    lines.append("")
    lines.append("[1. 코호트 개요]")
    lines.append(f"총 환자 수: {len(df)}")
    pcounts = df["phenotype"].value_counts().to_dict()
    for p in PHENO_ORDER:
        lines.append(f"  - {PHENO_LABEL_KO[p]}: {pcounts.get(p,0)} "
                     f"({pcounts.get(p,0)/max(len(df),1)*100:.1f}%)")
    lines.append("")
    lines.append("[2. KDA target 부합]")
    ot = int(df["overtreated_baseline"].sum())
    lines.append(f"Overtreatment(목표 lower 미만): {ot} ({ot/max(len(df),1)*100:.1f}%)")
    lines.append("")
    lines.append("[3. Deprescribing]")
    cand = df[df["deprescribe_candidate"] == 1]
    lines.append(f"후보 환자: {len(cand)}")
    for s in STAGE_ORDER:
        n = int((cand["deprescribe_stage"] == s).sum())
        lines.append(f"  - {STAGE_LABEL_KO[s]}: {n}")
    lines.append("")
    lines.append("[4. 12개월 outcome]")
    for col, lab in [("severe_hypo_ed_12mo", "중증 저혈당 ED"),
                     ("fall_12mo", "낙상"),
                     ("fracture_12mo", "골절"),
                     ("readmission_12mo", "재입원"),
                     ("death_12mo", "사망")]:
        lines.append(f"  - {lab}: {int(df[col].sum())} ({df[col].mean()*100:.1f}%)")
    lines.append("")
    lines.append("[5. 인용 가이드라인]")
    lines.append(guidelines["kda_elderly_dm_2023"]["source"])
    lines.append(guidelines["ada_2025_older_adults"]["source"])
    lines.append(guidelines["ags_beers_2023"]["source"])
    lines.append(guidelines["stopp_start_v3"]["source"])
    lines.append(guidelines["kafm_polypharmacy"]["source"])
    lines.append("")
    lines.append(DISCLAIMER)
    return "\n".join(lines)


def render_report(df: pd.DataFrame, guidelines: dict) -> None:
    st.header("5. 리포트 export (KDA·KGS·KAFM 호환)")
    st.caption("외래 QI 리포트 다운로드 (docx / txt)")

    docx_bytes = build_docx_report(df, guidelines)
    txt = build_text_report(df, guidelines)

    if docx_bytes:
        st.download_button(
            label="📄 Word(.docx) 리포트 다운로드",
            data=docx_bytes,
            file_name=f"frail_dm_qi_report_{datetime.now().strftime('%Y%m%d_%H%M')}.docx",
            mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        )
    else:
        st.info("python-docx 미설치 → docx 대신 텍스트 리포트만 제공합니다.")

    st.download_button(
        label="📝 텍스트 리포트 다운로드",
        data=txt.encode("utf-8"),
        file_name=f"frail_dm_qi_report_{datetime.now().strftime('%Y%m%d_%H%M')}.txt",
        mime="text/plain",
    )

    with st.expander("리포트 미리보기 (txt)"):
        st.code(txt)


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
    st.set_page_config(page_title="FrailDMDeprescribe-Kor", layout="wide")
    st.title("프레일디엠디프리스크라이브코어 (FrailDMDeprescribe-Kor)")
    st.markdown("**노인 당뇨 코호트 노쇠 · KDA target · deprescribing · outcome 통합 대시보드**")
    st.warning(DISCLAIMER)

    # 데이터 로드
    if not os.path.exists(DATA_PATH):
        st.error(f"데이터 파일이 없습니다: {DATA_PATH}\n"
                 f"먼저 `python3 data/generate_synthetic.py`로 합성 데이터를 생성하세요.")
        return
    df = load_cohort(DATA_PATH)
    guidelines = load_guidelines(GUIDELINES_PATH)

    # 사이드바 필터
    with st.sidebar:
        st.header("필터")
        phenos = st.multiselect("Phenotype",
                                 options=PHENO_ORDER,
                                 default=PHENO_ORDER,
                                 format_func=lambda x: PHENO_LABEL_KO[x])
        age_min, age_max = int(df["age"].min()), int(df["age"].max())
        age_range = st.slider("연령 범위", min_value=age_min, max_value=age_max,
                               value=(age_min, age_max))
        su_only = st.checkbox("SU 사용자만")
        insulin_only = st.checkbox("Insulin 사용자만")
        overtreated_only = st.checkbox("Overtreatment(lower 미만) 환자만")

        st.markdown("---")
        st.caption("출처: KDA 2023 · ADA 2025 · AGS Beers 2023 · STOPP/START v3 · KAFM/KGS")
        st.caption(f"코호트: 합성 n={len(df)}")

    filtered = filter_cohort(df, phenos, age_range, su_only, insulin_only, overtreated_only)

    tabs = st.tabs([
        "1. 노쇠 phenotype",
        "2. KDA target",
        "3. Deprescribing",
        "4. Outcome",
        "5. 리포트",
    ])
    with tabs[0]:
        render_frailty_panel(filtered)
    with tabs[1]:
        render_kda_target(filtered, guidelines)
    with tabs[2]:
        render_deprescribing(filtered, guidelines)
    with tabs[3]:
        render_outcomes(filtered)
    with tabs[4]:
        render_report(filtered, guidelines)


if __name__ == "__main__":
    main()
