"""Tests for REST API endpoints."""

import pytest
from unittest.mock import MagicMock, AsyncMock, patch

from httpx import AsyncClient, ASGITransport


@pytest.fixture
def mock_pipeline_instance():
    """Create a mock Rooh matching the actual server interface."""
    p = MagicMock()
    # Rooh methods are async
    p.transcribe = AsyncMock(return_value="hello world")
    p.chat = AsyncMock(return_value="Hi there!")
    p.synthesize = AsyncMock(return_value=b"RIFF" + b"\x00" * 40)
    p.process_audio = AsyncMock(return_value=("hello world", "Hi there!", b"RIFF" + b"\x00" * 40))
    # swap_model is sync (called via asyncio.to_thread)
    p.swap_model = MagicMock()
    # Health/models endpoints access these directly
    p._stt_name = "whisper-tiny"
    p._tts_name = "piper"
    p._llm_name = "bedrock-claude"
    return p


@pytest.fixture
async def client(mock_pipeline_instance):
    """Create an async test client with mocked pipeline."""
    import server.app as app_module

    original_pipeline = app_module.pipeline
    app_module.pipeline = mock_pipeline_instance

    transport = ASGITransport(app=app_module.app)
    async with AsyncClient(transport=transport, base_url="http://test") as ac:
        yield ac

    app_module.pipeline = original_pipeline


class TestHealthEndpoint:
    async def test_health_returns_ok(self, client):
        resp = await client.get("/api/health")
        assert resp.status_code == 200
        data = resp.json()
        assert data["status"] == "ok"
        assert data["models"]["stt"] == "whisper-tiny"
        assert data["models"]["tts"] == "piper"
        assert data["models"]["llm"] == "bedrock-claude"


class TestModelsEndpoint:
    async def test_list_models(self, client):
        with patch("server.app.registry") as mock_reg:
            mock_reg.list_stt.return_value = ["whisper-tiny", "wav2vec2"]
            mock_reg.list_tts.return_value = ["piper", "speecht5", "bark"]
            mock_reg.list_llm.return_value = ["bedrock-claude"]
            resp = await client.get("/api/models")

        assert resp.status_code == 200
        data = resp.json()
        assert data["active_stt"] == "whisper-tiny"
        assert "whisper-tiny" in data["stt"]

    async def test_swap_model(self, client, mock_pipeline_instance):
        resp = await client.post(
            "/api/models/swap",
            json={"model_type": "stt", "model_name": "wav2vec2"},
        )
        assert resp.status_code == 200
        data = resp.json()
        assert data["status"] == "ok"
        assert data["model_type"] == "stt"
        assert data["model_name"] == "wav2vec2"
        mock_pipeline_instance.swap_model.assert_called_once_with("stt", "wav2vec2")


class TestDocsEndpoints:
    async def test_docs_page_renders(self, client):
        resp = await client.get("/guide")
        assert resp.status_code == 200
        assert "RoohAI" in resp.text
        assert "sidebar" in resp.text

    async def test_docs_specific_page(self, client):
        resp = await client.get("/guide/configuration")
        assert resp.status_code == 200
        assert "Configuration" in resp.text

    async def test_docs_search_returns_results(self, client):
        resp = await client.get("/api/docs/search?q=pipeline")
        assert resp.status_code == 200
        data = resp.json()
        assert "results" in data
        assert len(data["results"]) > 0
        assert "page" in data["results"][0]
        assert "snippet" in data["results"][0]

    async def test_docs_search_short_query(self, client):
        resp = await client.get("/api/docs/search?q=a")
        assert resp.status_code == 200
        assert resp.json()["results"] == []

    async def test_docs_list(self, client):
        resp = await client.get("/api/docs")
        assert resp.status_code == 200
        data = resp.json()
        assert "pages" in data
        assert any(p["slug"] == "index" for p in data["pages"])
