from collections import deque
from dataclasses import dataclass
import threading
import time

from vna_system.core.config import SWEEP_BUFFER_MAX_SIZE
from vna_system.core.logging.logger import get_component_logger

logger = get_component_logger(__file__)

Point = tuple[float, float]  # (real, imag)


@dataclass(slots=True, frozen=True)
class SweepData:
    """
    Immutable container for a single sweep with metadata.

    Attributes
    ----------
    sweep_number:
        Monotonically increasing identifier for the sweep.
    timestamp:
        UNIX timestamp (seconds since epoch) when the sweep was stored.
    points:
        Sequence of complex-valued points represented as (real, imag) tuples.
    total_points:
        Cached number of points in `points` for quick access.
    """
    sweep_number: int
    timestamp: float
    points: list[Point]
    total_points: int


class SweepBuffer:
    """
    Thread-safe circular buffer for sweep data.

    Parameters
    ----------
    max_size:
        Maximum number of sweeps to retain. Old entries are discarded when the
        buffer exceeds this size.
    initial_sweep_number:
        Starting value for the internal sweep counter.
    """

    def __init__(self, max_size: int = SWEEP_BUFFER_MAX_SIZE, initial_sweep_number: int = 0) -> None:
        self._buffer: deque[SweepData] = deque(maxlen=max_size)
        self._lock = threading.RLock()
        self._sweep_counter = initial_sweep_number
        logger.debug("SweepBuffer initialized", max_size=max_size, initial_sweep_number=initial_sweep_number)

    # ------------------------------
    # Introspection utilities
    # ------------------------------
    @property
    def current_sweep_number(self) -> int:
        """Return the last assigned sweep number (0 if none were added yet)."""
        with self._lock:
            logger.debug("Current sweep number retrieved", sweep_number=self._sweep_counter)
            return self._sweep_counter

    # ------------------------------
    # Core API
    # ------------------------------
    def add_sweep(self, points: list[Point], timestamp: float | None = None) -> int:
        """
        Add a new sweep to the buffer.

        Parameters
        ----------
        points:
            Sequence of (real, imag) tuples representing a sweep.
        timestamp:
            Optional UNIX timestamp. If None, current time is used.

        Returns
        -------
        int
            The assigned sweep number for the newly added sweep.
        """
        if timestamp is None:
            timestamp = time.time()
        with self._lock:
            self._sweep_counter += 1
            sweep = SweepData(
                sweep_number=self._sweep_counter,
                timestamp=timestamp,
                points=list(points),  # ensure we store our own list
                total_points=len(points),
            )
            self._buffer.append(sweep)
            logger.debug(
                "New sweep added",
                sweep_number=sweep.sweep_number,
                total_points=sweep.total_points,
                buffer_size=len(self._buffer),
            )
            return self._sweep_counter

    def get_latest_sweep(self) -> SweepData | None:
        """Return the most recent sweep, or None if the buffer is empty."""
        with self._lock:
            sweep = self._buffer[-1] if self._buffer else None
            # if sweep: # TOO NOISY
            #     logger.debug("Latest sweep retrieved", sweep_number=sweep.sweep_number)
            # else:
            #     logger.debug("Latest sweep requested but buffer is empty")
            return sweep
