"""Test StashDB connectora — paginate, delta-by-updated, error handling."""
from __future__ import annotations

import json
from datetime import UTC, datetime
from pathlib import Path

import httpx
import pytest
import respx

from app.connectors.stashdb import StashDBConnector


@pytest.fixture
def stashdb_raw() -> dict:
    return json.loads(
        (Path(__file__).parent / "fixtures" / "stashdb_scene.json").read_text()
    )


def _gql_response(scenes: list[dict]) -> dict:
    return {"data": {"queryScenes": {"count": len(scenes), "scenes": scenes}}}


def test_paginates_until_short_page(stashdb_raw: dict) -> None:
    s2 = {**stashdb_raw, "id": "scene-2", "title": "Two", "updated": "2026-04-29T11:00:00Z"}
    s3 = {**stashdb_raw, "id": "scene-3", "title": "Three", "updated": "2026-04-29T10:00:00Z"}

    with respx.mock(assert_all_called=True) as mock:
        route = mock.post("https://stashdb.org/graphql").mock(
            side_effect=[
                httpx.Response(200, json=_gql_response([stashdb_raw, s2])),
                httpx.Response(200, json=_gql_response([s3])),
            ]
        )
        c = StashDBConnector(api_key="k", per_page=2)
        results = list(c.fetch_scenes())
        assert [r.title for r in results] == ["The Great Heist", "Two", "Three"]
        assert route.call_count == 2


def test_stops_when_updated_before_since(stashdb_raw: dict) -> None:
    fresh = {**stashdb_raw, "updated": "2026-04-30T12:00:00Z"}
    stale = {**stashdb_raw, "id": "scene-stale", "updated": "2026-04-01T12:00:00Z"}
    with respx.mock() as mock:
        mock.post("https://stashdb.org/graphql").mock(
            return_value=httpx.Response(200, json=_gql_response([fresh, stale]))
        )
        c = StashDBConnector(api_key="k")
        since = datetime(2026, 4, 15, tzinfo=UTC)
        results = list(c.fetch_scenes(since=since))
        assert len(results) == 1
        assert results[0].external_id == fresh["id"]


def test_respects_limit(stashdb_raw: dict) -> None:
    s2 = {**stashdb_raw, "id": "scene-2", "title": "Two"}
    with respx.mock() as mock:
        mock.post("https://stashdb.org/graphql").mock(
            return_value=httpx.Response(200, json=_gql_response([stashdb_raw, s2]))
        )
        c = StashDBConnector(api_key="k", per_page=10)
        results = list(c.fetch_scenes(limit=1))
        assert len(results) == 1


def test_raises_on_graphql_errors() -> None:
    with respx.mock() as mock:
        mock.post("https://stashdb.org/graphql").mock(
            return_value=httpx.Response(
                200, json={"errors": [{"message": "unauthorized"}]}
            )
        )
        c = StashDBConnector(api_key="k")
        with pytest.raises(RuntimeError, match="stashdb graphql errors"):
            list(c.fetch_scenes())


def test_api_key_required(monkeypatch: pytest.MonkeyPatch) -> None:
    from app.config import get_settings
    monkeypatch.setenv("STASHDB_API_KEY", "")
    get_settings.cache_clear()
    with pytest.raises(RuntimeError, match="STASHDB_API_KEY"):
        StashDBConnector(api_key=None)
    get_settings.cache_clear()
