"""oftt.py — 경구 지방부하검사(Oral Fat Tolerance Test) 분석.

시점별 혈중 중성지방(TG, ±apoB48, ±retinyl palmitate) 농도 곡선으로부터
식후 지질 동태 지표를 산출한다:
  - total AUC / incremental AUC (0-6h, 0-8h)
  - peak, Tmax
  - 기저 복귀(청소) 시간
  - 섭취 지방량 대비 normalize (per g fat)

방법론 근거:
  - Frayn KN. Metabolic Regulation: A Human Perspective (식후 지질 대사).
  - Cobelli/Toffolo 등 minimal-model 기반 식후 청소 개념.
  - 일반적 OFTT 프로토콜: 표준 고지방식(예: 50-65 g fat/m² 또는 정량 지방)
    섭취 후 0,2,4,6(,8)h TG 측정. (Mihas C et al. Curr Vasc Pharmacol 2011 리뷰 참조)

단위:
  - TG: mg/dL 또는 mmol/L (units.py에서 변환)
  - AUC: (농도단위)·h

면책: 연구용·참고용. 임상 의사결정에 직접 사용 금지.
"""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Dict, List, Optional, Sequence

import numpy as np

import auc as _auc


@dataclass
class OFTTResult:
    """OFTT 단일 곡선(한 피험자, 한 analyte) 분석 결과."""
    analyte: str
    baseline: float
    peak: float
    tmax_h: float
    total_auc: float
    incremental_auc: float
    incremental_auc_0_6: float
    incremental_auc_0_8: float
    return_to_baseline_h: float
    iauc_per_g_fat: Optional[float] = None
    units: str = ""
    flags: List[str] = field(default_factory=list)

    def as_dict(self) -> Dict[str, object]:
        return {
            "analyte": self.analyte,
            "units": self.units,
            "baseline": self.baseline,
            "peak": self.peak,
            "tmax_h": self.tmax_h,
            "total_auc": self.total_auc,
            "incremental_auc": self.incremental_auc,
            "incremental_auc_0_6h": self.incremental_auc_0_6,
            "incremental_auc_0_8h": self.incremental_auc_0_8,
            "return_to_baseline_h": self.return_to_baseline_h,
            "iauc_per_g_fat": self.iauc_per_g_fat,
            "flags": "; ".join(self.flags) if self.flags else "",
        }


def analyze_oftt(times_h: Sequence[float],
                 values: Sequence[float],
                 analyte: str = "TG",
                 units: str = "mg/dL",
                 fat_load_g: Optional[float] = None,
                 baseline: Optional[float] = None,
                 clip_negative_iauc: bool = False,
                 expected_times: Optional[Sequence[float]] = None) -> OFTTResult:
    """OFTT 단일 곡선 분석.

    Parameters
    ----------
    times_h : 시점(시간, h)
    values  : 해당 시점 농도값
    analyte : 'TG' | 'apoB48' | 'retinyl' 등 라벨
    units   : 농도 단위 문자열(메타데이터)
    fat_load_g : 섭취 총 지방량(g). 주어지면 iAUC/g fat 산출.
    baseline : 기저값(미지정 시 첫 시점값).
    clip_negative_iauc : iAUC 계산 시 음수 구간 절단 여부.
    expected_times : QC용 기대 시점 목록(누락 플래그).
    """
    times_h = list(times_h)
    values = list(values)
    base = float(values[0]) if baseline is None else float(baseline)

    peak, tmax = _auc.peak_and_tmax(times_h, values)
    tot = _auc.total_auc(times_h, values)
    iauc = _auc.incremental_auc(times_h, values, baseline=base,
                                clip_negative=clip_negative_iauc)

    # 0-6h, 0-8h 구간 iAUC: baseline subtract 후 cumulative_auc_to
    t_arr = np.asarray(times_h, dtype=float)
    v_arr = np.asarray(values, dtype=float)
    delta = v_arr - base
    if clip_negative_iauc:
        delta = np.clip(delta, 0.0, None)
    iauc_0_6 = _auc.cumulative_auc_to(t_arr, delta, 6.0)
    iauc_0_8 = _auc.cumulative_auc_to(t_arr, delta, 8.0)

    rtb = _auc.return_to_baseline_time(times_h, values, baseline=base)

    iauc_per_g = (iauc / fat_load_g) if (fat_load_g and fat_load_g > 0) else None

    flags: List[str] = []
    # QC: 시점 누락
    if expected_times is not None:
        present = set(round(float(x), 3) for x in times_h)
        for et in expected_times:
            if round(float(et), 3) not in present:
                flags.append(f"누락시점 {et}h")
    # QC: 음수값
    if np.any(v_arr < 0):
        flags.append("음수 농도값 존재")
    # QC: peak 이 기저보다 낮음(반응 없음/이상)
    if peak <= base:
        flags.append("peak<=baseline (식후 상승 미관찰)")
    # QC: 최소 시점 수
    if len(times_h) < 3:
        flags.append("시점 3개 미만 (AUC 신뢰도 낮음)")

    return OFTTResult(
        analyte=analyte,
        baseline=base,
        peak=peak,
        tmax_h=tmax,
        total_auc=tot,
        incremental_auc=iauc,
        incremental_auc_0_6=iauc_0_6,
        incremental_auc_0_8=iauc_0_8,
        return_to_baseline_h=rtb,
        iauc_per_g_fat=iauc_per_g,
        units=units,
        flags=flags,
    )


def analyze_oftt_long(df, id_col: str = "subject_id",
                      time_col: str = "time_h",
                      value_col: str = "tg",
                      analyte: str = "TG",
                      units: str = "mg/dL",
                      fat_load_g: Optional[float] = None,
                      expected_times: Optional[Sequence[float]] = None) -> List[Dict[str, object]]:
    """long-format DataFrame(subject_id,time_h,value)을 피험자별로 분석.

    pandas DataFrame을 받지만 import는 호출부에 위임(여기선 .groupby만 사용).
    반환: 피험자별 결과 dict 리스트.
    """
    out: List[Dict[str, object]] = []
    for sid, sub in df.groupby(id_col):
        sub = sub.sort_values(time_col)
        res = analyze_oftt(
            sub[time_col].tolist(),
            sub[value_col].tolist(),
            analyte=analyte,
            units=units,
            fat_load_g=fat_load_g,
            expected_times=expected_times,
        )
        d = res.as_dict()
        d[id_col] = sid
        out.append(d)
    return out
