"""Tests for LLM Ledger."""

import pytest
from llm_ledger import (
    LedgerClient,
    LLMRequest,
    ProvenanceMetadata,
    InMemoryCache
)


@pytest.fixture
def client():
    """Create test client with in-memory cache."""
    return LedgerClient(
        cache_backend=InMemoryCache(),
        enable_cache=True,
        enable_persistence=False  # Use in-memory for tests
    )


def test_cache_key_generation(client):
    """Test deterministic cache key generation."""
    request1 = LLMRequest(
        model="claude-sonnet-4",
        messages=[{"role": "user", "content": "Hello"}],
        temperature=0.0
    )
    
    request2 = LLMRequest(
        model="claude-sonnet-4",
        messages=[{"role": "user", "content": "Hello"}],
        temperature=0.0
    )
    
    key1 = client.cache.generate_cache_key(request1)
    key2 = client.cache.generate_cache_key(request2)
    
    assert key1 == key2, "Identical requests should generate same cache key"


def test_cache_key_different_for_different_params(client):
    """Test that different parameters generate different cache keys."""
    request1 = LLMRequest(
        model="claude-sonnet-4",
        messages=[{"role": "user", "content": "Hello"}],
        temperature=0.0
    )
    
    request2 = LLMRequest(
        model="claude-sonnet-4",
        messages=[{"role": "user", "content": "Hello"}],
        temperature=0.5
    )
    
    key1 = client.cache.generate_cache_key(request1)
    key2 = client.cache.generate_cache_key(request2)
    
    assert key1 != key2, "Different parameters should generate different cache keys"


def test_provenance_metadata():
    """Test provenance metadata creation."""
    metadata = ProvenanceMetadata(
        workflow_id="test_workflow",
        chunk_id="chunk_1",
        section_id="section_1",
        character_range=(100, 200),
        tags={"key": "value"}
    )
    
    assert metadata.workflow_id == "test_workflow"
    assert metadata.chunk_id == "chunk_1"
    assert metadata.character_range == (100, 200)
    assert metadata.tags["key"] == "value"


def test_request_normalization():
    """Test request normalization for caching."""
    request = LLMRequest(
        model="claude-sonnet-4",
        messages=[{"role": "user", "content": "  Hello   World  "}],
        temperature=0.0
    )
    
    normalized = request.normalize_for_cache_key()
    assert "Hello World" in normalized
    assert "  " not in normalized


def test_fluent_builder(client):
    """Test fluent builder pattern."""
    builder = (
        client.completion()
        .model("claude-sonnet-4")
        .system("Test system")
        .user("Test user")
        .temperature(0.5)
        .max_tokens(100)
        .with_metadata(workflow_id="test")
    )
    
    request = builder.build()
    
    assert request.model == "claude-sonnet-4"
    assert request.temperature == 0.5
    assert request.max_tokens == 100
    assert len(request.messages) == 2
    assert request.metadata.workflow_id == "test"


def test_cache_statistics(client):
    """Test cache statistics tracking."""
    initial_stats = client.cache.stats
    assert initial_stats["hits"] == 0
    assert initial_stats["misses"] == 0
    
    # This would need mocking of LiteLLM in real tests
    # For now, just test the stats structure
    assert "hit_rate" in initial_stats
    assert "total_requests" in initial_stats


@pytest.mark.asyncio
async def test_async_interface(client):
    """Test async interface (requires mocking)."""
    # This would need mocking in real tests
    request = LLMRequest(
        model="claude-sonnet-4",
        messages=[{"role": "user", "content": "Test"}],
        temperature=0.0
    )
    
    # Would execute: response = await client.complete_async(request)
    # assert response.content is not None


def test_token_usage_calculation():
    """Test token usage and cost estimation."""
    from llm_ledger.models import TokenUsage
    
    usage = TokenUsage(
        prompt_tokens=100,
        completion_tokens=50,
        total_tokens=150
    )
    
    assert usage.total_tokens == 150
    assert usage.cost_estimate > 0  # Should calculate some cost


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
