from typing import List

from sqlalchemy import exc, and_

from app.extensions.database import session
from app.extensions.utils.log_helper import logger_
from app.extensions.utils.query_helper import RawQueryHelper
from app.persistence.model import NotificationModel, NotificationTokenModel
from core.domains.notification.dto.notification_dto import CreateNotificationDto
from core.domains.notification.entity.notification_entity import NotificationEntity
from core.exceptions import NotUniqueErrorException

logger = logger_.getLogger(__name__)


class NotificationRepository:
    def get_fcm_token(self, user_id: int) -> str | None:
        query = session.query(NotificationTokenModel).filter_by(user_id=user_id)
        query_set = query.first()

        if not query_set:
            return None

        return query_set.token

    def create_notification(self, dto: CreateNotificationDto) -> None:
        try:
            model = NotificationModel(
                user_id=dto.user_id,
                token=dto.token,
                topic=dto.topic,
                status=dto.status,
                message=dto.message,
            )
            session.add(model)
            session.commit()
        except exc.IntegrityError as e:
            logger.error(
                f"[NotificationRepository][create_notification] user_id : {dto.user_id} error : {e}"
            )
            session.rollback()
            raise NotUniqueErrorException

    def update_notification_read(self, notification_id: int) -> None:
        try:
            session.query(NotificationModel).filter_by(id=notification_id).update(
                {"is_read": True,}
            )
            session.commit()
        except exc.IntegrityError as e:
            logger.error(
                f"[NotificationRepository][update_notification_read] notification_id : {notification_id} error : {e}"
            )
            session.rollback()
            raise NotUniqueErrorException

    def update_notification_all_read(self, user_id: int) -> None:
        try:
            session.query(NotificationModel).filter_by(user_id=user_id).update(
                {"is_read": True,}
            )
            session.commit()
        except exc.IntegrityError as e:
            logger.error(
                f"[NotificationRepository][update_notification_all_read] user_id : {user_id} error : {e}"
            )
            session.rollback()
            raise NotUniqueErrorException

    def get_notifications(
        self, user_id: int, previous_id: int | None
    ) -> List[NotificationEntity] | None:
        offset = 10
        previous_id_filters = list()

        if previous_id:
            previous_id_filters = [
                # previous_id = 1 -> 1보다 크고, 3보다 작은 notification_id
                and_(
                    NotificationModel.id < previous_id,
                    NotificationModel.id >= previous_id - offset,
                )
            ]

        query = (
            session.query(NotificationModel)
            .filter(NotificationModel.user_id == user_id)
            .filter(*previous_id_filters)
            .order_by(NotificationModel.id.desc())
            .limit(10)
        )
        RawQueryHelper.print_raw_query(query)
        query_set = query.all()

        if not query_set:
            return None

        return [query.to_notification_entity() for query in query_set]

    def get_badge(self, user_id: int) -> bool:
        query = session.query(
            NotificationModel.query.filter(
                NotificationModel.user_id == user_id, NotificationModel.is_read == False
            ).exists()
        )
        query_set = query.scalar()
        return query_set
