import math

import pytest

from pygui.backend.visualization.radial_metrics import bucket_by_ring, ring_boundaries
from pygui.backend.wafer.zwafer_models import Sensor


def test_ring_boundaries_empty_sensors_returns_default_quartiles():
    assert ring_boundaries([]) == [37.5, 75.0, 112.5, 150.0]


def test_ring_boundaries_single_ring_plus_outer_boundary():
    sensors = [
        Sensor(label="1", x=100.0, y=0.0),
        Sensor(label="2", x=0.0, y=100.0),
        Sensor(label="3", x=-100.0, y=0.0),
        Sensor(label="4", x=0.0, y=-100.0),
    ]
    assert ring_boundaries(sensors) == [100.0, 105.0]


def test_ring_boundaries_excludes_center_point():
    sensors = [
        Sensor(label="center", x=0.0, y=0.0),
        Sensor(label="1", x=50.0, y=0.0),
        Sensor(label="2", x=0.0, y=50.0),
        Sensor(label="3", x=-50.0, y=0.0),
        Sensor(label="4", x=0.0, y=-50.0),
    ]
    assert ring_boundaries(sensors) == [50.0, 52.5]


def test_ring_boundaries_merges_radii_within_2mm_and_averages():
    sensors = [
        Sensor(label="1", x=30.0, y=0.0),
        Sensor(label="2", x=0.0, y=31.5),
        Sensor(label="3", x=80.0, y=0.0),
    ]
    assert ring_boundaries(sensors) == [30.75, 80.0, 84.0]


def test_ring_boundaries_square_family_grid_buckets_by_distance_not_axis():
    # A square-family sensor grid (per wafer_layouts.py, e.g. the 80-sensor
    # square layout): axis points and corner points sit at different radial
    # distances from center despite forming a visually square pattern, so
    # ring clustering must key on hypot() distance, not grid position.
    sensors = [
        Sensor(label="axis1", x=40.0, y=0.0),
        Sensor(label="axis2", x=-40.0, y=0.0),
        Sensor(label="axis3", x=0.0, y=40.0),
        Sensor(label="axis4", x=0.0, y=-40.0),
        Sensor(label="corner1", x=40.0, y=40.0),
        Sensor(label="corner2", x=-40.0, y=-40.0),
    ]
    axis_r = 40.0
    corner_r = math.hypot(40.0, 40.0)
    outer = corner_r * 1.05
    assert ring_boundaries(sensors) == pytest.approx([axis_r, corner_r, outer])


def test_bucket_by_ring_groups_single_ring_sensors_together():
    sensors = [
        Sensor(label="1", x=100.0, y=0.0),
        Sensor(label="2", x=0.0, y=100.0),
        Sensor(label="3", x=-100.0, y=0.0),
        Sensor(label="4", x=0.0, y=-100.0),
    ]
    values = [10.0, 20.0, 30.0, 40.0]
    assert bucket_by_ring(sensors, values) == {0: [10.0, 20.0, 30.0, 40.0], 1: []}


def test_bucket_by_ring_assigns_each_sensor_to_its_nearest_boundary():
    sensors = [
        Sensor(label="1", x=30.0, y=0.0),
        Sensor(label="2", x=0.0, y=31.5),
        Sensor(label="3", x=80.0, y=0.0),
    ]
    values = [1.0, 2.0, 3.0]
    assert bucket_by_ring(sensors, values) == {0: [1.0, 2.0], 1: [3.0], 2: []}


def test_bucket_by_ring_ignores_sensors_beyond_a_short_values_list():
    sensors = [
        Sensor(label="1", x=100.0, y=0.0),
        Sensor(label="2", x=0.0, y=100.0),
    ]
    values = [10.0]  # frame shorter than sensor count
    assert bucket_by_ring(sensors, values) == {0: [10.0], 1: []}
