# -*- coding: utf-8 -*-
"""
ObesityRecruitFeas-Kor — Streamlit UI
======================================

항비만 임상연구 모집 feasibility 계산기의 웹 UI.

실행:
    streamlit run app.py

Streamlit 미설치 시:
    python3 main.py --demo   (CLI 오프라인 시연)

면책: 참고용·연구용 도구. 실제 임상시험 feasibility는 CRO/생물통계
전문가 검토 필수.
"""

import sys

try:
    import streamlit as st
except ImportError:
    sys.stderr.write(
        "[오류] streamlit 이 설치되어 있지 않습니다.\n"
        "  설치: pip install streamlit\n"
        "  또는 CLI 사용: python3 main.py --demo\n"
    )
    sys.exit(1)

import pandas as pd
import core


st.set_page_config(page_title="ObesityRecruitFeas-Kor",
                   page_icon=":bar_chart:", layout="wide")


@st.cache_data
def _load():
    return core.load_microdata()


def main():
    st.title("ObesityRecruitFeas-Kor")
    st.caption("항비만 임상연구 모집 feasibility 계산기 · 합성 KNHANES microdata 기반")

    st.warning(core.DISCLAIMER)
    st.info("주의: 본 도구의 적격 모집단 추정치는 **합성 데이터**(실제 KNHANES "
            "아님 — 공개 유병률 추세 기반)에서 산출됩니다.")

    try:
        df = _load()
    except (FileNotFoundError, ValueError) as e:
        st.error(f"microdata 로드 실패: {e}")
        st.stop()

    # ----------------------------------------------------------------
    # 사이드바: 적격성 기준 빌더 + timeline 설정
    # ----------------------------------------------------------------
    with st.sidebar:
        st.header("1. 적격성 기준 빌더")
        st.subheader("포함 기준")
        bmi_min, bmi_max = st.slider("BMI 범위 (kg/m^2)", 15.0, 47.0,
                                     (27.0, 45.0), step=0.5)
        age_min, age_max = st.slider("연령 범위 (세)", 19, 80, (19, 70))
        waist_min = st.number_input("허리둘레 하한 (cm, 0=미적용)",
                                    min_value=0.0, max_value=140.0,
                                    value=0.0, step=1.0)

        st.markdown("**동반질환 요구**")
        comorb_opts = {
            "t2dm": "제2형 당뇨병",
            "htn": "고혈압",
            "dyslipidemia": "이상지질혈증",
            "masld": "MASLD(대용지표)",
            "osa": "OSA 고위험",
        }
        comorbidities = st.multiselect(
            "동반질환 후보", list(comorb_opts.keys()),
            format_func=lambda k: comorb_opts[k])
        comorb_count = st.number_input(
            "후보 중 요구 보유 개수", min_value=0,
            max_value=max(1, len(comorbidities)),
            value=min(1, len(comorbidities)) if comorbidities else 0,
            step=1)

        st.subheader("제외 기준")
        exclude_t2dm = st.checkbox("T2DM 보유자 제외", value=False)
        exclude_glp1ra = st.checkbox("최근 GLP-1RA 사용자 제외", value=True)
        exclude_smoker = st.checkbox("현재 흡연자 제외", value=False)
        exclude_severe_htn = st.checkbox("중증 고혈압 제외(SBP>=160/DBP>=100)",
                                         value=True)
        exclude_high_alt = st.checkbox("ALT>3xULN(>120 U/L) 제외", value=True)

        st.header("2. 모집 timeline 설정")
        target_n = st.number_input("목표 등록 환자 수", min_value=10,
                                   max_value=5000, value=200, step=10)
        n_sites = st.number_input("참여 사이트 수", min_value=1,
                                  max_value=200, value=10, step=1)
        screening_per_site = st.number_input(
            "사이트당 월간 스크리닝 capacity", min_value=0.5,
            max_value=100.0, value=8.0, step=0.5)
        attrition = st.slider("추가 운영 손실(동의철회 등) 비율", 0.0, 0.5,
                              0.10, step=0.01)
        site_activation = st.slider("사이트 가동 평균 소요 (개월)", 0.0, 12.0,
                                    2.0, step=0.5)

    criteria = {
        "bmi_min": bmi_min, "bmi_max": bmi_max,
        "age_min": age_min, "age_max": age_max,
        "waist_min": waist_min,
        "comorbidities": comorbidities,
        "comorbidity_required_count": comorb_count,
        "exclude_t2dm": exclude_t2dm,
        "exclude_recent_glp1ra": exclude_glp1ra,
        "exclude_current_smoker": exclude_smoker,
        "exclude_severe_htn": exclude_severe_htn,
        "exclude_high_alt": exclude_high_alt,
    }

    # ----------------------------------------------------------------
    # 계산
    # ----------------------------------------------------------------
    pop = core.estimate_population(df, criteria)
    sens = core.sensitivity_analysis(df, criteria)
    sf = core.screen_fail_analysis(df, criteria)
    tl = core.recruitment_timeline(
        target_n=target_n, n_sites=n_sites,
        screening_per_site_month=screening_per_site,
        screen_fail_rate=sf["screen_fail_rate"],
        extra_attrition=attrition,
        site_activation_months=site_activation,
    )
    need_screen = core.screened_to_enrolled(target_n, sf["screen_fail_rate"],
                                            attrition)

    # ----------------------------------------------------------------
    # 탭 구성
    # ----------------------------------------------------------------
    tab1, tab2, tab3, tab4, tab5 = st.tabs([
        "적격 모집단", "기준 민감도", "screen-fail",
        "모집 timeline", "feasibility 리포트"])

    # --- 1. 적격 모집단 ---
    with tab1:
        st.subheader("적격 모집단 추정 (합성 KNHANES 가중 외삽)")
        c1, c2, c3 = st.columns(3)
        c1.metric("적격 표본 수",
                  f"{pop['n_sample_eligible']:,} / {pop['n_sample_total']:,}")
        c2.metric("적격 비율", f"{pop['prevalence']*100:.2f}%",
                  help=(f"95% CI {pop['prevalence_ci'][0]*100:.2f}~"
                        f"{pop['prevalence_ci'][1]*100:.2f}%"))
        c3.metric("적격 모집단(외삽)",
                  f"{pop['weighted_eligible']:,.0f} 명")
        st.write(f"**95% 신뢰구간:** {pop['ci_low']:,.0f} ~ "
                 f"{pop['ci_high']:,.0f} 명")
        st.write(f"설계효과(deff) {pop['design_effect']:.2f}, "
                 f"유효표본 {pop['effective_n']:.0f}")
        st.markdown("**적용된 적격성 기준**")
        st.code(core.criteria_summary_text(criteria))

    # --- 2. 기준 민감도 (tornado) ---
    with tab2:
        st.subheader("기준 민감도 분석 — tornado")
        st.caption("각 기준을 단독으로 완화했을 때 적격 모집단 증가량. "
                   "막대가 길수록 '비싼' 기준입니다.")
        if sens:
            sdf = pd.DataFrame(sens)[["label", "delta", "delta_pct"]]
            sdf.columns = ["기준 완화", "모집단 증가(명)", "증가율(%)"]
            chart_df = sdf.set_index("기준 완화")[["모집단 증가(명)"]]
            st.bar_chart(chart_df, horizontal=True)
            st.dataframe(sdf, use_container_width=True, hide_index=True)
            top = sens[0]
            st.success(f"가장 '비싼' 기준: **{top['label']}** "
                       f"(완화 시 +{top['delta']:,.0f} 명, "
                       f"{top['delta_pct']:+.1f}%)")
        else:
            st.info("완화 가능한 기준이 없습니다.")

    # --- 3. screen-fail ---
    with tab3:
        st.subheader("screen-fail 예측")
        c1, c2, c3 = st.columns(3)
        c1.metric("포함기준 통과 인구",
                  f"{sf['weighted_inclusion']:,.0f} 명")
        c2.metric("최종 적격 인구",
                  f"{sf['weighted_eligible']:,.0f} 명")
        c3.metric("예상 screen-fail율",
                  f"{sf['screen_fail_rate']*100:.1f}%")
        if sf["per_exclusion"]:
            st.markdown("**제외기준별 단독 영향** (포함기준 통과자 대비)")
            edf = pd.DataFrame(sf["per_exclusion"])
            edf = edf[["label", "weighted_excluded", "pct_of_inclusion"]]
            edf.columns = ["제외기준", "제외 인구(명)", "포함자 대비(%)"]
            st.dataframe(edf, use_container_width=True, hide_index=True)
        st.info(f"목표 등록 **{target_n}명** 달성에 필요한 스크리닝 수: "
                f"약 **{need_screen:,.0f}명** "
                f"(추가 운영 손실 {attrition*100:.0f}% 가정 포함)")

    # --- 4. 모집 timeline ---
    with tab4:
        st.subheader("모집 timeline 시뮬레이터 (Monte-Carlo)")
        c1, c2, c3 = st.columns(3)
        c1.metric("P10 (낙관)", f"{tl['p10']:.0f} 개월")
        c2.metric("P50 (중앙)", f"{tl['p50']:.0f} 개월")
        c3.metric("P90 (보수)", f"{tl['p90']:.0f} 개월")
        c4, c5 = st.columns(2)
        c4.metric("스크리닝→등록 환산율",
                  f"{tl['enroll_yield']*100:.1f}%")
        c5.metric(f"{tl['max_months']}개월 내 도달 확률",
                  f"{tl['feasible_ratio']*100:.1f}%")
        st.write(f"전 사이트 가동 시 월 기대 등록: "
                 f"**{tl['monthly_expected_enroll']:.1f}명** "
                 f"(사이트 {n_sites}개 × 월 {screening_per_site:.0f}건 × "
                 f"환산율 {tl['enroll_yield']*100:.0f}%)")
        st.bar_chart(pd.DataFrame(
            {"개월": [tl["p10"], tl["p50"], tl["p90"]]},
            index=["P10", "P50", "P90"]))

    # --- 5. feasibility 리포트 ---
    with tab5:
        st.subheader("feasibility 리포트 (프로토콜 feasibility 섹션 형식)")
        report = core.generate_report(
            df, criteria,
            target_n=target_n, n_sites=n_sites,
            screening_per_site_month=screening_per_site,
            extra_attrition=attrition,
            site_activation_months=site_activation,
        )
        st.code(report)
        st.download_button("리포트 텍스트 다운로드", report,
                           file_name="obesity_recruit_feasibility.txt")


if __name__ == "__main__":
    main()
