"""
BIBIVIDI — Feedback tests (TODO-10)
Tests for request ID tracking, flag_bad(), list_flagged(), and the
X-BIBIVIDI-Request-ID response header.
"""
from __future__ import annotations

from unittest.mock import patch

import pytest

# ── Stats / DB tests ──────────────────────────────────────────────────────────

async def _make_library(tmp_path):
    """Return an initialised in-memory Library for the given tmp_path."""
    from bvd.engine.library import Library
    with patch("bvd.config.settings.db_path", tmp_path / "test.db"):
        lib = Library()
        await lib.init()
        return lib


@pytest.mark.asyncio
async def test_flag_bad_returns_skill_id(tmp_path):
    """flag_bad() returns the skill_id of the matched execution."""
    from bvd.engine.stats import StatsTracker

    with patch("bvd.config.settings.db_path", tmp_path / "test.db"):
        lib = await _make_library(tmp_path)
        tracker = StatsTracker()

        await tracker.record(
            script_id="my-skill",
            tokens_before=1000,
            tokens_after=100,
            request_id="req-abc-123",
        )

        result = await tracker.flag_bad("req-abc-123")
        assert result == "my-skill"
        await lib.close()


@pytest.mark.asyncio
async def test_flag_bad_unknown_id_returns_none(tmp_path):
    """flag_bad() returns None for an unknown request_id."""
    from bvd.engine.stats import StatsTracker

    with patch("bvd.config.settings.db_path", tmp_path / "test.db"):
        await _make_library(tmp_path)
        tracker = StatsTracker()

        result = await tracker.flag_bad("does-not-exist")
        assert result is None


@pytest.mark.asyncio
async def test_list_flagged_shows_bad_executions(tmp_path):
    """list_flagged() returns only executions flagged as bad."""
    from bvd.engine.stats import StatsTracker

    with patch("bvd.config.settings.db_path", tmp_path / "test.db"):
        lib = await _make_library(tmp_path)
        tracker = StatsTracker()

        await tracker.record("skill-a", 500, 50, request_id="req-1")
        await tracker.record("skill-b", 800, 80, request_id="req-2")
        await tracker.record("skill-a", 300, 30, request_id="req-3")

        await tracker.flag_bad("req-1")
        await tracker.flag_bad("req-3")

        flagged = await tracker.list_flagged()
        ids = [r["request_id"] for r in flagged]
        assert "req-1" in ids
        assert "req-3" in ids
        assert "req-2" not in ids  # not flagged
        await lib.close()


@pytest.mark.asyncio
async def test_list_flagged_empty_when_none_flagged(tmp_path):
    """list_flagged() returns empty list when nothing is flagged."""
    from bvd.engine.stats import StatsTracker

    with patch("bvd.config.settings.db_path", tmp_path / "test.db"):
        await _make_library(tmp_path)
        tracker = StatsTracker()

        await tracker.record("skill-a", 500, 50, request_id="req-1")
        flagged = await tracker.list_flagged()
        assert flagged == []


@pytest.mark.asyncio
async def test_record_without_request_id(tmp_path):
    """record() without request_id still works (request_id is optional)."""
    from bvd.engine.stats import StatsTracker

    with patch("bvd.config.settings.db_path", tmp_path / "test.db"):
        await _make_library(tmp_path)
        tracker = StatsTracker()

        # Should not raise
        await tracker.record("skill-a", 500, 50)
        flagged = await tracker.list_flagged()
        assert flagged == []


# ── Proxy response header tests ───────────────────────────────────────────────

@pytest.mark.asyncio
async def test_proxy_adds_request_id_header():
    """Every proxied response includes X-BIBIVIDI-Request-ID."""
    from unittest.mock import AsyncMock, MagicMock
    from unittest.mock import patch as _patch

    import httpx
    from fastapi.testclient import TestClient

    from bvd.core.proxy import app

    mock_response = httpx.Response(
        200,
        content=b'{"choices":[]}',
        headers={"content-type": "application/json"},
        request=httpx.Request("POST", "https://api.openai.com/v1/chat/completions"),
    )

    with (
        _patch("bvd.engine.library.library.init", AsyncMock()),
        _patch("bvd.engine.matcher.matcher.match", AsyncMock(return_value=None)),
        _patch("bvd.engine.generator.generator.maybe_generate", AsyncMock()),
        _patch("bvd.engine.stats.stats_tracker.record", AsyncMock()),
        _patch("bvd.core.proxy.get_client") as mock_client_fn,
        _patch("bvd.notifier.digest_scheduler", AsyncMock()),
    ):
        mock_client = MagicMock()
        mock_client.is_closed = False
        mock_client.request = AsyncMock(return_value=mock_response)
        mock_client_fn.return_value = mock_client

        client = TestClient(app, raise_server_exceptions=False)
        resp = client.post(
            "/v1/chat/completions",
            json={"model": "gpt-4", "messages": [{"role": "user", "content": "hello"}]},
            headers={"Host": "api.openai.com"},
        )

    assert "x-bibividi-request-id" in resp.headers
    rid = resp.headers["x-bibividi-request-id"]
    # Must be a valid UUID4 (format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
    import re
    assert re.fullmatch(
        r"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}",
        rid,
    ), f"Not a UUID4: {rid!r}"
