"""AllegoCore RPC wrappers — thin service layer (proto only)."""

from __future__ import annotations

from typing import TYPE_CHECKING

import grpc

from radiens_core._constants import PRIMARY_CACHE_STREAM_GROUP_ID
from radiens_core._generated import allegoserver_pb2, allegoserver_pb2_grpc, common_pb2, datasource_pb2
from radiens_core._transport._errors import handle_grpc_error
from radiens_core.models.common import ClientType, ServiceEndpoint

if TYPE_CHECKING:
    from radiens_core._transport._channel_pool import ChannelPool


def get_intan_impedance_proto(
    pool: ChannelPool,
    addr: str,
    request: datasource_pb2.ImpedanceRequest,
) -> datasource_pb2.Impedance:
    """Send ``GetIntanImpedance`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetIntanImpedance(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def scan_ports_proto(
    pool: ChannelPool,
    addr: str,
) -> common_pb2.SignalGroup:
    """Send ``ScanPorts`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.ScanPorts(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_stim_params_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.StimParamsReply:
    """Send ``GetStimParams`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetStimParams(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_status_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.BackboneStatus:
    """Send ``GetStatus`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetStatus(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_stream_state_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.SetStreamRequest,
) -> common_pb2.StandardReply:
    """Send ``SetStreamState`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetStreamState(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def healthcheck_proto(
    pool: ChannelPool,
    addr: str,
    service: ServiceEndpoint = ServiceEndpoint.ALLEGO_LIFECYCLE,
) -> common_pb2.StandardReply:
    """Send ``Healthcheck`` RPC to an allego service, return raw proto response.

    *service* selects the stub: ``ALLEGO_CORE`` uses ``AllegoCoreStub``,
    anything else defaults to ``AllegoLifecycleStub``.
    """
    channel = pool.get_channel(addr)
    req = common_pb2.StandardRequest()
    try:
        if service == ServiceEndpoint.ALLEGO_CORE:
            return allegoserver_pb2_grpc.AllegoCoreStub(channel).Healthcheck(req)
        return allegoserver_pb2_grpc.AllegoLifecycleStub(channel).Healthcheck(req)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_record_state_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.SetRecordRequest,
) -> common_pb2.StandardReply:
    """Send ``SetRecordState`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetRecordState(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def restart_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.RestartRequest,
) -> common_pb2.StandardReply:
    """Send ``Restart`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.Restart(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_stim_params_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.StimParams,
) -> common_pb2.StandardReply:
    """Send ``SetStimParams`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetStimParams(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_trigger_state_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.SetTriggerRequest,
) -> common_pb2.StandardReply:
    """Send ``SetTriggerState`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetTriggerState(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_trigger_state_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.TriggerState:
    """Send ``GetTriggerState`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetTriggerState(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def manual_stim_trigger_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.ManualStimTriggerRequest,
) -> common_pb2.StandardReply:
    """Send ``ManualStimTrigger`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.ManualStimTrigger(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def manual_stim_trigger_toggle_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.ManualStimTriggerToggleRequest,
) -> common_pb2.StandardReply:
    """Send ``ManualStimTriggerToggle`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.ManualStimTriggerToggle(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_stim_step_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.StimStepMessage,
) -> common_pb2.StandardReply:
    """Send ``SetStimStep`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetStimStep(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_stim_step_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.StimStepMessage:
    """Send ``GetStimStep`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetStimStep(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_signal_group_proto(
    pool: ChannelPool,
    addr: str,
) -> common_pb2.SignalGroup:
    """Send ``GetSignalGroup`` RPC, return raw proto response."""
    req = common_pb2.SignalGroupIDRequest()
    # Use default stream group ID if none provided, like the old client
    req.streamGroupId = PRIMARY_CACHE_STREAM_GROUP_ID

    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetSignalGroup(req)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_recording_config_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.ConfigRecording,
) -> common_pb2.StandardReply:
    """Send ``SetConfigRecording`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetConfigRecording(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_recording_config_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.ConfigRecording:
    """Send ``GetConfigRecording`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetConfigRecording(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_config_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.ConfigCore:
    """Send ``GetConfig`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetConfig(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_config_core_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.SetConfigCoreRequest,
) -> common_pb2.StandardReply:
    """Send ``SetConfigCore`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetConfigCore(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_dac_gain_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.DACGainRequest,
) -> common_pb2.StandardReply:
    """Send ``SetDACGain`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDACGain(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_dac_stream_proto(
    pool: ChannelPool,
    addr: str,
    request: common_pb2.DACStreamRequest,
) -> common_pb2.StandardReply:
    """Send ``SetDACStream`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDACStream(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_dac_off_proto(
    pool: ChannelPool,
    addr: str,
    request: common_pb2.DACOffRequest,
) -> common_pb2.StandardReply:
    """Send ``SetDACOff`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDACOff(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def set_dac_highpass_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.DACHighPassRegister,
) -> common_pb2.StandardReply:
    """Send ``SetDACHighPass`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDACHighPass(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise


# --- Digital Output (DIO) ---


def set_dio_events_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.DIOModeEventsRequest,
) -> common_pb2.StandardReply:
    """Send ``SetDIOEvents`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDIOEvents(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise


def set_dio_manual_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.DIOModeManualRequest,
) -> common_pb2.StandardReply:
    """Send ``SetDIOManual`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDIOManual(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise


def set_dio_gated_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.DIOModeGatedRequest,
) -> common_pb2.StandardReply:
    """Send ``SetDIOGated`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDIOGated(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise


def set_dio_pulse_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.DIOModePulseRequest,
) -> common_pb2.StandardReply:
    """Send ``SetDIOPulse`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDIOPulse(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise


def get_dio_reg_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.DigitalOutRegister:
    """Send ``GetDIOReg`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetDIOReg(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise


# unreachable, but satisfies mypy


def get_dac_reg_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.AnalogOutRegister:
    """Send ``GetDACReg`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetDACReg(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_dsp_group_proto(
    pool: ChannelPool,
    addr: str,
    stream_group_id: str,
) -> common_pb2.DSPGroup:
    """Send ``GetDSPGroup`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    req = common_pb2.SignalGroupIDRequest(streamGroupId=stream_group_id)
    try:
        return stub.GetDSPGroup(req)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise


def set_dsp_group_proto(
    pool: ChannelPool,
    addr: str,
    request: common_pb2.SetDSPGroupRequest,
) -> common_pb2.StandardReply:
    """Send ``SetDSPGroup`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.SetDSPGroup(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise


# --- Sinaps ---


def load_all_mosi_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.LoadAllMosiRequest,
) -> common_pb2.StandardReply:
    """Send ``LoadAllMosi`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.LoadAllMosi(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def transmit_mosi_proto(
    pool: ChannelPool,
    addr: str,
) -> common_pb2.StandardReply:
    """Send ``TransmitMosi`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.TransmitMosi(allegoserver_pb2.TransmitMosiRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def read_wire_out_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.ReadWireOutRequest,
) -> allegoserver_pb2.ReadWireOutReply:
    """Send ``ReadWireOut`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.ReadWireOut(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def get_sinaps_status_registers_proto(
    pool: ChannelPool,
    addr: str,
) -> allegoserver_pb2.SinapsStatusRegisters:
    """Send ``GetSinapsStatusRegisters`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.GetSinapsStatusRegisters(common_pb2.StandardRequest())
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy


def flash_sinaps_proto(
    pool: ChannelPool,
    addr: str,
    request: allegoserver_pb2.FlashSinapsRequest,
) -> common_pb2.StandardReply:
    """Send ``FlashSinaps`` RPC, return raw proto response."""
    channel = pool.get_channel(addr)
    stub = allegoserver_pb2_grpc.AllegoCoreStub(channel)
    try:
        return stub.FlashSinaps(request)
    except grpc.RpcError as ex:
        handle_grpc_error(ex, ClientType.ALLEGO)
        raise  # unreachable, but satisfies mypy
