"""Integration tests for storage → API → retrieval flow for evaluations.

These tests verify the full data flow:
1. Write evaluation via BanditoClient -> API
2. Read evaluation via RetrievalLayer -> API

Key issues being tested:
- Cache should not return stale empty results
- No client should return empty dict
- Write should be visible to subsequent reads
"""

from __future__ import annotations

import pytest
from unittest.mock import MagicMock, patch

from bandito.cli.api import BanditoClient


class _FakeResponse:
    """Minimal httpx.Response stand-in."""

    def __init__(self, status_code, json_data=None, text=""):
        self.status_code = status_code
        self._json = json_data
        self.text = text

    def json(self):
        return self._json


class TestWriteThenRead:
    """Core test: write eval via client, read via retrieval layer."""

    def test_write_and_immediately_read(self):
        """Write one eval, then read it back."""
        client = BanditoClient(api_key="test-key")

        fake_write = _FakeResponse(201, {
            "id": 1,
            "project": "test-project",
            "trace_id": "trace-abc",
            "human_score": 1,
        })

        fake_read = _FakeResponse(200, {
            "evaluations": {
                "trace-abc": {
                    "id": 1,
                    "project": "test-project",
                    "trace_id": "trace-abc",
                    "human_score": 1,
                }
            }
        })

        with patch("httpx.request", side_effect=[fake_write, fake_read]):
            write_result = client.write_evaluation({
                "project": "test-project",
                "trace_id": "trace-abc",
                "human_score": 1,
            })
            assert write_result["human_score"] == 1

            from bandito.cli.retrieval import RetrievalLayer

            r = RetrievalLayer(
                "test-project",
                use_cache=False,
            )
            evals = r.evals()

            assert "trace-abc" in evals
            assert evals["trace-abc"]["human_score"] == 1


class TestCacheBehavior:
    """Test caching logic for evaluations."""

    def test_empty_cache_result_not_returned_on_subsequent_calls(self):
        """BUG: If cache has empty result, subsequent call should still hit API.

        Current behavior: returns cached empty dict {}
        Expected: should fetch fresh from API
        """
        from bandito.cli.retrieval import RetrievalLayer
        from bandito.cli.cache import DiskCache

        cache = MagicMock(spec=DiskCache)
        cache.get.return_value = {}  # Simulate stale empty cached result

        r = RetrievalLayer("test-project", cache=cache, use_cache=True)

        mock_client = MagicMock()
        mock_client.list_evaluations.return_value = {
            "trace-1": {"trace_id": "trace-1", "human_score": 1}
        }

        with patch.object(r, "_get_client", return_value=mock_client):
            evals = r.evals()

        mock_client.list_evaluations.assert_called_once()

    def test_cache_only_set_when_results_exist(self):
        """Cache should only store non-empty results."""
        from bandito.cli.retrieval import RetrievalLayer
        from bandito.cli.cache import DiskCache

        cache = MagicMock(spec=DiskCache)
        cache.get.return_value = None  # Cache miss

        r = RetrievalLayer("test-project", cache=cache, use_cache=True)

        mock_client = MagicMock()
        mock_client.list_evaluations.return_value = {"trace-1": {"human_score": 1}}

        with patch.object(r, "_get_client", return_value=mock_client):
            r.evals()

        cache.set.assert_called_once()


class TestNoClientFallback:
    """Test behavior when API client is unavailable."""

    def test_evals_empty_when_no_client(self):
        """When BanditoClient can't be created, evals returns {}"""
        from bandito.cli.retrieval import RetrievalLayer

        r = RetrievalLayer("test-project", use_cache=False)

        r._client = None

        evals = r.evals()
        assert evals == {}


class TestScopingEvals:
    """Test ScopedRetrieval passes through evals correctly."""

    def test_scoped_evals_passes_since_param(self):
        """ScopedRetrieval should pass through since parameter."""
        from datetime import datetime, timezone
        from bandito.cli.retrieval import RetrievalLayer, ScopedRetrieval

        r = RetrievalLayer("test-project", use_cache=False)
        mock_client = MagicMock()
        mock_client.list_evaluations.return_value = {}
        r._client = mock_client

        since = datetime(2026, 1, 1, tzinfo=timezone.utc)
        scoped = r.with_time_window(days=7)

        scoped.evals()

        mock_client.list_evaluations.assert_called_once()
        call_kwargs = mock_client.list_evaluations.call_args
        assert call_kwargs.kwargs.get("since") is not None
