import logging

from sqlalchemy import delete, select, update
from sqlalchemy.exc import (
    IntegrityError,
    SQLAlchemyError,
)
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import false

from media_manager.exceptions import ConflictError, NotFoundError
from media_manager.notification.models import Notification
from media_manager.notification.schemas import (
    Notification as NotificationSchema,
)
from media_manager.notification.schemas import (
    NotificationId,
)

log = logging.getLogger(__name__)


class NotificationRepository:
    def __init__(self, db: Session) -> None:
        self.db = db

    def get_notification(self, nid: NotificationId) -> NotificationSchema:
        result = self.db.get(Notification, nid)

        if not result:
            msg = f"Notification with id {nid} not found."
            raise NotFoundError(msg)

        return NotificationSchema.model_validate(result)

    def get_unread_notifications(self) -> list[NotificationSchema]:
        try:
            stmt = (
                select(Notification)
                .where(Notification.read == false())
                .order_by(Notification.timestamp.desc())
            )
            results = self.db.execute(stmt).scalars().all()
            return [
                NotificationSchema.model_validate(notification)
                for notification in results
            ]
        except SQLAlchemyError:
            log.exception("Database error while retrieving unread notifications")
            raise

    def get_all_notifications(self) -> list[NotificationSchema]:
        try:
            stmt = select(Notification).order_by(Notification.timestamp.desc())
            results = self.db.execute(stmt).scalars().all()
            return [
                NotificationSchema.model_validate(notification)
                for notification in results
            ]
        except SQLAlchemyError:
            log.exception("Database error while retrieving notifications")
            raise

    def save_notification(self, notification: NotificationSchema) -> None:
        try:
            self.db.add(
                Notification(
                    id=notification.id,
                    read=notification.read,
                    timestamp=notification.timestamp,
                    message=notification.message,
                )
            )
            self.db.commit()
        except IntegrityError:
            log.exception("Could not save notification")
            msg = f"Notification with id {notification.id} already exists."
            raise ConflictError(msg) from None
        return

    def mark_notification_as_read(self, nid: NotificationId) -> None:
        stmt = update(Notification).where(Notification.id == nid).values(read=True)
        self.db.execute(stmt)
        return

    def mark_notification_as_unread(self, nid: NotificationId) -> None:
        stmt = update(Notification).where(Notification.id == nid).values(read=False)
        self.db.execute(stmt)
        return

    def delete_notification(self, nid: NotificationId) -> None:
        stmt = delete(Notification).where(Notification.id == nid)
        result = self.db.execute(stmt)
        if result.rowcount == 0:
            msg = f"Notification with id {nid} not found."
            raise NotFoundError(msg)
        self.db.commit()
        return
