import pytest

from backend.app.models import AppSetting

pytestmark = pytest.mark.integration


_NETSCAPE = (
    "# Netscape HTTP Cookie File\n"
    ".patreon.com\tTRUE\t/\tTRUE\t1700000000\tsession_id\tabc\n"
)


@pytest.fixture
async def ext_key(db):
    # Seed an extension API key for tests that want to assert the
    # X-Extension-Key path explicitly.
    db.add(AppSetting(key="extension_api_key", value="test-ext-key"))
    await db.commit()
    return "test-ext-key"


@pytest.mark.asyncio
async def test_list_empty(client):
    resp = await client.get("/api/credentials")
    assert resp.status_code == 200
    assert await resp.get_json() == []


@pytest.mark.asyncio
async def test_create_and_get(client):
    resp = await client.post("/api/credentials", json={
        "platform": "patreon",
        "credential_type": "cookies",
        "data": _NETSCAPE,
    })
    assert resp.status_code == 201
    body = await resp.get_json()
    assert body["platform"] == "patreon"
    assert body["credential_type"] == "cookies"
    assert "data" not in body  # never echoed
    assert "encrypted_blob" not in body

    one = await client.get("/api/credentials/patreon")
    assert one.status_code == 200
    assert (await one.get_json())["platform"] == "patreon"


@pytest.mark.asyncio
async def test_create_updates_existing(client):
    a = await client.post("/api/credentials", json={
        "platform": "patreon", "credential_type": "cookies", "data": _NETSCAPE,
    })
    assert a.status_code == 201
    b = await client.post("/api/credentials", json={
        "platform": "patreon", "credential_type": "cookies", "data": _NETSCAPE + "x",
    })
    # On update, our convention is 200 not 201
    assert b.status_code == 200
    listing = await (await client.get("/api/credentials")).get_json()
    assert len(listing) == 1


@pytest.mark.asyncio
async def test_unknown_platform_400(client):
    resp = await client.post("/api/credentials", json={
        "platform": "fanbox", "credential_type": "cookies", "data": "x",
    })
    assert resp.status_code == 400
    assert (await resp.get_json())["error"] == "unknown_platform"


@pytest.mark.asyncio
async def test_wrong_auth_type_400(client):
    resp = await client.post("/api/credentials", json={
        "platform": "discord", "credential_type": "cookies", "data": "x",
    })
    assert resp.status_code == 400
    body = await resp.get_json()
    assert body["error"] == "wrong_auth_type"
    assert body["expected"] == "token"


@pytest.mark.asyncio
async def test_empty_data_400(client):
    resp = await client.post("/api/credentials", json={
        "platform": "patreon", "credential_type": "cookies", "data": "   ",
    })
    assert resp.status_code == 400
    assert (await resp.get_json())["error"] == "empty_data"


@pytest.mark.asyncio
async def test_invalid_body_400(client):
    resp = await client.post("/api/credentials", json=[1, 2, 3])
    assert resp.status_code == 400


@pytest.mark.asyncio
async def test_get_404(client):
    resp = await client.get("/api/credentials/patreon")
    assert resp.status_code == 404


@pytest.mark.asyncio
async def test_delete_204_then_404(client):
    await client.post("/api/credentials", json={
        "platform": "patreon", "credential_type": "cookies", "data": _NETSCAPE,
    })
    dele = await client.delete("/api/credentials/patreon")
    assert dele.status_code == 204
    nope = await client.delete("/api/credentials/patreon")
    assert nope.status_code == 404


@pytest.mark.asyncio
async def test_extension_key_correct_accepts(client, ext_key):
    resp = await client.post(
        "/api/credentials",
        json={"platform": "patreon", "credential_type": "cookies", "data": _NETSCAPE},
        headers={"X-Extension-Key": ext_key},
    )
    assert resp.status_code == 201


@pytest.mark.asyncio
async def test_extension_key_wrong_rejects(client, ext_key):
    resp = await client.post(
        "/api/credentials",
        json={"platform": "patreon", "credential_type": "cookies", "data": _NETSCAPE},
        headers={"X-Extension-Key": "WRONG"},
    )
    assert resp.status_code == 401


@pytest.mark.asyncio
async def test_verify_no_credential_is_untestable(client):
    resp = await client.post("/api/credentials/patreon/verify")
    assert resp.status_code == 200
    body = await resp.get_json()
    assert body["valid"] is None
    assert "No credential" in body["reason"]


@pytest.mark.asyncio
async def test_verify_no_enabled_source_is_untestable(client):
    await client.post("/api/credentials", json={
        "platform": "patreon", "credential_type": "cookies", "data": _NETSCAPE,
    })
    resp = await client.post("/api/credentials/patreon/verify")
    body = await resp.get_json()
    assert body["valid"] is None
    assert "no enabled source" in body["reason"].lower()


@pytest.mark.asyncio
async def test_verify_runs_gallery_dl_and_stamps_on_success(client, db, monkeypatch):
    """A gallery-dl platform routes through GalleryDLService.verify; success
    stamps last_verified (platform-agnostic endpoint behavior)."""
    from backend.app.models import Artist, Source
    from backend.app.services import gallery_dl as gdl_mod

    # Stub the gallery-dl probe so the test doesn't shell out / hit network.
    async def _fake_verify(self, *args, **kwargs):
        return (True, "Credentials valid — the feed authenticated.")
    monkeypatch.setattr(gdl_mod.GalleryDLService, "verify", _fake_verify)

    # hentaifoundry is a gallery-dl platform (subscribestar migrated to native).
    await client.post("/api/credentials", json={
        "platform": "hentaifoundry", "credential_type": "cookies", "data": _NETSCAPE,
    })
    artist = Artist(name="Maewix", slug="maewix")
    db.add(artist)
    await db.flush()
    db.add(Source(
        artist_id=artist.id, platform="hentaifoundry",
        url="https://www.hentai-foundry.com/user/Maewix", enabled=True, config_overrides={},
    ))
    await db.commit()

    resp = await client.post("/api/credentials/hentaifoundry/verify")
    body = await resp.get_json()
    assert body["valid"] is True
    assert body["last_verified"] is not None

    # The stamp is persisted on the credential record.
    rec = await (await client.get("/api/credentials/hentaifoundry")).get_json()
    assert rec["last_verified"] is not None


@pytest.mark.asyncio
async def test_verify_patreon_uses_native_ingester_not_gallery_dl(client, db, monkeypatch):
    """plan #697 cutover: Patreon credential verify routes through the native
    ingester (verify_patreon_credential), NOT gallery-dl --simulate."""
    from backend.app.models import Artist, Source
    from backend.app.services import gallery_dl as gdl_mod
    from backend.app.services import patreon_ingester as pi_mod

    async def _native_verify(url, cookies_path, overrides):
        return (True, "Credentials valid — the Patreon feed authenticated.")
    monkeypatch.setattr(pi_mod, "verify_patreon_credential", _native_verify)

    # gallery-dl must NOT be consulted for Patreon.
    async def _boom(self, *args, **kwargs):
        raise AssertionError("gallery-dl verify must not run for patreon")
    monkeypatch.setattr(gdl_mod.GalleryDLService, "verify", _boom)

    await client.post("/api/credentials", json={
        "platform": "patreon", "credential_type": "cookies", "data": _NETSCAPE,
    })
    artist = Artist(name="Maewix", slug="maewix")
    db.add(artist)
    await db.flush()
    db.add(Source(
        artist_id=artist.id, platform="patreon",
        url="https://www.patreon.com/maewix", enabled=True, config_overrides={},
    ))
    await db.commit()

    resp = await client.post("/api/credentials/patreon/verify")
    body = await resp.get_json()
    assert body["valid"] is True
    assert body["last_verified"] is not None


@pytest.mark.asyncio
async def test_verify_reports_auth_failure(client, db, monkeypatch):
    from backend.app.models import Artist, Source
    from backend.app.services import gallery_dl as gdl_mod

    async def _fake_verify(self, *args, **kwargs):
        return (False, "Authentication failed — cookies may be expired or invalid")
    monkeypatch.setattr(gdl_mod.GalleryDLService, "verify", _fake_verify)

    await client.post("/api/credentials", json={
        "platform": "hentaifoundry", "credential_type": "cookies", "data": _NETSCAPE,
    })
    artist = Artist(name="HolyMeh", slug="holymeh")
    db.add(artist)
    await db.flush()
    db.add(Source(
        artist_id=artist.id, platform="hentaifoundry",
        url="https://www.hentai-foundry.com/user/HolyMeh", enabled=True,
        config_overrides={},
    ))
    await db.commit()

    resp = await client.post("/api/credentials/hentaifoundry/verify")
    body = await resp.get_json()
    assert body["valid"] is False
    assert "auth" in body["reason"].lower()
    assert body["last_verified"] is None
