"""Contract tests for Engine protocol.

These tests verify that Engine implementations satisfy the architectural requirements:
- createSession() returns EngineSession
- dispose() is idempotent
- After dispose(), createSession() raises EngineError
- Engine is thread-safe for createSession()
"""

import pytest

from abogen.tts_plugin.engine import Engine, EngineSession
from abogen.tts_plugin.errors import EngineError

from .conftest import FakeEngine, FakeEngineSession


class TestEngineProtocolContract:
    """Contract tests for the Engine protocol itself."""

    def test_engine_is_protocol(self) -> None:
        assert hasattr(Engine, "__protocol_attrs__")

    def test_engine_session_is_protocol(self) -> None:
        assert hasattr(EngineSession, "__protocol_attrs__")

    def test_fake_engine_satisfies_protocol(self) -> None:
        engine = FakeEngine()
        assert isinstance(engine, Engine)

    def test_fake_session_satisfies_protocol(self) -> None:
        session = FakeEngineSession()
        assert isinstance(session, EngineSession)


class TestEngineCreateSessionContract:
    """Contract tests for Engine.createSession()."""

    def test_create_session_returns_engine_session(self) -> None:
        engine = FakeEngine()
        session = engine.createSession()
        assert isinstance(session, EngineSession)

    def test_create_session_returns_new_instance(self) -> None:
        engine = FakeEngine()
        session1 = engine.createSession()
        session2 = engine.createSession()
        assert session1 is not session2

    def test_create_session_ownership_transfers(self) -> None:
        """Architecture spec: Ownership transfers to caller."""
        engine = FakeEngine()
        session = engine.createSession()
        assert isinstance(session, EngineSession)


class TestEngineDisposeContract:
    """Contract tests for Engine.dispose()."""

    def test_dispose_is_idempotent(self) -> None:
        """Architecture spec: dispose() is idempotent."""
        engine = FakeEngine()
        engine.dispose()
        engine.dispose()  # Should not raise

    def test_dispose_never_raises(self) -> None:
        """Architecture spec: dispose() never raises exceptions."""
        engine = FakeEngine()
        engine.dispose()  # Should not raise

    def test_create_session_after_dispose_raises(self) -> None:
        """Architecture spec: After dispose(), all methods except dispose() raise EngineError."""
        engine = FakeEngine()
        engine.dispose()
        with pytest.raises(EngineError):
            engine.createSession()


class TestEngineLifecycleContract:
    """Contract tests for Engine lifecycle."""

    def test_full_lifecycle(self) -> None:
        """Test complete engine lifecycle: create -> sessions -> dispose."""
        engine = FakeEngine()

        # Create sessions
        session1 = engine.createSession()
        session2 = engine.createSession()

        # Use sessions
        assert isinstance(session1, EngineSession)
        assert isinstance(session2, EngineSession)

        # Dispose sessions
        session1.dispose()
        session2.dispose()

        # Dispose engine
        engine.dispose()

    def test_engine_disposed_session_raises(self) -> None:
        """Architecture spec: After dispose(), all methods except dispose() raise EngineError."""
        engine = FakeEngine()
        engine.dispose()
        with pytest.raises(EngineError):
            engine.createSession()
