from __future__ import annotations

from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager

import pytest

from src.services import stats_service as stats_service_module
from src.services.stats_service import StatsService


class _NoopRedis:
    async def get(self, key: str) -> None:
        return None

    async def set(self, key: str, value: str, ex: int) -> None:
        return None


class _EmptyResult:
    def all(self) -> list:
        return []


class _RowsResult:
    def __init__(self, rows: list[tuple]) -> None:
        self._rows = rows

    def all(self) -> list[tuple]:
        return self._rows


class _FakeDb:
    async def execute(self, statement) -> _EmptyResult:
        return _EmptyResult()


class _QueuedDb:
    def __init__(self, results: list[_RowsResult]) -> None:
        self._results = results

    async def execute(self, statement) -> _RowsResult:
        return self._results.pop(0)


@pytest.mark.asyncio
async def test_stats_service_uses_default_project_for_db_context(monkeypatch) -> None:
    seen_project_ids: list[str | None] = []

    @asynccontextmanager
    async def fake_db_context(project_id: str | None = None) -> AsyncGenerator[_FakeDb, None]:
        seen_project_ids.append(project_id)
        yield _FakeDb()

    monkeypatch.setattr(stats_service_module, "get_db_context", fake_db_context)
    monkeypatch.setattr(stats_service_module, "get_redis", lambda: _NoopRedis())

    result = await StatsService().get_resolution_stats(days=7)

    assert result["sample_size"] == 0
    assert seen_project_ids == ["awoooi"]


@pytest.mark.asyncio
async def test_stats_service_allows_explicit_project_for_db_context(monkeypatch) -> None:
    seen_project_ids: list[str | None] = []
    seen_cache_keys: list[str] = []

    @asynccontextmanager
    async def fake_db_context(project_id: str | None = None) -> AsyncGenerator[_FakeDb, None]:
        seen_project_ids.append(project_id)
        yield _FakeDb()

    class TrackingRedis(_NoopRedis):
        async def get(self, key: str) -> None:
            seen_cache_keys.append(key)
            return None

    monkeypatch.setattr(stats_service_module, "get_db_context", fake_db_context)
    monkeypatch.setattr(stats_service_module, "get_redis", lambda: TrackingRedis())

    result = await StatsService().get_ai_performance(days=7, project_id="vibework")

    assert result["total_proposals"] == 0
    assert seen_project_ids == ["vibework"]
    assert seen_cache_keys == ["stats:vibework:ai_performance:7"]


@pytest.mark.asyncio
async def test_ai_performance_uses_auto_repair_execution_truth(monkeypatch) -> None:
    seen_project_ids: list[str | None] = []
    db = _QueuedDb(
        [
            _RowsResult([]),
            _RowsResult([(True, 1200), (False, 800), (True, 500)]),
        ]
    )

    @asynccontextmanager
    async def fake_db_context(project_id: str | None = None) -> AsyncGenerator[_QueuedDb, None]:
        seen_project_ids.append(project_id)
        yield db

    monkeypatch.setattr(stats_service_module, "get_db_context", fake_db_context)
    monkeypatch.setattr(stats_service_module, "get_redis", lambda: _NoopRedis())

    result = await StatsService().get_ai_performance(days=7)

    assert seen_project_ids == ["awoooi"]
    assert result["total_proposals"] == 3
    assert result["executed_count"] == 3
    assert result["success_count"] == 2
    assert result["success_rate"] == 66.67
    assert result["auto_repair_total"] == 3
    assert result["auto_repair_success"] == 2
    assert result["source"] == "auto_repair_executions+incident_outcome"
