"""Timestamped audio ring buffer implementation.

Store recent audio with timestamps for ASR processing and playback sync.
"""

from __future__ import annotations

from collections import deque
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from noteflow.infrastructure.audio.dto import TimestampedAudio


class TimestampedRingBuffer:
    """Ring buffer for timestamped audio chunks.

    Automatically discard old audio when the buffer exceeds max_duration.
    Thread-safe for single-producer, single-consumer use.
    """

    def __init__(self, max_duration: float = 30.0) -> None:
        """Initialize ring buffer.

        Args:
            max_duration: Maximum audio duration to keep in seconds.

        Raises:
            ValueError: If max_duration is not positive.
        """
        if max_duration <= 0:
            raise ValueError("max_duration must be positive")

        self._max_duration = max_duration
        self._buffer: deque[TimestampedAudio] = deque()
        self._total_duration: float = 0.0

    def push(self, audio: TimestampedAudio) -> None:
        """Add audio to the buffer.

        Old audio is discarded if buffer exceeds max_duration.

        Args:
            audio: Timestamped audio chunk to add.
        """
        self._buffer.append(audio)
        self._total_duration += audio.duration

        # Evict old chunks if over capacity
        while self._total_duration > self._max_duration and self._buffer:
            old = self._buffer.popleft()
            self._total_duration -= old.duration

    def get_window(self, duration_seconds: float) -> list[TimestampedAudio]:
        """Get the last N seconds of audio.

        Args:
            duration_seconds: How many seconds of audio to retrieve.

        Returns:
            List of TimestampedAudio chunks, ordered oldest to newest.
        """
        if duration_seconds <= 0:
            return []

        result: list[TimestampedAudio] = []
        accumulated_duration = 0.0

        # Iterate from newest to oldest
        for audio in reversed(self._buffer):
            result.append(audio)
            accumulated_duration += audio.duration
            if accumulated_duration >= duration_seconds:
                break

        # Return in chronological order (oldest first)
        result.reverse()
        return result

    def get_all(self) -> list[TimestampedAudio]:
        """Get all buffered audio.

        Returns:
            List of all TimestampedAudio chunks, ordered oldest to newest.
        """
        buffer = self._buffer
        return list(buffer)

    def clear(self) -> None:
        """Clear all audio from the buffer."""
        self._buffer.clear()
        self._total_duration = 0.0

    @property
    def duration(self) -> float:
        """Total duration of buffered audio in seconds."""
        return self._total_duration

    @property
    def max_duration(self) -> float:
        """Maximum buffer duration in seconds."""
        return self._max_duration

    @property
    def chunk_count(self) -> int:
        """Number of audio chunks currently in the buffer."""
        buffer = self._buffer
        return len(buffer)

    def __len__(self) -> int:
        """Return number of chunks in buffer."""
        return len(self._buffer)
