import pytest

from backend.app.models import Artist, DownloadEvent, Source

pytestmark = pytest.mark.integration


@pytest.fixture
async def seed(db):
    artist = Artist(name="Alice", slug="alice")
    db.add(artist)
    await db.flush()
    source = Source(
        artist_id=artist.id, platform="patreon",
        url="https://patreon.com/alice", enabled=True,
        config_overrides={},
    )
    db.add(source)
    await db.commit()
    return source


@pytest.mark.asyncio
async def test_check_202_creates_pending_event(client, seed, monkeypatch):
    monkeypatch.setattr(
        "backend.app.tasks.download.download_source.delay",
        lambda *a, **k: None,
    )
    resp = await client.post(f"/api/sources/{seed.id}/check")
    assert resp.status_code == 202
    body = await resp.get_json()
    assert body["status"] == "pending"
    assert isinstance(body["download_event_id"], int)


@pytest.mark.asyncio
async def test_check_404_for_unknown_source(client):
    resp = await client.post("/api/sources/99999/check")
    assert resp.status_code == 404


@pytest.mark.asyncio
async def test_check_400_for_disabled_source(client, db, seed):
    seed.enabled = False
    await db.commit()
    resp = await client.post(f"/api/sources/{seed.id}/check")
    assert resp.status_code == 400
    body = await resp.get_json()
    assert body["error"] == "source_disabled"


@pytest.mark.asyncio
async def test_check_409_when_already_running(client, db, seed, monkeypatch):
    monkeypatch.setattr(
        "backend.app.tasks.download.download_source.delay",
        lambda *a, **k: None,
    )
    existing = DownloadEvent(source_id=seed.id, status="running")
    db.add(existing)
    await db.commit()
    await db.refresh(existing)
    resp = await client.post(f"/api/sources/{seed.id}/check")
    assert resp.status_code == 409
    body = await resp.get_json()
    assert body["download_event_id"] == existing.id


@pytest.mark.asyncio
async def test_check_defers_when_platform_in_cooldown(client, db, seed, monkeypatch):
    """Bulk retries land here without ?force — when the source's platform
    has an active cooldown, return 202 with status='deferred' instead of
    creating an event. Lets the bulk path tally deferred-vs-queued in
    the toast so the operator can see rate-limit-induced failures at a
    glance."""
    from backend.app.services.scheduler_service import set_platform_cooldown

    delays: list = []
    monkeypatch.setattr(
        "backend.app.tasks.download.download_source.delay",
        lambda *a, **k: delays.append(a),
    )

    await set_platform_cooldown(db, seed.platform, seconds=900)
    await db.commit()

    resp = await client.post(f"/api/sources/{seed.id}/check")
    assert resp.status_code == 202
    body = await resp.get_json()
    assert body["status"] == "deferred"
    assert body["platform"] == seed.platform
    assert body["cooldown_until"]
    assert delays == []  # no dispatch when deferred


@pytest.mark.asyncio
async def test_check_force_overrides_cooldown(client, db, seed, monkeypatch):
    """Single-source RETRY click sends ?force=true — explicit operator
    override, used for rapid auth-fix testing without waiting on the
    cooldown."""
    from backend.app.services.scheduler_service import set_platform_cooldown

    delays: list = []
    monkeypatch.setattr(
        "backend.app.tasks.download.download_source.delay",
        lambda *a, **k: delays.append(a),
    )

    await set_platform_cooldown(db, seed.platform, seconds=900)
    await db.commit()

    resp = await client.post(f"/api/sources/{seed.id}/check?force=true")
    assert resp.status_code == 202
    body = await resp.get_json()
    assert body["status"] == "pending"
    assert isinstance(body["download_event_id"], int)
    assert len(delays) == 1
    assert delays[0] == (seed.id,)
