"""
Notification endpoints — the in-app alert bell (module 1.1.11).

GET   /notifications              list, most recent first, with unread count
PATCH /notifications/{id}/read    mark one as read
PATCH /notifications/read-all     mark everything read

Notification CREATION is not exposed as a public endpoint — notifications
are generated server-side (e.g. by app/services/notification_service.py,
triggered when an analysis result changes a crop plan's feasibility
status, or by a scheduled job) rather than something a customer can
fabricate for themselves via the API.
"""

from fastapi import APIRouter, Depends
from sqlalchemy import desc, func, select, update
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.current_user import get_current_user
from app.core.exceptions import AppException
from app.db.models import Notification, User
from app.db.session import get_db_session
from app.schemas.crop_plan import NotificationListResponse, NotificationResponse

router = APIRouter()


class NotificationNotFoundError(AppException):
    status_code = 404
    error_code = "NOTIFICATION_NOT_FOUND"


@router.get("/notifications", response_model=NotificationListResponse)
async def list_notifications(
    limit: int = 50,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db_session),
) -> NotificationListResponse:
    result = await db.execute(
        select(Notification)
        .where(Notification.user_id == current_user.id)
        .order_by(desc(Notification.created_at))
        .limit(min(limit, 200))  # hard cap regardless of what the client requests
    )
    notifications = result.scalars().all()

    unread_result = await db.execute(
        select(func.count())
        .select_from(Notification)
        .where(Notification.user_id == current_user.id, Notification.is_read.is_(False))
    )
    unread_count = unread_result.scalar_one()

    return NotificationListResponse(
        notifications=[
            NotificationResponse(
                id=n.id,
                title=n.title,
                message=n.message,
                severity=n.severity,
                related_crop_plan_id=n.related_crop_plan_id,
                is_read=n.is_read,
                created_at=n.created_at.isoformat(),
            )
            for n in notifications
        ],
        unread_count=unread_count,
    )


@router.patch("/notifications/{notification_id}/read", response_model=NotificationResponse)
async def mark_notification_read(
    notification_id: str,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db_session),
) -> NotificationResponse:
    result = await db.execute(
        select(Notification).where(
            Notification.id == notification_id, Notification.user_id == current_user.id
        )
    )
    notification = result.scalar_one_or_none()
    if notification is None:
        raise NotificationNotFoundError(message="Notification not found")

    notification.is_read = True
    await db.commit()
    await db.refresh(notification)

    return NotificationResponse(
        id=notification.id,
        title=notification.title,
        message=notification.message,
        severity=notification.severity,
        related_crop_plan_id=notification.related_crop_plan_id,
        is_read=notification.is_read,
        created_at=notification.created_at.isoformat(),
    )


@router.patch("/notifications/read-all", status_code=204)
async def mark_all_notifications_read(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db_session),
) -> None:
    await db.execute(
        update(Notification)
        .where(Notification.user_id == current_user.id, Notification.is_read.is_(False))
        .values(is_read=True)
    )
    await db.commit()
