"""Tests for RudderSensorConfig and DualRudderSensorConfig — Sprint 4."""

from __future__ import annotations

import pytest
from pydantic import ValidationError

from arautopilot.core.sensor_config import (
    DualRudderSensorConfig,
    RudderSensorConfig,
    RudderSensorType,
)


def _primary(
    sensor_type: RudderSensorType = RudderSensorType.AS5048A_SPI,
    cs: int = 10,
    fsd: float = 35.0,
) -> RudderSensorConfig:
    return RudderSensorConfig(type=sensor_type, spi_cs_gpio=cs, full_scale_deg=fsd)


def _redundant(cs: int = 11) -> RudderSensorConfig:
    return RudderSensorConfig(
        type=RudderSensorType.AS5048A_SPI,
        label="Redundant – actuator arm",
        spi_cs_gpio=cs,
        full_scale_deg=35.0,
    )


class TestRudderSensorConfig:
    def test_as5048a_defaults(self) -> None:
        s = _primary()
        assert s.type == RudderSensorType.AS5048A_SPI
        assert s.spi_cs_gpio == 10
        assert s.full_scale_deg == 35.0
        assert s.zero_offset_deg == 0.0
        assert s.divergence_alarm_deg == 3.0
        assert s.divergence_failover_deg == 6.0

    def test_potentiometer_type(self) -> None:
        s = _primary(sensor_type=RudderSensorType.POTENTIOMETER)
        assert s.type == RudderSensorType.POTENTIOMETER

    def test_gpio_bounds(self) -> None:
        with pytest.raises(ValidationError):
            RudderSensorConfig(type=RudderSensorType.AS5048A_SPI, spi_cs_gpio=40)
        with pytest.raises(ValidationError):
            RudderSensorConfig(type=RudderSensorType.AS5048A_SPI, spi_cs_gpio=-1)

    def test_full_scale_bounds(self) -> None:
        with pytest.raises(ValidationError):
            _primary(fsd=0.0)
        with pytest.raises(ValidationError):
            _primary(fsd=46.0)

    def test_label_optional(self) -> None:
        s = RudderSensorConfig(type=RudderSensorType.AS5048A_SPI)
        assert s.label == ""

    def test_extra_fields_forbidden(self) -> None:
        with pytest.raises(ValidationError):
            RudderSensorConfig(
                type=RudderSensorType.AS5048A_SPI,
                unknown_field="boom",  # type: ignore[call-arg]
            )

    def test_roundtrip_json(self) -> None:
        s = _primary()
        restored = RudderSensorConfig.model_validate_json(s.model_dump_json())
        assert restored == s


class TestDualRudderSensorConfig:
    def test_single_sensor_mode(self) -> None:
        d = DualRudderSensorConfig(primary=_primary())
        assert d.redundant is None
        assert not d.has_redundancy

    def test_dual_sensor_mode(self) -> None:
        d = DualRudderSensorConfig(primary=_primary(), redundant=_redundant())
        assert d.redundant is not None
        assert d.has_redundancy

    def test_different_cs_pins(self) -> None:
        d = DualRudderSensorConfig(primary=_primary(cs=10), redundant=_redundant(cs=11))
        assert d.primary.spi_cs_gpio == 10
        assert d.redundant is not None
        assert d.redundant.spi_cs_gpio == 11

    def test_roundtrip_yaml(self) -> None:
        import yaml

        d = DualRudderSensorConfig(primary=_primary(), redundant=_redundant())
        data = yaml.safe_dump(d.model_dump(mode="json"))
        restored = DualRudderSensorConfig.model_validate(yaml.safe_load(data))
        assert restored == d
