from typing import Any

from hypothesis import given, strategies as st

from inire.geometry.primitives import Port, rotate_port, translate_port


@st.composite
def port_strategy(draw: Any) -> Port:
    x = draw(st.floats(min_value=-1e6, max_value=1e6))
    y = draw(st.floats(min_value=-1e6, max_value=1e6))
    orientation = draw(st.sampled_from([0, 90, 180, 270]))
    return Port(x, y, orientation)


def test_port_snapping() -> None:
    p = Port(0.123456, 0.654321, 90)
    assert p.x == 0.123
    assert p.y == 0.654


@given(p=port_strategy())
def test_port_transform_invariants(p: Port) -> None:
    # Rotating 90 degrees 4 times should return to same orientation
    p_rot = p
    for _ in range(4):
        p_rot = rotate_port(p_rot, 90)

    assert abs(p_rot.x - p.x) < 1e-6
    assert abs(p_rot.y - p.y) < 1e-6
    assert (p_rot.orientation % 360) == (p.orientation % 360)


@given(
    p=port_strategy(),
    dx=st.floats(min_value=-1000, max_value=1000),
    dy=st.floats(min_value=-1000, max_value=1000),
)
def test_translate_snapping(p: Port, dx: float, dy: float) -> None:
    p_trans = translate_port(p, dx, dy)
    # Check that snapped result is indeed multiple of GRID_SNAP_UM (0.001 um = 1nm)
    assert abs(p_trans.x * 1000 - round(p_trans.x * 1000)) < 1e-6
    assert abs(p_trans.y * 1000 - round(p_trans.y * 1000)) < 1e-6


def test_orientation_normalization() -> None:
    p = Port(0, 0, 360)
    assert p.orientation == 0.0

    p2 = Port(0, 0, -90)
    assert p2.orientation == 270.0
