"""Scheduling metadata and receiver de-duplication for command copies.

Lab037 deliberately keeps the Lab033 :class:`~protocol.link_packet.LinkPacket`
wire representation unchanged.  A repeated command is therefore the exact
same encoded packet; only ``available_time_us`` and ``copy_index`` live in the
local scheduler metadata defined here.
"""

from __future__ import annotations

from dataclasses import dataclass
from enum import IntEnum
from typing import Iterable

from protocol.link_packet import LinkPacket, TrafficClass


class RepetitionMode(IntEnum):
    """Command protection alternatives compared by Lab037."""

    NONE = 1
    EMERGENCY_ONLY = 2
    EMERGENCY_AND_CONTROL = 3


@dataclass(frozen=True)
class CommandCopy:
    """One scheduling instance of an otherwise unchanged link packet."""

    packet: LinkPacket
    available_time_us: int
    copy_index: int
    arrival_order: int

    @property
    def is_repeat(self) -> bool:
        return self.copy_index > 0

    @property
    def command_key(self) -> tuple[int, int]:
        return self.packet.stream_id, self.packet.sequence_number


def copy_offsets_ms(
    traffic_class: TrafficClass,
    mode: RepetitionMode,
    control_repeat_delay_ms: float = 22.5,
) -> tuple[float, ...]:
    """Return copy offsets for a command class and protection mode."""

    traffic_class = TrafficClass(traffic_class)
    mode = RepetitionMode(mode)
    if traffic_class is TrafficClass.EMERGENCY:
        return (0.0, 15.0, 30.0) if mode >= RepetitionMode.EMERGENCY_ONLY else (0.0,)
    if traffic_class is TrafficClass.CONTROL:
        return (0.0, control_repeat_delay_ms) if mode is RepetitionMode.EMERGENCY_AND_CONTROL else (0.0,)
    raise ValueError("repetition is defined only for emergency and control commands")


def build_command_copies(
    packets: Iterable[LinkPacket],
    mode: RepetitionMode,
    *,
    first_arrival_order: int = 0,
    control_repeat_delay_ms: float = 22.5,
) -> tuple[CommandCopy, ...]:
    """Expand original commands into stable, time-ordered scheduler copies."""

    copies: list[CommandCopy] = []
    order = first_arrival_order
    for packet in packets:
        offsets = copy_offsets_ms(packet.traffic_class, mode, control_repeat_delay_ms)
        for copy_index, offset_ms in enumerate(offsets):
            copies.append(
                CommandCopy(
                    packet=packet,
                    available_time_us=packet.generation_time_us + int(round(offset_ms * 1000.0)),
                    copy_index=copy_index,
                    arrival_order=order,
                )
            )
            order += 1
    return tuple(
        sorted(
            copies,
            key=lambda item: (
                item.available_time_us,
                int(item.packet.traffic_class),
                item.arrival_order,
            ),
        )
    )


def cancel_superseded_control_copies(
    ready: Iterable[CommandCopy],
    newest: CommandCopy,
) -> tuple[tuple[CommandCopy, ...], tuple[CommandCopy, ...]]:
    """Remove only queued repeats belonging to older state commands.

    A packet already selected for transmission is intentionally outside this
    function, which makes packet serialization non-preemptive by construction.
    Primary copies and emergency commands are never removed here.
    """

    if newest.packet.traffic_class is not TrafficClass.CONTROL:
        return tuple(ready), ()
    retained: list[CommandCopy] = []
    cancelled: list[CommandCopy] = []
    for item in ready:
        if (
            item.packet.traffic_class is TrafficClass.CONTROL
            and item.packet.stream_id == newest.packet.stream_id
            and item.packet.sequence_number < newest.packet.sequence_number
            and item.is_repeat
        ):
            cancelled.append(item)
        else:
            retained.append(item)
    return tuple(retained), tuple(cancelled)


class CommandReceiver:
    """Apply only strictly newer sequence numbers independently per stream."""

    def __init__(self) -> None:
        self._last_sequence: dict[int, int] = {}
        self.accepted = 0
        self.suppressed = 0

    def accept(self, packet: LinkPacket) -> bool:
        """Return ``True`` only when this command advances its stream state."""

        if packet.traffic_class not in (TrafficClass.EMERGENCY, TrafficClass.CONTROL):
            raise ValueError("CommandReceiver accepts command traffic only")
        previous = self._last_sequence.get(packet.stream_id, -1)
        if packet.sequence_number <= previous:
            self.suppressed += 1
            return False
        self._last_sequence[packet.stream_id] = packet.sequence_number
        self.accepted += 1
        return True

    def last_sequence(self, stream_id: int) -> int | None:
        return self._last_sequence.get(stream_id)
