"""Tests for usage tracking and reporting."""

import pytest
import asyncio
from httpx import HTTPXMock

from vikdata import VikdataClient
from vikdata.usage import UsageReporter
from vikdata.models import UsageMetrics


@pytest.mark.asyncio
async def test_usage_reporter_initialization():
    """Test usage reporter initialization."""
    client = VikdataClient(base_url="http://localhost:8000", api_key="test-key")

    reporter = UsageReporter(
        client=client,
        service="test-service",
        batch_size=50,
        flush_interval=30.0,
    )

    assert reporter.service == "test-service"
    assert reporter.batch_size == 50
    assert reporter.flush_interval == 30.0
    assert len(reporter._buffer) == 0
    assert reporter._task is None
    assert reporter._running is False


@pytest.mark.asyncio
async def test_usage_reporter_record_single():
    """Test recording a single usage metric."""
    client = VikdataClient(base_url="http://localhost:8000", api_key="test-key")
    reporter = UsageReporter(client=client, service="test-service")

    await reporter.record(
        tenant_id="tenant-123",
        endpoint="/api/v1/vessels",
        method="GET",
        latency_ms=45.2,
        status_code=200,
    )

    # Should be in buffer
    assert len(reporter._buffer) > 0


@pytest.mark.asyncio
async def test_usage_reporter_batch_flush(httpx_mock: HTTPXMock):
    """Test that reporter flushes after batch size is reached."""
    httpx_mock.add_response(
        url="http://localhost:8000/api/v1/usage/report",
        method="POST",
        status_code=204,
    )

    async with VikdataClient(base_url="http://localhost:8000", api_key="test-key") as client:
        reporter = UsageReporter(client=client, service="test-service", batch_size=3)

        # Record 3 metrics - should trigger flush
        for i in range(3):
            await reporter.record(
                tenant_id="tenant-123",
                endpoint=f"/api/v1/endpoint{i}",
                method="GET",
            )

        # Give it a moment to flush
        await asyncio.sleep(0.1)

        # Buffer should be empty after flush
        assert len(reporter._buffer) == 0


@pytest.mark.asyncio
async def test_usage_reporter_manual_flush(httpx_mock: HTTPXMock):
    """Test manual flush of usage metrics."""
    httpx_mock.add_response(
        url="http://localhost:8000/api/v1/usage/report",
        method="POST",
        status_code=204,
    )

    async with VikdataClient(base_url="http://localhost:8000", api_key="test-key") as client:
        reporter = UsageReporter(client=client, service="test-service", batch_size=100)

        await reporter.record(
            tenant_id="tenant-123",
            endpoint="/api/v1/vessels",
            method="GET",
            latency_ms=45.2,
            status_code=200,
        )

        assert len(reporter._buffer) > 0

        await reporter.flush()

        assert len(reporter._buffer) == 0


@pytest.mark.asyncio
async def test_usage_reporter_aggregates_same_endpoint():
    """Test that reporter aggregates metrics for the same endpoint."""
    client = VikdataClient(base_url="http://localhost:8000", api_key="test-key")
    reporter = UsageReporter(client=client, service="test-service")

    # Record same endpoint multiple times
    for _ in range(5):
        await reporter.record(
            tenant_id="tenant-123",
            endpoint="/api/v1/vessels",
            method="GET",
            latency_ms=50.0,
        )

    # Should have aggregated into one entry
    key = "tenant-123:/api/v1/vessels:GET"
    assert key in reporter._buffer
    assert reporter._buffer[key]["count"] == 5


@pytest.mark.asyncio
async def test_usage_reporter_calculates_average_latency():
    """Test that reporter calculates average latency."""
    client = VikdataClient(base_url="http://localhost:8000", api_key="test-key")
    reporter = UsageReporter(client=client, service="test-service")

    latencies = [10.0, 20.0, 30.0]
    for latency in latencies:
        await reporter.record(
            tenant_id="tenant-123",
            endpoint="/api/v1/vessels",
            method="GET",
            latency_ms=latency,
        )

    key = "tenant-123:/api/v1/vessels:GET"
    assert len(reporter._buffer[key]["latencies"]) == 3
    avg = sum(reporter._buffer[key]["latencies"]) / len(reporter._buffer[key]["latencies"])
    assert avg == 20.0


@pytest.mark.asyncio
async def test_usage_reporter_start_stop():
    """Test starting and stopping usage reporter."""
    client = VikdataClient(base_url="http://localhost:8000", api_key="test-key")
    reporter = UsageReporter(client=client, service="test-service", flush_interval=1.0)

    await reporter.start()
    assert reporter._running is True
    assert reporter._task is not None

    await reporter.stop()
    assert reporter._running is False


@pytest.mark.asyncio
async def test_usage_reporter_flush_on_network_error():
    """Test that reporter preserves buffer on network error."""
    httpx_mock = HTTPXMock()
    httpx_mock.add_exception(Exception("Network error"))

    client = VikdataClient(base_url="http://localhost:8000", api_key="test-key")
    reporter = UsageReporter(client=client, service="test-service")

    await reporter.record(
        tenant_id="tenant-123",
        endpoint="/api/v1/vessels",
        method="GET",
    )

    # Flush should fail silently and preserve buffer
    try:
        await reporter.flush()
    except Exception:
        pass

    # Buffer should still contain the data
    assert len(reporter._buffer) > 0


@pytest.mark.asyncio
async def test_usage_reporter_empty_buffer_flush():
    """Test that flushing empty buffer does nothing."""
    client = VikdataClient(base_url="http://localhost:8000", api_key="test-key")
    reporter = UsageReporter(client=client, service="test-service")

    # Should not raise an exception
    await reporter.flush()
    assert len(reporter._buffer) == 0
