"""Tests for context management exceptions."""

from app.services.context.exceptions import (
    AssemblyTimeoutError,
    BudgetExceededError,
    CacheError,
    CompressionError,
    ContextError,
    ContextNotFoundError,
    FormattingError,
    InvalidContextError,
    ScoringError,
    TokenCountError,
)


class TestContextError:
    """Tests for base ContextError."""

    def test_basic_initialization(self) -> None:
        """Test basic error initialization."""
        error = ContextError("Test error")
        assert error.message == "Test error"
        assert error.details == {}
        assert str(error) == "Test error"

    def test_with_details(self) -> None:
        """Test error with details."""
        error = ContextError("Test error", {"key": "value", "count": 42})
        assert error.details == {"key": "value", "count": 42}

    def test_to_dict(self) -> None:
        """Test conversion to dictionary."""
        error = ContextError("Test error", {"key": "value"})
        result = error.to_dict()

        assert result["error_type"] == "ContextError"
        assert result["message"] == "Test error"
        assert result["details"] == {"key": "value"}

    def test_inheritance(self) -> None:
        """Test that ContextError inherits from Exception."""
        error = ContextError("Test")
        assert isinstance(error, Exception)


class TestBudgetExceededError:
    """Tests for BudgetExceededError."""

    def test_default_message(self) -> None:
        """Test default error message."""
        error = BudgetExceededError()
        assert "exceeded" in error.message.lower()

    def test_with_budget_info(self) -> None:
        """Test with budget information."""
        error = BudgetExceededError(
            allocated=1000,
            requested=1500,
            context_type="knowledge",
        )

        assert error.allocated == 1000
        assert error.requested == 1500
        assert error.context_type == "knowledge"
        assert error.details["overage"] == 500

    def test_to_dict_includes_budget_info(self) -> None:
        """Test that to_dict includes budget info."""
        error = BudgetExceededError(
            allocated=1000,
            requested=1500,
        )
        result = error.to_dict()

        assert result["details"]["allocated"] == 1000
        assert result["details"]["requested"] == 1500
        assert result["details"]["overage"] == 500


class TestTokenCountError:
    """Tests for TokenCountError."""

    def test_basic_error(self) -> None:
        """Test basic token count error."""
        error = TokenCountError()
        assert "token" in error.message.lower()

    def test_with_model_info(self) -> None:
        """Test with model information."""
        error = TokenCountError(
            message="Failed to count",
            model="claude-3-sonnet",
            text_length=5000,
        )

        assert error.model == "claude-3-sonnet"
        assert error.text_length == 5000
        assert error.details["model"] == "claude-3-sonnet"


class TestCompressionError:
    """Tests for CompressionError."""

    def test_basic_error(self) -> None:
        """Test basic compression error."""
        error = CompressionError()
        assert "compress" in error.message.lower()

    def test_with_token_info(self) -> None:
        """Test with token information."""
        error = CompressionError(
            original_tokens=2000,
            target_tokens=1000,
            achieved_tokens=1500,
        )

        assert error.original_tokens == 2000
        assert error.target_tokens == 1000
        assert error.achieved_tokens == 1500


class TestAssemblyTimeoutError:
    """Tests for AssemblyTimeoutError."""

    def test_basic_error(self) -> None:
        """Test basic timeout error."""
        error = AssemblyTimeoutError()
        assert "timed out" in error.message.lower()

    def test_with_timing_info(self) -> None:
        """Test with timing information."""
        error = AssemblyTimeoutError(
            timeout_ms=100,
            elapsed_ms=150.5,
            stage="scoring",
        )

        assert error.timeout_ms == 100
        assert error.elapsed_ms == 150.5
        assert error.stage == "scoring"
        assert error.details["stage"] == "scoring"


class TestScoringError:
    """Tests for ScoringError."""

    def test_basic_error(self) -> None:
        """Test basic scoring error."""
        error = ScoringError()
        assert "score" in error.message.lower()

    def test_with_scorer_info(self) -> None:
        """Test with scorer information."""
        error = ScoringError(
            scorer_type="relevance",
            context_id="ctx-123",
        )

        assert error.scorer_type == "relevance"
        assert error.context_id == "ctx-123"


class TestFormattingError:
    """Tests for FormattingError."""

    def test_basic_error(self) -> None:
        """Test basic formatting error."""
        error = FormattingError()
        assert "format" in error.message.lower()

    def test_with_model_info(self) -> None:
        """Test with model information."""
        error = FormattingError(
            model="claude-3-opus",
            adapter="ClaudeAdapter",
        )

        assert error.model == "claude-3-opus"
        assert error.adapter == "ClaudeAdapter"


class TestCacheError:
    """Tests for CacheError."""

    def test_basic_error(self) -> None:
        """Test basic cache error."""
        error = CacheError()
        assert "cache" in error.message.lower()

    def test_with_operation_info(self) -> None:
        """Test with operation information."""
        error = CacheError(
            operation="get",
            cache_key="ctx:abc123",
        )

        assert error.operation == "get"
        assert error.cache_key == "ctx:abc123"


class TestContextNotFoundError:
    """Tests for ContextNotFoundError."""

    def test_basic_error(self) -> None:
        """Test basic not found error."""
        error = ContextNotFoundError()
        assert "not found" in error.message.lower()

    def test_with_source_info(self) -> None:
        """Test with source information."""
        error = ContextNotFoundError(
            source="knowledge-base",
            query="authentication flow",
        )

        assert error.source == "knowledge-base"
        assert error.query == "authentication flow"


class TestInvalidContextError:
    """Tests for InvalidContextError."""

    def test_basic_error(self) -> None:
        """Test basic invalid error."""
        error = InvalidContextError()
        assert "invalid" in error.message.lower()

    def test_with_field_info(self) -> None:
        """Test with field information."""
        error = InvalidContextError(
            field="content",
            value="",
            reason="Content cannot be empty",
        )

        assert error.field == "content"
        assert error.value == ""
        assert error.reason == "Content cannot be empty"

    def test_value_type_only_in_details(self) -> None:
        """Test that only value type is included in details (not actual value)."""
        error = InvalidContextError(
            field="api_key",
            value="secret-key-here",
        )

        # Actual value should not be in details
        assert "secret-key-here" not in str(error.details)
        assert error.details["value_type"] == "str"
