"""Per-portal tract + cohort 통계 + ANOVA.

scipy 가용 시 일원분산분석, 아니면 numpy 기반 F 통계량 직접 계산.
"""
from __future__ import annotations

from dataclasses import dataclass
from typing import Dict, List, Tuple

import numpy as np


@dataclass
class CohortRow:
    group: str
    mouse_id: str
    slide_id: str
    n_portal: int
    dr_count: int
    dr_per_portal: float
    t1_count: int
    t2_count: int
    t3_count: int
    sirius_area_pct: float
    bridge_count: int
    ductular_bridge_count: int


def per_slide_summary(
    slide_id: str,
    mouse_id: str,
    group: str,
    portal_tracts: List,
    dr_structures: List,
    sirius_area_pct: float,
    bridges: List,
) -> CohortRow:
    n_portal = len(portal_tracts)
    types = [d.dr_type for d in dr_structures]
    t1 = types.count("T1")
    t2 = types.count("T2")
    t3 = types.count("T3")
    bc = len([b for b in bridges if b.bridge_type != "none"])
    db = len([b for b in bridges if b.bridge_type == "ductular_bridge"])
    return CohortRow(
        group=group,
        mouse_id=mouse_id,
        slide_id=slide_id,
        n_portal=n_portal,
        dr_count=len(dr_structures),
        dr_per_portal=(len(dr_structures) / n_portal) if n_portal > 0 else 0.0,
        t1_count=t1,
        t2_count=t2,
        t3_count=t3,
        sirius_area_pct=sirius_area_pct,
        bridge_count=bc,
        ductular_bridge_count=db,
    )


def one_way_anova(groups: Dict[str, List[float]]) -> Dict[str, float]:
    """numpy 기반 one-way ANOVA. F, p (근사) 반환.

    p는 scipy 없이는 정확 계산 어려워 F만 보고하고 p는 서바이벌 함수 근사 사용.
    """
    try:
        from scipy import stats as sstats  # type: ignore

        arrays = [np.asarray(v, dtype=float) for v in groups.values() if len(v) > 0]
        if len(arrays) < 2:
            return {"F": float("nan"), "p": float("nan")}
        f, p = sstats.f_oneway(*arrays)
        return {"F": float(f), "p": float(p)}
    except Exception:
        # 수동 F 계산
        all_vals = []
        means = []
        ns = []
        for v in groups.values():
            if len(v) == 0:
                continue
            arr = np.asarray(v, dtype=float)
            all_vals.append(arr)
            means.append(arr.mean())
            ns.append(len(arr))
        if len(all_vals) < 2:
            return {"F": float("nan"), "p": float("nan")}
        grand = np.concatenate(all_vals).mean()
        ss_between = sum(n * (m - grand) ** 2 for n, m in zip(ns, means))
        ss_within = sum(((arr - arr.mean()) ** 2).sum() for arr in all_vals)
        df_between = len(all_vals) - 1
        df_within = sum(ns) - len(all_vals)
        if df_within <= 0 or ss_within <= 0:
            return {"F": float("nan"), "p": float("nan")}
        ms_b = ss_between / df_between
        ms_w = ss_within / df_within
        F = ms_b / ms_w if ms_w > 0 else float("nan")
        # p 근사: F > 4 → 대략 p<0.05 식 보고, 정확한 cdf는 미제공
        p = float("nan")
        return {"F": float(F), "p": p, "df_between": df_between, "df_within": df_within}


def cohort_to_dict(rows: List[CohortRow]) -> List[Dict]:
    return [r.__dict__ for r in rows]
