"""
generate_sample_data.py — HepatoLipidFlux 합성 샘플 데이터 생성기 (재현 가능)

세 그룹(Chow 대조 / GAN diet MASH / GAN diet + 가상 약물)의
마우스 코호트에 대해 다음 CSV를 data/ 폴더에 생성한다:

  - plasma_tg_timeseries.csv : tyloxapol 주사 후 혈장 TG 시계열 (long format)
  - dnl_isotopomer.csv       : 간 TG 지방산 mass isotopomer 분포 (wide format)
  - oxidation_tracer.csv     : [13C]palmitate 산화 추적자 측정 (wide format)
  - animal_meta.csv          : 동물별 체중 / 제지방량 메타데이터

모든 값은 합성(synthetic)이며 마우스 문헌의 대표적 범위를 본떴다.
연구용·참고용 — 실제 실험 데이터가 아니다.

재현: `python3 generate_sample_data.py`  (난수 시드 고정 = 20260518)
"""

from __future__ import annotations

import os
import numpy as np
import pandas as pd

SEED = 20260518
rng = np.random.default_rng(SEED)

HERE = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(HERE, "data")
os.makedirs(DATA_DIR, exist_ok=True)

# 그룹 정의: (이름, 동물 수, VLDL 분비 기울기 평균(mg/dL/min), DNL 분율 평균,
#             산화 회수분율 평균, 체중 평균(g))
# 기울기는 마우스 tyloxapol 연구에서 관찰되는 빠른 혈장 TG 상승을 본떠
# 체중 정규화 후 참조범위(약 180-560 mg/kg/h)에 들도록 설정했다.
GROUPS = [
    ("Chow",            6, 9.0, 0.10, 0.16, 27.0),
    ("GAN_MASH",        6, 16.0, 0.32, 0.10, 41.0),
    ("GAN_MASH_DrugX",  6, 12.0, 0.16, 0.15, 38.0),
]

TIME_POINTS = [0, 15, 30, 45, 60, 90, 120]  # 분


def gen_animal_meta() -> pd.DataFrame:
    rows = []
    for gname, n, _slope, _dnl, _ox, bw_mean in GROUPS:
        for i in range(1, n + 1):
            aid = f"{gname}_{i:02d}"
            bw = float(rng.normal(bw_mean, bw_mean * 0.06))
            lean = float(bw * rng.uniform(0.62, 0.74))  # 제지방량은 체중의 일부
            batch = f"batch{((i - 1) % 2) + 1}"  # cage/batch 임의효과용
            rows.append({
                "animal_id": aid,
                "group": gname,
                "body_weight_g": round(bw, 2),
                "lean_mass_g": round(lean, 2),
                "batch": batch,
            })
    return pd.DataFrame(rows)


def gen_plasma_tg(meta: pd.DataFrame) -> pd.DataFrame:
    """
    tyloxapol 주사 후 혈장 TG는 초기 선형 증가 후 후기에 포화한다.
    선형 구간: 0..60분, 이후 90/120분은 포화(점근)하도록 모델링한다.
    """
    rows = []
    slope_map = {g[0]: g[2] for g in GROUPS}
    for _, m in meta.iterrows():
        aid = m["animal_id"]
        gname = m["group"]
        base_slope = slope_map[gname] * rng.normal(1.0, 0.08)
        baseline = float(rng.normal(55.0, 6.0))  # 0분 기저 TG (mg/dL)
        # 0..60분은 순수 선형 증가, 60분 이후는 포화.
        # 약 60%의 동물은 강한 포화(거의 plateau)를 보여 선형구간 자동탐지·
        # 비선형 경고 기능이 데모에서 작동하고, 나머지는 거의 선형을 유지한다.
        animal_idx = int(aid.split("_")[-1])
        strong_sat = (animal_idx % 5) != 0  # 5마리 중 4마리가 강한 포화
        for t in TIME_POINTS:
            conc_linear = baseline + base_slope * t
            if t <= 60:
                conc = conc_linear
            else:
                extra_t = t - 60
                conc60 = baseline + base_slope * 60.0
                if strong_sat:
                    # 강한 포화: 60분 이후 거의 plateau (작은 점근 증가만)
                    sat_extra = base_slope * 18.0 * (1.0 - np.exp(-extra_t / 40.0))
                    conc = conc60 + sat_extra
                else:
                    # 약한 포화: 60분 이후에도 선형에 가까움
                    lin_extra = base_slope * extra_t
                    sat_extra = base_slope * 55.0 * (1.0 - np.exp(-extra_t / 90.0))
                    conc = conc60 + 0.35 * lin_extra + 0.65 * sat_extra
            # 측정 잡음: 기울기 대비 작게 유지하여 선형구간 r^2 확보
            conc += rng.normal(0.0, 10.0)
            rows.append({
                "animal_id": aid,
                "group": gname,
                "time_min": t,
                "plasma_tg_mg_dl": round(max(conc, 1.0), 2),
            })
    return pd.DataFrame(rows)


def gen_dnl_isotopomer(meta: pd.DataFrame) -> pd.DataFrame:
    """
    간 TG palmitate 의 mass isotopomer 분포 M0..M4.
    fractional DNL 이 높을수록 M1+ 비중이 커진다.
    전구체(2H2O body water) enrichment 는 ~4% 가정.
    """
    from flux_core import binomial_isotopomer_distribution

    dnl_map = {g[0]: g[3] for g in GROUPS}
    n_sites = 22
    rows = []
    for _, m in meta.iterrows():
        aid = m["animal_id"]
        gname = m["group"]
        f_true = float(np.clip(rng.normal(dnl_map[gname], 0.04), 0.01, 0.85))
        precursor = float(rng.normal(0.040, 0.003))  # body water 2H enrichment
        new_dist = binomial_isotopomer_distribution(n_sites, precursor)[:5]
        nat = np.array([0.955, 0.040, 0.005, 0.0, 0.0])
        mix = (1 - f_true) * nat + f_true * (new_dist / new_dist.sum())
        mix = mix + rng.normal(0.0, 0.004, size=5)
        mix = np.clip(mix, 0.0, None)
        mix = mix / mix.sum()
        rows.append({
            "animal_id": aid,
            "group": gname,
            "precursor_enrichment": round(precursor, 4),
            "n_label_sites": n_sites,
            "M0": round(float(mix[0]), 5),
            "M1": round(float(mix[1]), 5),
            "M2": round(float(mix[2]), 5),
            "M3": round(float(mix[3]), 5),
            "M4": round(float(mix[4]), 5),
        })
    return pd.DataFrame(rows)


def gen_oxidation(meta: pd.DataFrame) -> pd.DataFrame:
    """
    [13C]palmitate 산화 추적자: 호기 13CO2 회수분율 + 케톤체 표지분율.
    """
    ox_map = {g[0]: g[4] for g in GROUPS}
    rows = []
    for _, m in meta.iterrows():
        aid = m["animal_id"]
        gname = m["group"]
        bw = float(m["body_weight_g"])
        dose = round(float(rng.normal(38.0, 1.5)), 2)  # umol [13C]palmitate 볼루스
        co2_rec = float(np.clip(rng.normal(ox_map[gname], 0.02), 0.01, 0.95))
        ketone = float(np.clip(rng.normal(co2_rec * 0.25, 0.01), 0.0, 0.5))
        rows.append({
            "animal_id": aid,
            "group": gname,
            "tracer_dose_umol": dose,
            "co2_recovery_fraction": round(co2_rec, 4),
            "ketone_label_fraction": round(ketone, 4),
            "body_weight_g": round(bw, 2),
            "bicarbonate_recovery": 0.80,
            "duration_h": 1.0,
        })
    return pd.DataFrame(rows)


def main() -> None:
    meta = gen_animal_meta()
    plasma = gen_plasma_tg(meta)
    dnl = gen_dnl_isotopomer(meta)
    ox = gen_oxidation(meta)

    meta.to_csv(os.path.join(DATA_DIR, "animal_meta.csv"), index=False)
    plasma.to_csv(os.path.join(DATA_DIR, "plasma_tg_timeseries.csv"), index=False)
    dnl.to_csv(os.path.join(DATA_DIR, "dnl_isotopomer.csv"), index=False)
    ox.to_csv(os.path.join(DATA_DIR, "oxidation_tracer.csv"), index=False)

    print("합성 데이터 생성 완료 (시드 =", SEED, ")")
    print(f"  animal_meta.csv          : {len(meta)} 행")
    print(f"  plasma_tg_timeseries.csv : {len(plasma)} 행")
    print(f"  dnl_isotopomer.csv       : {len(dnl)} 행")
    print(f"  oxidation_tracer.csv     : {len(ox)} 행")
    print("저장 위치:", DATA_DIR)


if __name__ == "__main__":
    main()
