"""Integration tests for notifications API."""

import uuid
from datetime import datetime, timedelta, timezone

import pytest
from fastapi.testclient import TestClient
from sqlalchemy.ext.asyncio import AsyncSession

from src.models.user.user import User
from src.models.user.user_config import UserConfig
from src.services.shared.notification_service import NotificationService


@pytest.fixture
def notification_service() -> NotificationService:
    return NotificationService()


@pytest.fixture
async def user_a(db_session: AsyncSession) -> User:
    user = User(
        id=uuid.uuid4(),
        email="user-a@headquarter.local",
        name="User A",
        authentik_id=f"authentik-{uuid.uuid4()}",
        avatar_url=None,
    )
    db_session.add(user)
    await db_session.commit()
    return user


@pytest.fixture
async def user_b(db_session: AsyncSession) -> User:
    user = User(
        id=uuid.uuid4(),
        email="user-b@headquarter.local",
        name="User B",
        authentik_id=f"authentik-{uuid.uuid4()}",
        avatar_url=None,
    )
    db_session.add(user)
    await db_session.commit()
    return user


def _mint_cookie_for_user(test_client: TestClient, user_id: uuid.UUID) -> None:
    from src.auth.session import create_session_cookie
    from src.config import Settings

    settings = Settings()
    cookie = create_session_cookie(
        settings=settings,
        user_id=str(user_id),
    )
    test_client.cookies.set("session", cookie)


@pytest.mark.integration
def test_list_requires_auth(test_client: TestClient) -> None:
    response = test_client.get("/notifications")
    assert response.status_code == 401


@pytest.mark.integration
def test_list_returns_only_own_notifications(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
    user_b: User,
) -> None:
    async def create_notifications() -> None:
        await notification_service.create_notification(
            db_session, user_a.id, category="instance", severity="info", title="A"
        )
        await notification_service.create_notification(
            db_session, user_b.id, category="instance", severity="info", title="B"
        )

    import asyncio

    asyncio.run(create_notifications())

    _mint_cookie_for_user(authenticated_client, user_a.id)
    response = authenticated_client.get("/notifications")
    assert response.status_code == 200
    data = response.json()
    assert len(data["items"]) == 1
    assert data["items"][0]["title"] == "A"


@pytest.mark.integration
def test_list_pagination(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
) -> None:
    async def create_many() -> None:
        for i in range(25):
            n = await notification_service.create_notification(
                db_session,
                user_a.id,
                category="instance",
                severity="info",
                title=f"Notification {i}",
            )
            n.created_at = datetime.now(timezone.utc) - timedelta(seconds=i)
            await db_session.commit()
            await db_session.refresh(n)

    import asyncio

    asyncio.run(create_many())

    _mint_cookie_for_user(authenticated_client, user_a.id)
    response = authenticated_client.get("/notifications?limit=10&offset=10")
    assert response.status_code == 200
    data = response.json()
    assert len(data["items"]) == 10
    assert data["total"] == 25
    assert data["limit"] == 10
    assert data["offset"] == 10


@pytest.mark.integration
def test_unread_count_endpoint(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
) -> None:
    async def create_unread() -> None:
        for _ in range(3):
            await notification_service.create_notification(
                db_session,
                user_a.id,
                category="instance",
                severity="info",
                title="Unread",
            )

    import asyncio

    asyncio.run(create_unread())

    _mint_cookie_for_user(authenticated_client, user_a.id)
    response = authenticated_client.get("/notifications/unread")
    assert response.status_code == 200
    data = response.json()
    assert data["count"] == 3


@pytest.mark.integration
def test_mark_read_endpoint(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
) -> None:
    async def create_and_get() -> uuid.UUID:
        n = await notification_service.create_notification(
            db_session, user_a.id, category="instance", severity="info", title="To read"
        )
        return n.id

    import asyncio

    nid = asyncio.run(create_and_get())

    _mint_cookie_for_user(authenticated_client, user_a.id)
    response = authenticated_client.patch(f"/notifications/{nid}/read")
    assert response.status_code == 200
    data = response.json()
    assert data["read_at"] is not None


@pytest.mark.integration
def test_mark_read_404_for_other_user(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
    user_b: User,
) -> None:
    async def create_and_get() -> uuid.UUID:
        n = await notification_service.create_notification(
            db_session,
            user_a.id,
            category="instance",
            severity="info",
            title="Owned by A",
        )
        return n.id

    import asyncio

    nid = asyncio.run(create_and_get())

    _mint_cookie_for_user(authenticated_client, user_b.id)
    response = authenticated_client.patch(f"/notifications/{nid}/read")
    assert response.status_code == 404


@pytest.mark.integration
def test_mark_all_read_endpoint(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
) -> None:
    async def create_unread() -> None:
        for _ in range(4):
            await notification_service.create_notification(
                db_session,
                user_a.id,
                category="instance",
                severity="info",
                title="Unread",
            )

    import asyncio

    asyncio.run(create_unread())

    _mint_cookie_for_user(authenticated_client, user_a.id)
    response = authenticated_client.post("/notifications/mark-all-read")
    assert response.status_code == 200
    data = response.json()
    assert data["marked_count"] == 4


@pytest.mark.integration
def test_dismiss_endpoint(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
) -> None:
    async def create_and_get() -> uuid.UUID:
        n = await notification_service.create_notification(
            db_session,
            user_a.id,
            category="instance",
            severity="info",
            title="To dismiss",
        )
        return n.id

    import asyncio

    nid = asyncio.run(create_and_get())

    _mint_cookie_for_user(authenticated_client, user_a.id)
    response = authenticated_client.delete(f"/notifications/{nid}")
    assert response.status_code == 204

    response = authenticated_client.get("/notifications")
    data = response.json()
    assert len(data["items"]) == 0


@pytest.mark.integration
def test_dismiss_404_for_other_user(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
    user_b: User,
) -> None:
    async def create_and_get() -> uuid.UUID:
        n = await notification_service.create_notification(
            db_session,
            user_a.id,
            category="instance",
            severity="info",
            title="Owned by A",
        )
        return n.id

    import asyncio

    nid = asyncio.run(create_and_get())

    _mint_cookie_for_user(authenticated_client, user_b.id)
    response = authenticated_client.delete(f"/notifications/{nid}")
    assert response.status_code == 404


@pytest.mark.integration
def test_mute_categories_filter_in_list(
    authenticated_client: TestClient,
    db_session: AsyncSession,
    notification_service: NotificationService,
    user_a: User,
) -> None:
    async def setup() -> None:
        config = UserConfig(
            user_id=user_a.id, config={"notification_mute_categories": ["instance"]}
        )
        db_session.add(config)
        await db_session.commit()

        await notification_service.create_notification(
            db_session,
            user_a.id,
            category="instance",
            severity="info",
            title="Instance",
        )
        await notification_service.create_notification(
            db_session, user_a.id, category="system", severity="info", title="System"
        )

    import asyncio

    asyncio.run(setup())

    _mint_cookie_for_user(authenticated_client, user_a.id)
    response = authenticated_client.get("/notifications")
    assert response.status_code == 200
    data = response.json()
    assert len(data["items"]) == 1
    assert data["items"][0]["title"] == "System"
