from sqlalchemy.orm import Session
from fastapi import HTTPException, status
from typing import Optional, List
from . import repository
from .schema import NotificationCreate, NotificationUpdate, NotificationResponse
from user_management import repository as user_repo


def _get_or_raise(db: Session, entity_id: str, repo) -> any:
    entity = repo.get_by_id(db, entity_id)
    if not entity:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Resource with id {entity_id} not found"
        )
    return entity


def create_notification(db: Session, data: NotificationCreate) -> NotificationResponse:
    user = user_repo.get_by_id(db, data.user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"User with id {data.user_id} does not exist"
        )
    
    try:
        notification = repository.create(db, data.model_dump())
        db.commit()
        db.refresh(notification)
        return NotificationResponse.model_validate(notification)
    except Exception:
        db.rollback()
        raise


def get_notification(db: Session, notification_id: str) -> NotificationResponse:
    notification = _get_or_raise(db, notification_id, repository)
    return NotificationResponse.model_validate(notification)


def list_notifications(
    db: Session,
    limit: int,
    offset: int,
    user_id: Optional[str] = None,
    is_read: Optional[bool] = None,
    notification_type: Optional[str] = None,
) -> List[NotificationResponse]:
    notifications = repository.list_all(
        db,
        limit=limit,
        offset=offset,
        user_id=user_id,
        is_read=is_read,
        notification_type=notification_type,
    )
    return [NotificationResponse.model_validate(n) for n in notifications]


def update_notification(
    db: Session, notification_id: str, data: NotificationUpdate
) -> NotificationResponse:
    _get_or_raise(db, notification_id, repository)
    
    update_data = data.model_dump(exclude_unset=True)
    
    if "user_id" in update_data:
        user = user_repo.get_by_id(db, update_data["user_id"])
        if not user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"User with id {update_data['user_id']} does not exist"
            )
    
    try:
        notification = repository.update(db, notification_id, update_data)
        db.commit()
        db.refresh(notification)
        return NotificationResponse.model_validate(notification)
    except Exception:
        db.rollback()
        raise


def delete_notification(db: Session, notification_id: str) -> dict:
    _get_or_raise(db, notification_id, repository)
    
    try:
        repository.delete(db, notification_id)
        db.commit()
        return {"message": "Notification deleted successfully"}
    except Exception:
        db.rollback()
        raise


def mark_notification_as_read(db: Session, notification_id: str) -> NotificationResponse:
    _get_or_raise(db, notification_id, repository)
    
    try:
        notification = repository.mark_as_read(db, notification_id)
        db.commit()
        db.refresh(notification)
        return NotificationResponse.model_validate(notification)
    except Exception:
        db.rollback()
        raise


def mark_all_notifications_as_read(db: Session, user_id: str) -> dict:
    user = user_repo.get_by_id(db, user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"User with id {user_id} does not exist"
        )
    
    try:
        count = repository.mark_all_as_read_for_user(db, user_id)
        db.commit()
        return {"message": f"Marked {count} notifications as read"}
    except Exception:
        db.rollback()
        raise


def get_unread_notification_count(db: Session, user_id: str) -> dict:
    user = user_repo.get_by_id(db, user_id)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"User with id {user_id} does not exist"
        )
    
    count = repository.get_unread_count(db, user_id)
    return {"unread_count": count}