package com.betriebsratkanzlei.legalconsenthub.notification

import com.betriebsratkanzlei.legalconsenthub.security.CustomJwtTokenPrincipal
import com.betriebsratkanzlei.legalconsenthub.user.UserRepository
import com.betriebsratkanzlei.legalconsenthub_api.model.CreateNotificationDto
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.util.UUID

@Service
class NotificationService(
    private val notificationRepository: NotificationRepository,
    private val userRepository: UserRepository,
    private val notificationMapper: NotificationMapper,
) {
    fun createNotificationForUser(createNotificationDto: CreateNotificationDto): Notification {
        val recipientKeycloakId =
            createNotificationDto.recipientId
                ?: throw IllegalArgumentException("recipientId must be provided for user notifications")

        val recipient =
            userRepository
                .findById(recipientKeycloakId)
                .orElseThrow { IllegalArgumentException("User not found with id: $recipientKeycloakId") }

        val notification =
            notificationMapper.toNotification(
                createNotificationDto = createNotificationDto,
                recipient = recipient,
            )
        return notificationRepository.save(notification)
    }

    @Transactional
    fun createNotificationForRoles(createNotificationDto: CreateNotificationDto): Notification {
        val targetRoles =
            createNotificationDto.targetRoles
                ?: throw IllegalArgumentException("targetRoles must be provided for role-based notifications")

        val notification =
            notificationMapper.toNotification(
                createNotificationDto = createNotificationDto,
                targetRoles = targetRoles.joinToString(","),
            )

        return notificationRepository.save(notification)
    }

    @Transactional
    fun createNotificationForOrganization(createNotificationDto: CreateNotificationDto): Notification {
        val notification =
            notificationMapper.toNotification(
                createNotificationDto = createNotificationDto,
            )

        return notificationRepository.save(notification)
    }

    fun getNotifications(
        recipientKeycloakId: String,
        organizationId: String,
        userRoles: List<String>,
        page: Int = 0,
        size: Int = 20,
    ): Page<Notification> {
        val pageable = PageRequest.of(page, size)
        val allNotifications =
            notificationRepository.findByRecipientOrWithoutRecipientAndOrganizationId(
                recipientKeycloakId,
                organizationId,
                pageable,
            )

        val filteredContent =
            allNotifications.content.filter { notification ->
                when {
                    // Direct recipient notification
                    notification.recipient != null -> true
                    // Organization-wide notification (no recipient, no roles)
                    notification.targetRoles.isNullOrEmpty() -> true
                    // Role-based notification
                    else -> {
                        val targetRolesList = notification.targetRoles!!.split(",")
                        userRoles.any { it in targetRolesList }
                    }
                }
            }

        return PageImpl(filteredContent, pageable, allNotifications.totalElements)
    }

    fun getUnreadNotifications(
        recipientKeycloakId: String,
        organizationId: String,
        userRoles: List<String>,
    ): List<Notification> {
        val allNotifications =
            notificationRepository.findUnreadByRecipientOrWithoutRecipientAndOrganizationId(
                recipientKeycloakId,
                organizationId,
            )

        return allNotifications.filter { notification ->
            when {
                // Direct recipient notification
                notification.recipient != null -> true
                // Organization-wide notification (no recipient, no roles)
                notification.targetRoles.isNullOrEmpty() -> true
                // Role-based notification
                else -> {
                    val targetRolesList = notification.targetRoles!!.split(",")
                    userRoles.any { it in targetRolesList }
                }
            }
        }
    }

    fun getUnreadNotificationCount(
        recipientKeycloakId: String,
        organizationId: String,
        userRoles: List<String>,
    ): Long = getUnreadNotifications(recipientKeycloakId, organizationId, userRoles).size.toLong()

    @Transactional
    fun markAllAsRead(
        recipientKeycloakId: String,
        organizationId: String,
    ) {
        notificationRepository.markAllAsReadByRecipientAndOrganization(recipientKeycloakId, organizationId)
    }

    @Transactional
    fun markNotificationAsRead(notificationId: UUID): Notification {
        val notification =
            notificationRepository
                .findById(notificationId)
                .orElseThrow { IllegalArgumentException("Notification not found with id: $notificationId") }

        val principal = SecurityContextHolder.getContext().authentication.principal as? CustomJwtTokenPrincipal
        val currentUserKeycloakId =
            principal?.id
                ?: throw IllegalStateException("User ID not found in security context")

        if (notification.recipient != null && notification.recipient?.keycloakId != currentUserKeycloakId) {
            throw IllegalArgumentException("Cannot mark notification as read for another user")
        }

        notification.isRead = true
        return notificationRepository.save(notification)
    }

    @Transactional
    fun clearAllNotifications(
        recipientKeycloakId: String,
        organizationId: String,
    ) {
        notificationRepository.deleteAllByRecipientAndOrganization(recipientKeycloakId, organizationId)
    }
}
