"""Tests for LLMEngine interface.

Updated to use the unified LLMEngine architecture.
BaseEngine and EngineFactory have been removed (issue #24).
"""

from __future__ import annotations

import pytest

from sagellm_core import LLMEngine, LLMEngineConfig
from sagellm_core.health import HealthStatus
from sagellm_protocol import (
    Request,
)


def test_llm_engine_interface() -> None:
    """Test LLMEngine has all required methods."""
    required_methods = [
        "start",
        "stop",
        "execute",
        "stream",
        "generate",
        "health_check",
        # Task2 interfaces (issue #8)
        "capabilities",
        "get_kv_cache",
        "set_scheduler",
    ]

    for method in required_methods:
        assert hasattr(LLMEngine, method), f"LLMEngine missing method: {method}"


def test_health_status_enum() -> None:
    """Test HealthStatus enum."""
    assert HealthStatus.HEALTHY.name == "HEALTHY"
    assert HealthStatus.DEGRADED.name == "DEGRADED"
    assert HealthStatus.UNHEALTHY.name == "UNHEALTHY"


def test_llm_engine_config_validation() -> None:
    """Test LLMEngineConfig validation."""
    # Missing model_path should raise ValueError
    with pytest.raises(ValueError, match="model_path"):
        LLMEngineConfig(model_path="")

    # Invalid backend_type should raise ValueError
    config = LLMEngineConfig(model_path="test-model", backend_type="invalid")
    with pytest.raises(ValueError, match="Unknown backend"):
        LLMEngine(config)


def test_llm_engine_valid_backends() -> None:
    """Test LLMEngine accepts valid backend types."""
    valid_backends = ["cpu", "cuda", "ascend", "auto"]
    for backend in valid_backends:
        config = LLMEngineConfig(model_path="test-model", backend_type=backend)
        engine = LLMEngine(config)
        assert engine is not None


@pytest.mark.asyncio
async def test_engine_start_stop() -> None:
    """Test engine start and stop lifecycle."""
    config = LLMEngineConfig(model_path="sshleifer/tiny-gpt2", backend_type="cpu")
    engine = LLMEngine(config=config)

    # Initially not running
    assert not engine.is_running

    # Start engine
    await engine.start()
    assert engine.is_running

    # Health check should return True when running
    health = await engine.health_check()
    assert health is True

    # Stop engine
    await engine.stop()
    assert not engine.is_running


@pytest.mark.asyncio
async def test_engine_execute() -> None:
    """Test engine execute method."""
    config = LLMEngineConfig(model_path="sshleifer/tiny-gpt2", backend_type="cpu")
    engine = LLMEngine(config=config)
    await engine.start()

    request = Request(
        request_id="test-001",
        trace_id="trace-001",
        model="test-model",
        prompt="Hello",
        max_tokens=10,
        stream=False,
    )

    response = await engine.execute(request)

    assert response.request_id == "test-001"
    assert response.trace_id == "trace-001"
    assert response.output_text is not None
    assert len(response.output_tokens) > 0
    assert response.metrics is not None

    await engine.stop()


@pytest.mark.asyncio
@pytest.mark.timeout(180)  # 3 minutes timeout
async def test_engine_stream() -> None:
    """Test engine streaming."""
    config = LLMEngineConfig(model_path="sshleifer/tiny-gpt2", backend_type="cpu")
    engine = LLMEngine(config=config)
    await engine.start()

    request = Request(
        request_id="test-002",
        trace_id="trace-002",
        model="test-model",
        prompt="Hello",
        max_tokens=10,
        stream=True,
    )

    events = []
    async for event in engine.stream(request):
        events.append(event)

    assert len(events) > 0
    assert events[0].event == "start"
    assert events[-1].event == "end"

    delta_count = sum(1 for e in events if e.event == "delta")
    assert delta_count > 0

    await engine.stop()


@pytest.mark.asyncio
async def test_engine_execute_before_start() -> None:
    """Test that execute fails when engine not started."""
    config = LLMEngineConfig(model_path="sshleifer/tiny-gpt2", backend_type="cpu")
    engine = LLMEngine(config=config)

    # Engine is not started
    assert not engine.is_running

    request = Request(
        request_id="test-003",
        trace_id="trace-003",
        model="test-model",
        prompt="Hello",
        max_tokens=10,
        stream=False,
    )

    # Should raise RuntimeError
    with pytest.raises(RuntimeError, match="not running"):
        await engine.execute(request)
