"""Notification service — create and manage user notifications."""

from __future__ import annotations

import uuid
from datetime import UTC
from typing import Any

from sqlalchemy import and_, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.notification import (
    Notification,
    NotificationPreference,
    NotificationType,
)


async def create_notification(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    type: str,
    title: str,
    body: str | None = None,
) -> Notification | None:
    """Create a new notification for a user if they have not disabled this type.

    Returns None if the user has opted out of this notification type.
    """
    # Check user preference
    pref = await db.execute(
        select(NotificationPreference).where(
            and_(
                NotificationPreference.user_id == user_id,
                NotificationPreference.type_key == type,
                NotificationPreference.tenant_id == tenant_id,
            )
        )
    )
    pref_row = pref.scalar_one_or_none()

    # If preference exists and is disabled, skip
    if pref_row and not pref_row.is_enabled:
        return None

    # If no preference, check if type is enabled by default
    if not pref_row:
        type_def = await db.execute(
            select(NotificationType).where(NotificationType.type_key == type)
        )
        type_row = type_def.scalar_one_or_none()
        if type_row and not type_row.is_enabled_by_default:
            return None

    # Create notification
    notif = Notification(
        tenant_id=tenant_id,
        user_id=user_id,
        type=type,
        title=title,
        body=body,
    )
    db.add(notif)
    await db.flush()
    return notif


async def list_notifications(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    page: int = 1,
    page_size: int = 25,
) -> dict[str, Any]:
    """List notifications for a user, unread first, then by created_at desc."""
    offset = (page - 1) * page_size
    # Count total
    count_q = (
        select(func.count())
        .select_from(Notification)
        .where(
            Notification.tenant_id == tenant_id,
            Notification.user_id == user_id,
        )
    )
    total = (await db.execute(count_q)).scalar() or 0

    # Query — unread first (read_at IS NULL), then newest
    q = (
        select(Notification)
        .where(
            Notification.tenant_id == tenant_id,
            Notification.user_id == user_id,
        )
        .order_by(
            Notification.read_at.isnot(None),  # False (unread) sorts first
            Notification.created_at.desc(),
        )
        .offset(offset)
        .limit(page_size)
    )
    result = await db.execute(q)
    items = result.scalars().all()

    return {
        "items": [_notification_to_dict(n) for n in items],
        "total": total,
        "page": page,
        "page_size": page_size,
    }


async def mark_notification_read(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
    notification_id: uuid.UUID,
) -> Notification | None:
    """Mark a notification as read."""
    from datetime import datetime

    q = (
        update(Notification)
        .where(
            Notification.id == notification_id,
            Notification.tenant_id == tenant_id,
            Notification.user_id == user_id,
        )
        .values(read_at=datetime.now(UTC))
        .returning(Notification)
    )
    result = await db.execute(q)
    row = result.scalar_one_or_none()
    return row


async def get_unread_count(
    db: AsyncSession,
    tenant_id: uuid.UUID,
    user_id: uuid.UUID,
) -> int:
    """Get unread notification count for a user."""
    q = (
        select(func.count())
        .select_from(Notification)
        .where(
            Notification.tenant_id == tenant_id,
            Notification.user_id == user_id,
            Notification.read_at.is_(None),
        )
    )
    result = await db.execute(q)
    return result.scalar() or 0


def _notification_to_dict(n: Notification) -> dict[str, Any]:
    """Convert a notification to a dict."""
    return {
        "id": str(n.id),
        "type": n.type,
        "title": n.title,
        "body": n.body,
        "read_at": n.read_at.isoformat() if n.read_at else None,
        "created_at": n.created_at.isoformat() if n.created_at else None,
    }
