"""Tests for the retrieval module."""

from __future__ import annotations

from datetime import datetime, timezone
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from bandito.retrieval import (
    ProjectStats,
    RetrievalLayer,
    ScopedRetrieval,
    get_retrieval,
    reset_retrieval,
)


class TestProjectStats:
    def test_default_values(self):
        stats = ProjectStats()
        assert stats.total_traces == 0
        assert stats.total_cost == 0.0
        assert stats.avg_cost == 0.0
        assert stats.span_kinds == {}
        assert stats.daily_counts == []

    def test_with_values(self):
        stats = ProjectStats(
            total_traces=100,
            total_cost=1.5,
            avg_cost=0.015,
            span_kinds={"llm": 50, "tool": 30},
            daily_counts=[10, 20, 15, 25, 30, 5, 0],
        )
        assert stats.total_traces == 100
        assert stats.total_cost == 1.5
        assert stats.span_kinds == {"llm": 50, "tool": 30}


class TestScopedRetrieval:
    def test_inherits_since(self):
        reset_retrieval()
        mock_retrieval = MagicMock()
        since = datetime(2026, 1, 1, tzinfo=timezone.utc)
        scoped = ScopedRetrieval(mock_retrieval, since)

        scoped.traces(tag="test")

        mock_retrieval.traces.assert_called_once_with(tag="test", since=since)

    def test_since_not_overwritten_if_provided(self):
        reset_retrieval()
        mock_retrieval = MagicMock()
        since = datetime(2026, 1, 1, tzinfo=timezone.utc)
        other_since = datetime(2026, 6, 1, tzinfo=timezone.utc)
        scoped = ScopedRetrieval(mock_retrieval, since)

        scoped.traces(tag="test", since=other_since)

        mock_retrieval.traces.assert_called_once_with(tag="test", since=other_since)


class TestRetrievalLayer:
    def test_singleton_per_project(self):
        reset_retrieval()

        r1 = get_retrieval("test-project")
        r2 = get_retrieval("test-project")
        r3 = get_retrieval("other-project")

        assert r1 is r2
        assert r1 is not r3

    def test_with_time_window(self):
        reset_retrieval()
        r = RetrievalLayer("test-project")
        scoped = r.with_time_window(days=14)

        assert isinstance(scoped, ScopedRetrieval)

    def test_refresh_clears_cache(self):
        reset_retrieval()
        r = RetrievalLayer("test-project")
        mock_cache = MagicMock()
        r._cache = mock_cache

        r.refresh()

        mock_cache.invalidate_project.assert_called_once_with("test-project")

    def test_invalidate_clears_cache(self):
        reset_retrieval()
        r = RetrievalLayer("test-project")
        mock_cache = MagicMock()
        r._cache = mock_cache

        r.invalidate()

        mock_cache.invalidate_project.assert_called_once_with("test-project")


class TestRetrievalIntegration:
    def test_traces_uses_cache(self, tmp_path: Path):
        reset_retrieval()

        from bandito.cache import DiskCache

        cache = DiskCache(cache_dir=tmp_path)
        r = RetrievalLayer("test-project", cache=cache)

        mock_store = MagicMock()
        mock_store.list_trace_summaries.return_value = [
            {"trace_id": "t1", "project": "test-project"}
        ]
        r._store = mock_store

        traces1 = r.traces(tier="summary")
        assert len(traces1) == 1
        assert mock_store.list_trace_summaries.call_count == 1

        traces2 = r.traces(tier="summary")
        assert len(traces2) == 1
        assert mock_store.list_trace_summaries.call_count == 1

    def test_evals_falls_back_to_empty_on_no_client(self):
        reset_retrieval()
        r = RetrievalLayer("test-project")
        r._client = None

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