"""Tests for the ring buffer."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

from curadise_agent.transport.buffer import RingBuffer

if TYPE_CHECKING:
    from curadise_agent.models.domain.metric import DomainMetric


class TestRingBuffer:
    """Tests for RingBuffer."""

    @pytest.fixture
    def buffer(self) -> RingBuffer[int]:
        return RingBuffer(max_size=5)

    async def test_add_and_get(self, buffer: RingBuffer[int]) -> None:
        """Test basic add and get operations."""
        await buffer.add(1)
        await buffer.add(2)
        await buffer.add(3)

        assert buffer.size == 3
        assert await buffer.get() == 1
        assert await buffer.get() == 2
        assert await buffer.get() == 3
        assert buffer.size == 0

    async def test_overflow_drops_oldest(self, buffer: RingBuffer[int]) -> None:
        """Test that overflow drops oldest items."""
        for i in range(7):
            await buffer.add(i)

        # Buffer should contain 2, 3, 4, 5, 6 (oldest 0, 1 dropped)
        assert buffer.size == 5
        stats = buffer.stats()
        assert stats.total_dropped == 2

        # First item should be 2
        assert await buffer.get() == 2

    async def test_get_batch(self, buffer: RingBuffer[int]) -> None:
        """Test batch retrieval."""
        for i in range(5):
            await buffer.add(i)

        batch = await buffer.get_batch(3)
        assert batch == [0, 1, 2]
        assert buffer.size == 2

    async def test_peek(self, buffer: RingBuffer[int]) -> None:
        """Test peeking without removal."""
        await buffer.add(1)
        await buffer.add(2)

        assert await buffer.peek() == 1
        assert buffer.size == 2  # Size unchanged

    async def test_clear(self, buffer: RingBuffer[int]) -> None:
        """Test clearing the buffer."""
        for i in range(3):
            await buffer.add(i)

        cleared = await buffer.clear()
        assert cleared == 3
        assert buffer.is_empty

    async def test_stats(self, buffer: RingBuffer[int]) -> None:
        """Test statistics tracking."""
        for i in range(7):
            await buffer.add(i)

        await buffer.get()
        await buffer.get()

        stats = buffer.stats()
        assert stats.current_size == 3
        assert stats.max_size == 5
        assert stats.total_added == 7
        assert stats.total_removed == 2
        assert stats.total_dropped == 2

    async def test_empty_buffer_returns_none(self, buffer: RingBuffer[int]) -> None:
        """Test that empty buffer returns None."""
        assert await buffer.get() is None
        assert await buffer.peek() is None

    async def test_add_many(self, buffer: RingBuffer[int]) -> None:
        """Test adding multiple items at once."""
        dropped = await buffer.add_many([1, 2, 3, 4, 5, 6, 7])

        assert dropped == 2
        assert buffer.size == 5


class TestRingBufferWithMetrics:
    """Tests for RingBuffer with DomainMetric type."""

    @pytest.fixture
    def buffer(self) -> RingBuffer[DomainMetric]:
        return RingBuffer(max_size=10)

    async def test_with_metrics(
        self,
        buffer: RingBuffer[DomainMetric],
        sample_metrics: list[DomainMetric],
    ) -> None:
        """Test buffer with metric objects."""
        for metric in sample_metrics:
            await buffer.add(metric)

        assert buffer.size == 10

        retrieved = await buffer.get()
        assert retrieved is not None
        assert retrieved.name == "test.metric.0"
