"""Integration tests for app.api.v1.notifications — list, mark_read, mark_all_read."""

import uuid

import pytest
import pytest_asyncio

from app.models.operations import SystemNotification


@pytest_asyncio.fixture
async def auth_ctx(client):
    """Register a user and return (headers, user_id)."""
    email = f"t{uuid.uuid4().hex[:6]}@test.cn"
    resp = await client.post("/api/v1/auth/register", json={
        "email": email, "password": "Test1234", "display_name": "TestUser",
    })
    assert resp.status_code == 200
    data = resp.json()
    headers = {"Authorization": f"Bearer {data['token']['access_token']}"}
    user_id = uuid.UUID(data["user"]["id"])
    return headers, user_id


@pytest_asyncio.fixture
async def seed_notifications(db):
    """Seed some system notifications. Cleans up first."""
    from sqlalchemy import delete as sql_delete
    await db.execute(sql_delete(SystemNotification))

    n1 = SystemNotification(id=uuid.uuid4(), title="通知1", content="内容1",
                            target_type="all", is_read=False)
    n2 = SystemNotification(id=uuid.uuid4(), title="通知2", content="内容2",
                            target_type="all", is_read=False)
    db.add_all([n1, n2])
    await db.commit()
    return [n1, n2]


# ── List Notifications ──


@pytest.mark.asyncio
async def test_list_notifications_empty(client, auth_ctx, db):
    """List notifications returns empty when none exist."""
    from sqlalchemy import delete as sql_delete
    from app.models.operations import SystemNotification
    await db.execute(sql_delete(SystemNotification))
    await db.commit()
    headers, _ = auth_ctx
    r = await client.get("/api/v1/notifications", headers=headers)
    assert r.status_code == 200
    data = r.json()
    assert data["items"] == []
    assert data["total"] == 0
    assert data["unread"] == 0


@pytest.mark.asyncio
async def test_list_notifications_with_data(client, auth_ctx, seed_notifications):
    """List notifications returns seeded items."""
    headers, _ = auth_ctx
    r = await client.get("/api/v1/notifications", headers=headers)
    assert r.status_code == 200
    data = r.json()
    assert data["total"] >= 2
    assert data["unread"] >= 2
    titles = {n["title"] for n in data["items"]}
    assert "通知1" in titles
    assert "通知2" in titles


# ── Mark Read ──


@pytest.mark.asyncio
async def test_mark_read(client, auth_ctx, seed_notifications):
    """Mark a notification as read."""
    headers, _ = auth_ctx
    nid = str(seed_notifications[0].id)
    r = await client.post(f"/api/v1/notifications/{nid}/read", headers=headers)
    assert r.status_code == 200
    assert r.json()["status"] == "ok"

    # Verify unread count decreased
    r2 = await client.get("/api/v1/notifications", headers=headers)
    assert r2.json()["unread"] >= 1  # at least 1 still unread


@pytest.mark.asyncio
async def test_mark_read_nonexistent(client, auth_ctx):
    """Mark non-existent notification returns ok (no-op)."""
    headers, _ = auth_ctx
    r = await client.post(f"/api/v1/notifications/{uuid.uuid4()}/read", headers=headers)
    assert r.status_code == 200
    assert r.json()["status"] == "ok"


# ── Mark All Read ──


@pytest.mark.asyncio
async def test_mark_all_read(client, auth_ctx, seed_notifications):
    """Mark all notifications as read."""
    headers, _ = auth_ctx
    r = await client.post("/api/v1/notifications/read-all", headers=headers)
    assert r.status_code == 200
    assert r.json()["status"] == "ok"

    r2 = await client.get("/api/v1/notifications", headers=headers)
    assert r2.json()["unread"] == 0


@pytest.mark.asyncio
async def test_mark_all_read_when_none(client, auth_ctx):
    """Mark all read with no notifications returns ok."""
    headers, _ = auth_ctx
    r = await client.post("/api/v1/notifications/read-all", headers=headers)
    assert r.status_code == 200
    assert r.json()["status"] == "ok"


# ── Delete Notification ──


@pytest.mark.asyncio
async def test_delete_notification(client, auth_ctx, seed_notifications):
    """Delete a notification returns status deleted and removes it."""
    headers, _ = auth_ctx
    nid = str(seed_notifications[0].id)
    r = await client.delete(f"/api/v1/notifications/{nid}", headers=headers)
    assert r.status_code == 200
    assert r.json()["status"] == "deleted"

    # Verify it's gone from the list
    r2 = await client.get("/api/v1/notifications", headers=headers)
    titles = {n["title"] for n in r2.json()["items"]}
    assert seed_notifications[0].title not in titles
    assert seed_notifications[1].title in titles


@pytest.mark.asyncio
async def test_delete_notification_nonexistent(client, auth_ctx):
    """Delete non-existent notification returns 404."""
    headers, _ = auth_ctx
    r = await client.delete(f"/api/v1/notifications/{uuid.uuid4()}", headers=headers)
    assert r.status_code == 404
    assert r.json() == {"success": False, "error": {"code": "HTTP_404", "message": "Notification not found"}}


@pytest.mark.asyncio
async def test_delete_notification_other_user(client, db, auth_ctx):
    """Delete another user's notification returns 404 and does not remove it."""
    from sqlalchemy import select

    # Create a second user
    email = f"t{uuid.uuid4().hex[:6]}@test.cn"
    resp = await client.post("/api/v1/auth/register", json={
        "email": email, "password": "Test1234", "display_name": "OtherUser",
    })
    assert resp.status_code == 200
    data = resp.json()
    other_user_id = uuid.UUID(data["user"]["id"])

    # Create a notification targeting the second user only
    n = SystemNotification(id=uuid.uuid4(), title="Private通知", content="Private内容",
                           target_type="user", target_id=other_user_id, is_read=False)
    db.add(n)
    await db.commit()

    headers, _ = auth_ctx
    r = await client.delete(f"/api/v1/notifications/{n.id}", headers=headers)
    assert r.status_code == 404
    assert r.json() == {"success": False, "error": {"code": "HTTP_404", "message": "Notification not found"}}

    # Verify notification still exists in the database (no status change)
    result = await db.execute(select(SystemNotification).where(SystemNotification.id == n.id))
    assert result.scalar() is not None
