"""Per-frame descriptive statistics"""

from __future__ import annotations

import math
from dataclasses import dataclass


@dataclass(frozen=True)
class Stats:
    min: float; min_index: int
    max: float; max_index: int
    diff: float; avg: float
    sigma: float; three_sigma: float


@dataclass(frozen=True)
class EdgeCenterStats:
    min_edge_index: int; min_center_index: int
    min_edge: float; min_center: float; min_delta: float
    max_edge_index: int; max_center_index: int
    max_edge: float; max_center: float; max_delta: float


def compute_edge_center(
    values: list[float],
    pairs: tuple[tuple[int, int], ...],
    excluded: set[int],
) -> EdgeCenterStats | None:
    """Min/max |edge - center| over the family's pair table.

    Pairs with an excluded (replaced) sensor, an index outside the frame, or a
    NaN value are skipped. Returns None when no pair is computable.
    """
    n = len(values)
    best: list[tuple[float, int, int]] = []
    for e, c in pairs:
        if e in excluded or c in excluded or e >= n or c >= n:
            continue
        if math.isnan(values[e]) or math.isnan(values[c]):
            continue
        best.append((abs(values[e] - values[c]), e, c))
    if not best:
        return None
    min_d, min_e, min_c = min(best)
    max_d, max_e, max_c = max(best)
    return EdgeCenterStats(
        min_edge_index=min_e, min_center_index=min_c,
        min_edge=values[min_e], min_center=values[min_c], min_delta=min_d,
        max_edge_index=max_e, max_center_index=max_c,
        max_edge=values[max_e], max_center=values[max_c], max_delta=max_d,
    )


def compute_stats(values: list[float]) -> Stats:
    clean = [(i, v) for i, v in enumerate(values) if not math.isnan(v)]
    if not clean:
        return Stats(0.0, -1, 0.0, -1, 0.0, 0.0, 0.0, 0.0)

    min_index, min_v = min(clean, key=lambda iv: iv[1])
    max_index, max_v = max(clean, key=lambda iv: iv[1])
    nums = [v for _, v in clean]
    avg = sum(nums) / len(nums)
    variance = sum((v - avg) ** 2 for v in nums) / len(nums)
    sigma = math.sqrt(variance)

    return Stats(
        min=min_v,
        min_index=min_index,
        max=max_v,
        max_index=max_index,
        diff=max_v - min_v,
        avg=avg,
        sigma=sigma,
        three_sigma=3 * sigma,
    )
