import math

import pytest

from pygui.backend.models.frame_stats import (
    EdgeCenterStats,
    Stats,
    compute_edge_center,
    compute_stats,
)


def test_basic_stat():
    s = compute_stats([148.0, 150.0, 149.0])
    assert s.min == 148.0 and s.min_index == 0
    assert s.max == 150.0 and s.max_index == 1
    assert s.diff == pytest.approx(2.0)
    assert s.avg == pytest.approx(149.0)
    assert s.sigma == pytest.approx(math.sqrt(2 / 3))
    assert s.three_sigma == pytest.approx(3 * math.sqrt(2 / 3))


def test_empty_values_returns_zeros():
    s = compute_stats([])
    assert s == Stats(0.0, -1, 0.0, -1, 0.0, 0.0, 0.0, 0.0)


def test_ignores_nan():
    s = compute_stats([149.0, float("nan"), 151.0])
    assert s.min == 149.0 and s.min_index == 0
    assert s.max == 151.0 and s.max_index == 2
    assert s.diff == pytest.approx(2.0)
    assert s.avg == pytest.approx(150.0)
    assert s.sigma == pytest.approx(1.0)
    assert s.three_sigma == pytest.approx(3.0)


# ---- edge-center delta (ADR-0002) ----

PAIRS = ((0, 4), (1, 4), (2, 5), (3, 5))


def test_edge_center_min_max_pairs():
    #        e0     e1     e2     e3     c4     c5
    values = [150.0, 148.0, 151.0, 149.4, 149.0, 149.5]
    ec = compute_edge_center(values, PAIRS, excluded=set())
    # deltas: |150-149|=1.0, |148-149|=1.0, |151-149.5|=1.5, |149.4-149.5|=0.1
    assert ec == EdgeCenterStats(
        min_edge_index=3, min_center_index=5, min_edge=149.4, min_center=149.5,
        min_delta=pytest.approx(0.1),
        max_edge_index=2, max_center_index=5, max_edge=151.0, max_center=149.5,
        max_delta=pytest.approx(1.5),
    )


def test_edge_center_skips_excluded_sensors():
    values = [150.0, 148.0, 151.0, 149.4, 149.0, 149.5]
    ec = compute_edge_center(values, PAIRS, excluded={2, 3})  # both c5 pairs out
    assert ec is not None
    assert ec.max_delta == pytest.approx(1.0)
    # excluding a center sensor kills all its pairs
    assert compute_edge_center(values, PAIRS, excluded={4, 5}) is None


def test_edge_center_skips_nan_and_out_of_range():
    values = [150.0, math.nan, 149.0]  # only pair (0, 2) is computable
    ec = compute_edge_center(values, ((0, 2), (1, 2), (0, 9)), excluded=set())
    assert ec is not None
    assert ec.min_delta == ec.max_delta == pytest.approx(1.0)
    assert (ec.min_edge_index, ec.min_center_index) == (0, 2)


def test_edge_center_none_when_no_pairs():
    assert compute_edge_center([1.0, 2.0], (), excluded=set()) is None
    assert compute_edge_center([], PAIRS, excluded=set()) is None
