"""Tests for the Jellyseerr stats provider + generic stat widget source."""

from __future__ import annotations

import asyncio
from unittest.mock import MagicMock, patch

from media_library_viewer_api.widgets.jellyseerr_stats import JellyseerrStatsProvider
from media_library_viewer_api.widgets.sources import ServiceRecord, StatsWidgetSource
from media_library_viewer_api.widgets.stats_provider import (
    StatValue,
    get_stats_provider,
)


def _service(
    *,
    jellyseerr_url: str = "https://js.example.com",
    jellyseerr_api_key: str = "key",
) -> ServiceRecord:
    return ServiceRecord(
        id="svc-jf",
        service_type="jellyfin",
        name="Jellyfin",
        config={"jellyseerr_url": jellyseerr_url, "jellyseerr_api_key": jellyseerr_api_key},
        secrets={},
        enabled=True,
    )


_COUNTS = {"total": 5, "pending": 2, "approved": 1, "declined": 0, "processing": 1, "available": 1}
_RECENT = [{"id": 1, "name": "Inception", "status": "pending", "media_status": "available"}]


def test_provider_registered_for_jellyfin():
    assert isinstance(get_stats_provider("jellyfin"), JellyseerrStatsProvider)


def test_provider_returns_normalized_stats_and_recent():
    provider = JellyseerrStatsProvider(ttl=0)
    fake = MagicMock()
    fake.request_count.return_value = _COUNTS
    fake.recent_requests.return_value = _RECENT
    with patch("media_library_viewer_api.widgets.jellyseerr_stats._jellyseer_client", return_value=fake):
        result = provider.fetch_stats(_service())
    assert [s.key for s in result.stats] == [
        "total",
        "pending",
        "approved",
        "declined",
        "processing",
        "available",
    ]
    by_key = {s.key: s.value for s in result.stats}
    assert by_key["pending"] == 2 and by_key["total"] == 5
    assert result.recent == _RECENT


def test_provider_not_configured_returns_detail():
    provider = JellyseerrStatsProvider()
    result = provider.fetch_stats(_service(jellyseerr_url="", jellyseerr_api_key=""))
    assert result.stats == []
    assert "not configured" in (result.detail or "").lower()


def test_provider_caches_within_ttl():
    provider = JellyseerrStatsProvider(ttl=10)
    fake = MagicMock()
    fake.request_count.return_value = _COUNTS
    fake.recent_requests.return_value = _RECENT
    with patch("media_library_viewer_api.widgets.jellyseerr_stats._jellyseer_client", return_value=fake):
        provider.fetch_stats(_service())
        provider.fetch_stats(_service())  # served from cache
    assert fake.request_count.call_count == 1


def test_stat_widget_returns_selected_value():
    src = StatsWidgetSource()
    result = MagicMock()
    result.detail = None
    result.stats = [StatValue("total", "Total", 5), StatValue("pending", "Pending", 2)]
    result.recent = []
    provider = MagicMock()
    provider.fetch_stats.return_value = result
    with patch("media_library_viewer_api.widgets.sources.get_stats_provider", return_value=provider):
        data = asyncio.run(src.fetch(_service(), "stat", {"stat": "pending"}))
    assert data == {"key": "pending", "label": "Pending", "value": 2}


def test_stats_overview_widget_returns_all_stats_and_recent():
    src = StatsWidgetSource()
    result = MagicMock()
    result.detail = None
    result.stats = [StatValue("total", "Total", 5), StatValue("pending", "Pending", 2)]
    result.recent = _RECENT
    provider = MagicMock()
    provider.fetch_stats.return_value = result
    with patch("media_library_viewer_api.widgets.sources.get_stats_provider", return_value=provider):
        data = asyncio.run(src.fetch(_service(), "stats_overview", {}))
    assert data["stats"] == [
        {"key": "total", "label": "Total", "value": 5},
        {"key": "pending", "label": "Pending", "value": 2},
    ]
    assert data["recent"] == _RECENT


def test_stat_widget_unknown_stat_returns_error():
    src = StatsWidgetSource()
    result = MagicMock()
    result.detail = None
    result.stats = [StatValue("total", "Total", 5)]
    result.recent = []
    provider = MagicMock()
    provider.fetch_stats.return_value = result
    with patch("media_library_viewer_api.widgets.sources.get_stats_provider", return_value=provider):
        data = asyncio.run(src.fetch(_service(), "stat", {"stat": "nope"}))
    assert "error" in data


def test_jellyseer_client_open_requests_resolves_titles():
    """open_requests() fetches pending+approved via filter and resolves titles."""
    from media_library_viewer_api.clients.jellyseerr import JellyseerrClient

    c = JellyseerrClient("https://js.example.com", "key")
    c.session = MagicMock()

    def mock_get(url, **kw):
        resp = MagicMock()
        resp.raise_for_status.return_value = None
        resp.status_code = 200
        resp.text = ""
        params = kw.get("params", {})
        if "/movie/" in url or "/tv/" in url:
            resp.json.return_value = {"title": "Inception"}  # title resolution
        elif params.get("filter") == "pending":
            resp.json.return_value = {
                "results": [
                    {
                        "id": 7,
                        "type": 1,
                        "status": 1,
                        "media": {"tmdbId": 123, "status": 5},
                        "createdAt": 1_700_000_000,
                    }
                ]
            }
        else:
            resp.json.return_value = {"results": []}
        return resp

    c.session.get.side_effect = mock_get

    out = c.open_requests()

    assert len(out) == 1
    r = out[0]
    assert r["id"] == 7
    assert r["type"] == "movie"
    assert r["name"] == "Inception"  # resolved via /movie/123
    assert r["status"] == "pending"
    assert r["media_status"] == "available"
    assert r["created_at"] == 1_700_000_000

    # Title is cached: a second call doesn't re-fetch /movie/123.
    movie_calls_before = sum(1 for call in c.session.get.call_args_list if "/movie/" in call.args[0])
    assert movie_calls_before == 1
    c.open_requests()  # second poll
    movie_calls_after = sum(1 for call in c.session.get.call_args_list if "/movie/" in call.args[0])
    assert movie_calls_after == 1  # cached, no new /movie call


def test_fetch_jellyseer_requests_not_configured_returns_empty():
    """No jellyseerr config -> empty list (tab shows 'No requests')."""
    from media_library_viewer_api.widgets.jellyseerr_stats import fetch_jellyseer_requests

    service = ServiceRecord(id="s", service_type="jellyfin", name="JF", config={}, secrets={})
    assert fetch_jellyseer_requests(service) == []
