"""Tests for exception classes."""


class TestErrorCode:
    """Tests for ErrorCode enum."""

    def test_error_code_values(self):
        """Test error code values."""
        from exceptions import ErrorCode

        assert ErrorCode.UNKNOWN_ERROR.value == "KB_UNKNOWN_ERROR"
        assert (
            ErrorCode.DATABASE_CONNECTION_ERROR.value == "KB_DATABASE_CONNECTION_ERROR"
        )
        assert (
            ErrorCode.EMBEDDING_GENERATION_ERROR.value
            == "KB_EMBEDDING_GENERATION_ERROR"
        )
        assert ErrorCode.CHUNKING_ERROR.value == "KB_CHUNKING_ERROR"
        assert ErrorCode.SEARCH_ERROR.value == "KB_SEARCH_ERROR"
        assert ErrorCode.COLLECTION_NOT_FOUND.value == "KB_COLLECTION_NOT_FOUND"
        assert ErrorCode.DOCUMENT_NOT_FOUND.value == "KB_DOCUMENT_NOT_FOUND"


class TestKnowledgeBaseError:
    """Tests for base exception class."""

    def test_basic_error(self):
        """Test basic error creation."""
        from exceptions import ErrorCode, KnowledgeBaseError

        error = KnowledgeBaseError(
            message="Something went wrong",
            code=ErrorCode.UNKNOWN_ERROR,
        )

        assert error.message == "Something went wrong"
        assert error.code == ErrorCode.UNKNOWN_ERROR
        assert error.details == {}
        assert error.cause is None

    def test_error_with_details(self):
        """Test error with details."""
        from exceptions import ErrorCode, KnowledgeBaseError

        error = KnowledgeBaseError(
            message="Query failed",
            code=ErrorCode.DATABASE_QUERY_ERROR,
            details={"query": "SELECT * FROM table", "error_code": 42},
        )

        assert error.details["query"] == "SELECT * FROM table"
        assert error.details["error_code"] == 42

    def test_error_with_cause(self):
        """Test error with underlying cause."""
        from exceptions import ErrorCode, KnowledgeBaseError

        original = ValueError("Original error")
        error = KnowledgeBaseError(
            message="Wrapped error",
            code=ErrorCode.INTERNAL_ERROR,
            cause=original,
        )

        assert error.cause is original
        assert isinstance(error.cause, ValueError)

    def test_to_dict(self):
        """Test to_dict method."""
        from exceptions import ErrorCode, KnowledgeBaseError

        error = KnowledgeBaseError(
            message="Test error",
            code=ErrorCode.INVALID_REQUEST,
            details={"field": "value"},
        )

        result = error.to_dict()

        assert result["error"] == "KB_INVALID_REQUEST"
        assert result["message"] == "Test error"
        assert result["details"]["field"] == "value"

    def test_str_representation(self):
        """Test string representation."""
        from exceptions import ErrorCode, KnowledgeBaseError

        error = KnowledgeBaseError(
            message="Test error",
            code=ErrorCode.INVALID_REQUEST,
        )

        assert str(error) == "[KB_INVALID_REQUEST] Test error"

    def test_repr_representation(self):
        """Test repr representation."""
        from exceptions import ErrorCode, KnowledgeBaseError

        error = KnowledgeBaseError(
            message="Test error",
            code=ErrorCode.INVALID_REQUEST,
            details={"key": "value"},
        )

        repr_str = repr(error)
        assert "KnowledgeBaseError" in repr_str
        assert "Test error" in repr_str
        assert "KB_INVALID_REQUEST" in repr_str


class TestDatabaseErrors:
    """Tests for database-related exceptions."""

    def test_database_connection_error(self):
        """Test database connection error."""
        from exceptions import DatabaseConnectionError, ErrorCode

        error = DatabaseConnectionError(
            message="Cannot connect to database",
            details={"host": "localhost", "port": 5432},
        )

        assert error.code == ErrorCode.DATABASE_CONNECTION_ERROR
        assert error.details["host"] == "localhost"

    def test_database_connection_error_default_message(self):
        """Test database connection error with default message."""
        from exceptions import DatabaseConnectionError

        error = DatabaseConnectionError()

        assert error.message == "Failed to connect to database"

    def test_database_query_error(self):
        """Test database query error."""
        from exceptions import DatabaseQueryError, ErrorCode

        error = DatabaseQueryError(
            message="Query failed",
            query="SELECT * FROM missing_table",
        )

        assert error.code == ErrorCode.DATABASE_QUERY_ERROR
        assert error.details["query"] == "SELECT * FROM missing_table"


class TestEmbeddingErrors:
    """Tests for embedding-related exceptions."""

    def test_embedding_generation_error(self):
        """Test embedding generation error."""
        from exceptions import EmbeddingGenerationError, ErrorCode

        error = EmbeddingGenerationError(
            message="Failed to generate",
            texts_count=10,
        )

        assert error.code == ErrorCode.EMBEDDING_GENERATION_ERROR
        assert error.details["texts_count"] == 10

    def test_embedding_dimension_mismatch(self):
        """Test embedding dimension mismatch error."""
        from exceptions import EmbeddingDimensionMismatchError, ErrorCode

        error = EmbeddingDimensionMismatchError(
            expected=1536,
            actual=768,
        )

        assert error.code == ErrorCode.EMBEDDING_DIMENSION_MISMATCH
        assert "expected 1536" in error.message
        assert "got 768" in error.message
        assert error.details["expected_dimension"] == 1536
        assert error.details["actual_dimension"] == 768


class TestChunkingErrors:
    """Tests for chunking-related exceptions."""

    def test_unsupported_file_type_error(self):
        """Test unsupported file type error."""
        from exceptions import ErrorCode, UnsupportedFileTypeError

        error = UnsupportedFileTypeError(
            file_type=".xyz",
            supported_types=[".py", ".js", ".md"],
        )

        assert error.code == ErrorCode.UNSUPPORTED_FILE_TYPE
        assert error.details["file_type"] == ".xyz"
        assert len(error.details["supported_types"]) == 3

    def test_file_too_large_error(self):
        """Test file too large error."""
        from exceptions import ErrorCode, FileTooLargeError

        error = FileTooLargeError(
            file_size=10_000_000,
            max_size=1_000_000,
        )

        assert error.code == ErrorCode.FILE_TOO_LARGE
        assert error.details["file_size"] == 10_000_000
        assert error.details["max_size"] == 1_000_000

    def test_encoding_error(self):
        """Test encoding error."""
        from exceptions import EncodingError, ErrorCode

        error = EncodingError(
            message="Cannot decode file",
            encoding="utf-8",
        )

        assert error.code == ErrorCode.ENCODING_ERROR
        assert error.details["encoding"] == "utf-8"


class TestSearchErrors:
    """Tests for search-related exceptions."""

    def test_invalid_search_type_error(self):
        """Test invalid search type error."""
        from exceptions import ErrorCode, InvalidSearchTypeError

        error = InvalidSearchTypeError(
            search_type="invalid",
            valid_types=["semantic", "keyword", "hybrid"],
        )

        assert error.code == ErrorCode.INVALID_SEARCH_TYPE
        assert error.details["search_type"] == "invalid"
        assert len(error.details["valid_types"]) == 3

    def test_search_timeout_error(self):
        """Test search timeout error."""
        from exceptions import ErrorCode, SearchTimeoutError

        error = SearchTimeoutError(timeout=30.0)

        assert error.code == ErrorCode.SEARCH_TIMEOUT
        assert error.details["timeout"] == 30.0
        assert "30" in error.message


class TestCollectionErrors:
    """Tests for collection-related exceptions."""

    def test_collection_not_found_error(self):
        """Test collection not found error."""
        from exceptions import CollectionNotFoundError, ErrorCode

        error = CollectionNotFoundError(
            collection="missing-collection",
            project_id="proj-123",
        )

        assert error.code == ErrorCode.COLLECTION_NOT_FOUND
        assert error.details["collection"] == "missing-collection"
        assert error.details["project_id"] == "proj-123"


class TestDocumentErrors:
    """Tests for document-related exceptions."""

    def test_document_not_found_error(self):
        """Test document not found error."""
        from exceptions import DocumentNotFoundError, ErrorCode

        error = DocumentNotFoundError(
            source_path="/path/to/file.py",
            project_id="proj-123",
        )

        assert error.code == ErrorCode.DOCUMENT_NOT_FOUND
        assert error.details["source_path"] == "/path/to/file.py"

    def test_invalid_document_error(self):
        """Test invalid document error."""
        from exceptions import ErrorCode, InvalidDocumentError

        error = InvalidDocumentError(
            message="Empty content",
            details={"reason": "no content"},
        )

        assert error.code == ErrorCode.INVALID_DOCUMENT


class TestProjectErrors:
    """Tests for project-related exceptions."""

    def test_project_not_found_error(self):
        """Test project not found error."""
        from exceptions import ErrorCode, ProjectNotFoundError

        error = ProjectNotFoundError(project_id="missing-proj")

        assert error.code == ErrorCode.PROJECT_NOT_FOUND
        assert error.details["project_id"] == "missing-proj"

    def test_project_access_denied_error(self):
        """Test project access denied error."""
        from exceptions import ErrorCode, ProjectAccessDeniedError

        error = ProjectAccessDeniedError(project_id="restricted-proj")

        assert error.code == ErrorCode.PROJECT_ACCESS_DENIED
        assert "restricted-proj" in error.message
