import pytest

pytestmark = pytest.mark.integration


async def _mk(client, name, kind, fandom_id=None):
    body = {"name": name, "kind": kind}
    if fandom_id is not None:
        body["fandom_id"] = fandom_id
    r = await client.post("/api/tags", json=body)
    return (await r.get_json())["id"]


@pytest.mark.asyncio
async def test_get_tag_returns_shape_and_404(client):
    tid = await _mk(client, "Lookup", "general")
    resp = await client.get(f"/api/tags/{tid}")
    assert resp.status_code == 200
    body = await resp.get_json()
    assert body["id"] == tid
    assert body["name"] == "Lookup"
    assert body["kind"] == "general"
    resp = await client.get("/api/tags/99999999")
    assert resp.status_code == 404


@pytest.mark.asyncio
async def test_patch_sets_and_clears_character_fandom(client):
    fandom = await _mk(client, "Bleach", "fandom")
    char = await _mk(client, "Ichigo", "character")
    resp = await client.patch(f"/api/tags/{char}", json={"fandom_id": fandom})
    assert resp.status_code == 200
    assert (await resp.get_json())["fandom_id"] == fandom
    resp = await client.patch(f"/api/tags/{char}", json={"fandom_id": None})
    assert resp.status_code == 200
    assert (await resp.get_json())["fandom_id"] is None


@pytest.mark.asyncio
async def test_patch_fandom_collision_then_merge(client):
    f1 = await _mk(client, "Bleach", "fandom")
    f2 = await _mk(client, "Naruto", "fandom")
    c1 = await _mk(client, "Renji", "character", fandom_id=f1)
    c2 = await _mk(client, "Renji", "character", fandom_id=f2)
    # Moving c1 into f2 collides with c2 → rich 409 (same shape as rename).
    resp = await client.patch(f"/api/tags/{c1}", json={"fandom_id": f2})
    assert resp.status_code == 409
    assert (await resp.get_json())["target"]["id"] == c2
    # Confirm → merge c1 into c2, c2 survives.
    resp = await client.patch(
        f"/api/tags/{c1}", json={"fandom_id": f2, "merge": True}
    )
    assert resp.status_code == 200
    assert (await resp.get_json())["id"] == c2


@pytest.mark.asyncio
async def test_rename_collision_returns_rich_409(client):
    tgt = await _mk(client, "Canonical", "general")
    src = await _mk(client, "Duplicate", "general")
    resp = await client.patch(f"/api/tags/{src}", json={"name": "Canonical"})
    assert resp.status_code == 409
    body = await resp.get_json()
    assert body["target"]["id"] == tgt
    assert body["target"]["name"] == "Canonical"
    assert body["source_image_count"] == 0
    assert body["will_alias"] is False


@pytest.mark.asyncio
async def test_merge_endpoint_moves_and_deletes(client):
    tgt = await _mk(client, "Keep", "general")
    src = await _mk(client, "Gone", "general")
    resp = await client.post(
        f"/api/tags/{src}/merge", json={"target_id": tgt}
    )
    assert resp.status_code == 200
    body = await resp.get_json()
    assert body["target"]["id"] == tgt
    assert body["source_deleted"] is True
    # source is gone: renaming something else onto its old name no longer collides
    other = await _mk(client, "Other", "general")
    r2 = await client.patch(f"/api/tags/{other}", json={"name": "Gone"})
    assert r2.status_code == 200


@pytest.mark.asyncio
async def test_merge_self_is_400(client):
    t = await _mk(client, "Selfie", "general")
    resp = await client.post(f"/api/tags/{t}/merge", json={"target_id": t})
    assert resp.status_code == 400


@pytest.mark.asyncio
async def test_merge_missing_target_is_404(client):
    t = await _mk(client, "Lonely", "general")
    resp = await client.post(
        f"/api/tags/{t}/merge", json={"target_id": 999999}
    )
    assert resp.status_code == 404


@pytest.mark.asyncio
async def test_merge_requires_target_id(client):
    t = await _mk(client, "NoBody", "general")
    resp = await client.post(f"/api/tags/{t}/merge", json={})
    assert resp.status_code == 400
