"""Tests for StimParams converter — proto → domain model."""

from __future__ import annotations

from radiens_core._converters._stim import stim_params_from_proto
from radiens_core._generated import allegoserver_pb2
from radiens_core.models.stim import (
    StimParams,
    StimPolarity,
    StimPulseMode,
    StimShape,
    StimTriggerEdgeOrLevel,
    StimTriggerHighOrLow,
)


def _make_proto_stim_params(
    *,
    sys_chan_idx: int = 0,
    shape: allegoserver_pb2.StimShape.ValueType = allegoserver_pb2.BIPHASIC,
    polarity: allegoserver_pb2.StimPolarity.ValueType = allegoserver_pb2.CATHODIC_FIRST,
) -> allegoserver_pb2.StimParams:
    """Build a fully populated StimParams proto message."""
    return allegoserver_pb2.StimParams(
        stimShape=shape,
        stimPolarity=polarity,
        firstPhaseDuration=0.001,
        secondPhaseDuration=0.002,
        interphaseDelay=0.0001,
        firstPhaseAmplitude=100.0,
        secondPhaseAmplitude=50.0,
        baselineVoltage=0.0,
        triggerEdgeOrLevel=allegoserver_pb2.ST_EDGE,
        triggerHighOrLow=allegoserver_pb2.ST_HIGH,
        enabled=True,
        postTriggerDelay=0.005,
        pulseOrTrain=allegoserver_pb2.SINGLE_PULSE,
        numberOfStimPulses=1,
        pulseTrainPeriod=0.01,
        refractoryPeriod=0.001,
        preStimAmpSettle=0.0005,
        postStimAmpSettle=0.001,
        maintainAmpSettle=False,
        enableAmpSettle=True,
        postStimChargeRecovOn=0.0,
        postStimChargeRecovOff=0.0,
        enableChargeRecovery=False,
        triggerSourceIsKeypress=False,
        triggerSourceIdx=0,
        stimSysChanIdx=sys_chan_idx,
    )


class TestStimParamsFromProto:
    def test_single_channel(self) -> None:
        pb = _make_proto_stim_params(sys_chan_idx=5)
        reply = allegoserver_pb2.StimParamsReply()
        reply.params[5].CopyFrom(pb)

        result = stim_params_from_proto(reply)

        assert len(result) == 1
        assert 5 in result
        params = result[5]
        assert isinstance(params, StimParams)
        assert params.stim_shape == StimShape.BIPHASIC
        assert params.stim_polarity == StimPolarity.CATHODIC_FIRST
        assert params.first_phase_duration == 0.001
        assert params.second_phase_duration == 0.002
        assert params.interphase_delay == 0.0001
        assert params.first_phase_amplitude == 100.0
        assert params.second_phase_amplitude == 50.0
        assert params.baseline_voltage == 0.0
        assert params.trigger_edge_or_level == StimTriggerEdgeOrLevel.EDGE
        assert params.trigger_high_or_low == StimTriggerHighOrLow.HIGH
        assert params.enabled is True
        assert params.post_trigger_delay == 0.005
        assert params.pulse_or_train == StimPulseMode.SINGLE_PULSE
        assert params.number_of_stim_pulses == 1
        assert params.pulse_train_period == 0.01
        assert params.refractory_period == 0.001
        assert params.pre_stim_amp_settle == 0.0005
        assert params.post_stim_amp_settle == 0.001
        assert params.maintain_amp_settle is False
        assert params.enable_amp_settle is True
        assert params.post_stim_charge_recov_on == 0.0
        assert params.post_stim_charge_recov_off == 0.0
        assert params.enable_charge_recovery is False
        assert params.trigger_source_is_keypress is False
        assert params.trigger_source_idx == 0
        assert params.stim_sys_chan_idx == 5

    def test_multiple_channels(self) -> None:
        reply = allegoserver_pb2.StimParamsReply()
        reply.params[0].CopyFrom(_make_proto_stim_params(sys_chan_idx=0))
        reply.params[3].CopyFrom(
            _make_proto_stim_params(
                sys_chan_idx=3,
                shape=allegoserver_pb2.MONOPHASIC,
                polarity=allegoserver_pb2.ANODIC_FIRST,
            )
        )

        result = stim_params_from_proto(reply)

        assert len(result) == 2
        assert result[0].stim_shape == StimShape.BIPHASIC
        assert result[3].stim_shape == StimShape.MONOPHASIC
        assert result[3].stim_polarity == StimPolarity.ANODIC_FIRST

    def test_empty_reply(self) -> None:
        reply = allegoserver_pb2.StimParamsReply()
        result = stim_params_from_proto(reply)
        assert result == {}

    def test_frozen_model(self) -> None:
        pb = _make_proto_stim_params()
        reply = allegoserver_pb2.StimParamsReply()
        reply.params[0].CopyFrom(pb)
        result = stim_params_from_proto(reply)
        params = result[0]

        import pytest
        from pydantic import ValidationError

        with pytest.raises(ValidationError):
            params.enabled = False  # type: ignore[misc]

    def test_all_enum_variations(self) -> None:
        """Verify all enum combinations convert correctly."""
        reply = allegoserver_pb2.StimParamsReply()
        pb = allegoserver_pb2.StimParams(
            stimShape=allegoserver_pb2.TRIPHASIC,
            stimPolarity=allegoserver_pb2.ANODIC_FIRST,
            firstPhaseDuration=0.0,
            secondPhaseDuration=0.0,
            interphaseDelay=0.0,
            firstPhaseAmplitude=0.0,
            secondPhaseAmplitude=0.0,
            baselineVoltage=0.0,
            triggerEdgeOrLevel=allegoserver_pb2.ST_LEVEL,
            triggerHighOrLow=allegoserver_pb2.ST_LOW,
            enabled=False,
            postTriggerDelay=0.0,
            pulseOrTrain=allegoserver_pb2.PULSE_TRAIN,
            numberOfStimPulses=10,
            pulseTrainPeriod=0.0,
            refractoryPeriod=0.0,
            preStimAmpSettle=0.0,
            postStimAmpSettle=0.0,
            maintainAmpSettle=True,
            enableAmpSettle=False,
            postStimChargeRecovOn=0.0,
            postStimChargeRecovOff=0.0,
            enableChargeRecovery=True,
            triggerSourceIsKeypress=True,
            triggerSourceIdx=2,
            stimSysChanIdx=7,
        )
        reply.params[7].CopyFrom(pb)

        result = stim_params_from_proto(reply)
        params = result[7]
        assert params.stim_shape == StimShape.TRIPHASIC
        assert params.stim_polarity == StimPolarity.ANODIC_FIRST
        assert params.trigger_edge_or_level == StimTriggerEdgeOrLevel.LEVEL
        assert params.trigger_high_or_low == StimTriggerHighOrLow.LOW
        assert params.pulse_or_train == StimPulseMode.PULSE_TRAIN
        assert params.number_of_stim_pulses == 10
        assert params.enable_charge_recovery is True
        assert params.trigger_source_is_keypress is True
