import uuid

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

from app.core.database import get_db
from app.core.security import AuthContext, get_auth_context
from app.models.notification import Notification
from app.schemas.notification import NotificationRead, UnreadCount

router = APIRouter(prefix="/notifications", tags=["notifications"])


def _visible_to(ctx: AuthContext):
    """Rows the caller's bell shows. A Public Portal (vendor) caller sees
    only rows addressed to them personally; an internal caller sees those
    plus the tenant-wide broadcast rows (recipient_user_id NULL — every
    notification configured without recipient roles/users). No module
    permission gates this router: what you can see is exactly what was
    addressed to you, so there's nothing further to grant."""
    mine = Notification.recipient_user_id == ctx.user_id
    if ctx.vendor_id is not None:
        return mine
    return or_(Notification.recipient_user_id.is_(None), mine)


@router.get("", response_model=list[NotificationRead])
async def list_notifications(
    skip: int = 0,
    limit: int = 50,
    unread_only: bool = False,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
) -> list[Notification]:
    stmt = select(Notification).where(Notification.tenant_id == ctx.tenant_id, _visible_to(ctx))
    if unread_only:
        stmt = stmt.where(Notification.read.is_(False))
    stmt = stmt.order_by(Notification.created_at.desc()).offset(skip).limit(limit)
    return list((await db.execute(stmt)).scalars().all())


@router.get("/unread-count", response_model=UnreadCount)
async def unread_count(
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
) -> UnreadCount:
    stmt = select(func.count()).select_from(Notification).where(
        Notification.tenant_id == ctx.tenant_id, _visible_to(ctx), Notification.read.is_(False)
    )
    return UnreadCount(count=(await db.execute(stmt)).scalar_one())


@router.post("/{notification_id}/read", response_model=NotificationRead)
async def mark_read(
    notification_id: uuid.UUID,
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
) -> Notification:
    stmt = select(Notification).where(
        Notification.tenant_id == ctx.tenant_id, Notification.id == notification_id, _visible_to(ctx)
    )
    notification = (await db.execute(stmt)).scalar_one_or_none()
    if notification is None:
        raise HTTPException(status_code=404, detail="Notification not found")
    notification.read = True
    await db.flush()
    await db.refresh(notification)
    return notification


@router.post("/read-all", response_model=UnreadCount)
async def mark_all_read(
    ctx: AuthContext = Depends(get_auth_context),
    db: AsyncSession = Depends(get_db),
) -> UnreadCount:
    stmt = (
        update(Notification)
        .where(Notification.tenant_id == ctx.tenant_id, _visible_to(ctx), Notification.read.is_(False))
        .values(read=True)
        .execution_options(synchronize_session=False)
    )
    result = await db.execute(stmt)
    await db.flush()
    return UnreadCount(count=result.rowcount or 0)
