"""Stream API route coverage."""

from __future__ import annotations

from http import HTTPStatus
from types import SimpleNamespace
from typing import TYPE_CHECKING, cast
from unittest.mock import AsyncMock, MagicMock
from uuid import UUID, uuid4

import pytest
import pytest_asyncio
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient
from roboco.api.deps import get_agent_context, get_current_agent_id, get_db
from roboco.api.routes.stream import router as stream_router
from roboco.db.tables import AgentTable
from roboco.models import AgentRole, AgentStatus, MessageType, Team
from roboco.models.permissions import AgentContext

if TYPE_CHECKING:
    from collections.abc import AsyncIterator

    from sqlalchemy.ext.asyncio import AsyncSession


@pytest_asyncio.fixture
async def stream_client(
    db_session: AsyncSession,
) -> AsyncIterator[dict]:
    agent = AgentTable(
        id=uuid4(),
        name="Dev",
        slug=f"be-dev-{uuid4().hex[:8]}",
        role=AgentRole.DEVELOPER,
        team=Team.BACKEND,
        status=AgentStatus.ACTIVE,
        model_config={},
        system_prompt="dev",
        capabilities=[],
        permissions={},
        metrics={},
    )
    db_session.add(agent)
    await db_session.flush()

    app = FastAPI()
    # Init transcription/extraction state attributes (None by default)
    app.state.transcription = None
    app.state.extraction = None
    app.include_router(stream_router, prefix="/api/stream")

    async def _override_db() -> AsyncIterator[AsyncSession]:
        yield db_session

    async def _override_agent_id() -> UUID:
        return cast("UUID", agent.id)

    async def _override_agent() -> AgentContext:
        return AgentContext(
            agent_id=cast("UUID", agent.id), role=AgentRole.DEVELOPER, team=Team.BACKEND
        )

    app.dependency_overrides[get_db] = _override_db
    app.dependency_overrides[get_current_agent_id] = _override_agent_id
    app.dependency_overrides[get_agent_context] = _override_agent

    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://test") as client:
        yield {"client": client, "agent": agent, "app": app}
    app.dependency_overrides.clear()


_HDR = {"X-Agent-ID": str(uuid4()), "X-Agent-Role": "developer"}


# ---------------------------------------------------------------------------
# /chunk
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_chunk_no_transcription(stream_client: dict) -> None:
    response = await stream_client["client"].post(
        "/api/stream/chunk",
        json={
            "channel_id": str(uuid4()),
            "session_id": str(uuid4()),
            "chunk": "hello",
        },
        headers=_HDR,
    )
    assert response.status_code == HTTPStatus.SERVICE_UNAVAILABLE


@pytest.mark.asyncio
async def test_chunk_success(stream_client: dict) -> None:
    transcription = AsyncMock()
    buffer = SimpleNamespace(char_count=10)
    transcription.process_chunk = AsyncMock(return_value=buffer)
    stream_client["app"].state.transcription = transcription
    response = await stream_client["client"].post(
        "/api/stream/chunk",
        json={
            "channel_id": str(uuid4()),
            "session_id": str(uuid4()),
            "chunk": "hello",
        },
        headers=_HDR,
    )
    assert response.status_code == HTTPStatus.ACCEPTED
    body = response.json()
    assert body["ready_for_extraction"] is True


@pytest.mark.asyncio
async def test_chunk_no_buffer(stream_client: dict) -> None:
    transcription = AsyncMock()
    transcription.process_chunk = AsyncMock(return_value=None)
    stream_client["app"].state.transcription = transcription
    response = await stream_client["client"].post(
        "/api/stream/chunk",
        json={
            "channel_id": str(uuid4()),
            "session_id": str(uuid4()),
            "chunk": "hi",
        },
        headers=_HDR,
    )
    assert response.status_code == HTTPStatus.ACCEPTED
    assert response.json()["buffer_size"] == 0


# ---------------------------------------------------------------------------
# /complete
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_complete_no_transcription(stream_client: dict) -> None:
    response = await stream_client["client"].post(
        "/api/stream/complete",
        json={"session_id": str(uuid4())},
        headers=_HDR,
    )
    assert response.status_code == HTTPStatus.SERVICE_UNAVAILABLE


@pytest.mark.asyncio
async def test_complete_success(stream_client: dict) -> None:
    transcription = AsyncMock()
    transcription.process_stream_complete = AsyncMock(return_value=SimpleNamespace())
    transcription.flush_buffer = AsyncMock(return_value="some content")
    stream_client["app"].state.transcription = transcription
    response = await stream_client["client"].post(
        "/api/stream/complete",
        json={"session_id": str(uuid4())},
        headers=_HDR,
    )
    assert response.status_code == HTTPStatus.OK
    assert response.json()["status"] == "completed"


@pytest.mark.asyncio
async def test_complete_no_buffer(stream_client: dict) -> None:
    transcription = AsyncMock()
    transcription.process_stream_complete = AsyncMock(return_value=None)
    stream_client["app"].state.transcription = transcription
    response = await stream_client["client"].post(
        "/api/stream/complete",
        json={"session_id": str(uuid4())},
        headers=_HDR,
    )
    assert response.status_code == HTTPStatus.OK
    assert response.json()["status"] == "no_content"


# ---------------------------------------------------------------------------
# /extract
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_extract_no_extraction(stream_client: dict) -> None:
    response = await stream_client["client"].post(
        "/api/stream/extract",
        json={
            "channel_id": str(uuid4()),
            "session_id": str(uuid4()),
            "group_id": str(uuid4()),
            "content": "x",
        },
        headers=_HDR,
    )
    assert response.status_code == HTTPStatus.SERVICE_UNAVAILABLE


@pytest.mark.asyncio
async def test_extract_success(stream_client: dict) -> None:
    extraction = AsyncMock()
    msg = SimpleNamespace(
        id=uuid4(),
        type=MessageType.DIALOGUE,
        content="hi",
        content_length=2,
        confidence=0.99,
    )
    extraction.process_buffer = AsyncMock(
        return_value=SimpleNamespace(
            message_count=1,
            messages=[msg],
            types_extracted=[MessageType.DIALOGUE],
        )
    )
    stream_client["app"].state.extraction = extraction
    response = await stream_client["client"].post(
        "/api/stream/extract",
        json={
            "channel_id": str(uuid4()),
            "session_id": str(uuid4()),
            "group_id": str(uuid4()),
            "content": "x",
        },
        headers=_HDR,
    )
    assert response.status_code == HTTPStatus.OK


# ---------------------------------------------------------------------------
# /stats
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_stats_no_transcription(stream_client: dict) -> None:
    response = await stream_client["client"].get("/api/stream/stats", headers=_HDR)
    assert response.status_code == HTTPStatus.SERVICE_UNAVAILABLE


@pytest.mark.asyncio
async def test_stats_success(stream_client: dict) -> None:
    transcription = MagicMock()
    transcription.get_stats = MagicMock(
        return_value={
            "active_agents": 0,
            "total_buffers": 0,
            "total_buffered_chars": 0,
            "running": True,
        }
    )
    stream_client["app"].state.transcription = transcription
    response = await stream_client["client"].get("/api/stream/stats", headers=_HDR)
    assert response.status_code == HTTPStatus.OK


# ---------------------------------------------------------------------------
# /permissions
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_get_my_permissions(stream_client: dict) -> None:
    response = await stream_client["client"].get(
        "/api/stream/permissions", headers=_HDR
    )
    assert response.status_code == HTTPStatus.OK


@pytest.mark.asyncio
async def test_check_channel_permission(stream_client: dict) -> None:
    response = await stream_client["client"].get(
        "/api/stream/permissions/channel/backend-cell", headers=_HDR
    )
    assert response.status_code == HTTPStatus.OK
    body = response.json()
    assert "can_read" in body
    assert "can_write" in body
