package notification

import (
	"jannex/admin-dashboard-backend/modules/database"
	"jannex/admin-dashboard-backend/modules/requestclient"
	"jannex/admin-dashboard-backend/modules/structs"
	"jannex/admin-dashboard-backend/modules/utils"
	"jannex/admin-dashboard-backend/socketclients"
	"time"

	"git.ex.umbach.dev/Alex/roese-utils/rspagination"
	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
)

func GetTotalNotifications(userId string) int {
	var totalNotifications int64

	database.DB.Model(&structs.Notification{}).Where("user_id = ?", userId).Count(&totalNotifications)

	return int(totalNotifications)
}

func GetNotifications(query rspagination.PageQuery, userId string) []structs.Notification {
	var notifications []structs.Notification

	rspagination.DbPageQuery(database.DB, query,
		utils.NotificationsPaginationLimit,
		&notifications,
		"created_at DESC",
		"user_id = ?",
		userId)

	return notifications
}

func AddNotification(c *fiber.Ctx, body structs.AddNotificationRequest) error {
	var userIds []string

	if len(body.UserIds) == 0 && body.NeededPermission == "" && len(body.RoleIds) == 0 {
		// c not defined when called from code itself
		if c == nil {
			return nil
		}

		return c.SendStatus(fiber.StatusBadRequest)
	}

	if len(body.UserIds) > 0 {
		// check if user with provided user id in body.UserIds exist in database and add to userIds
		database.DB.Model(&structs.User{}).Where("id IN ?", body.UserIds).Select("id").Find(&userIds)
	}

	var roleIds []string

	if body.NeededPermission != "" {
		// fetch all role_ids from role_permissions and use the role_id to get all users which are in the role from database which have the permission provided in body.NeededPermission and add to userIds
		database.DB.Model(&structs.RolePermission{}).Where("permission_id = ?", body.NeededPermission).Select("role_id").Find(&roleIds)

		var userIdsTemp []string

		database.DB.Model(&structs.User{}).Where("role_id IN ?", roleIds).Select("id").Find(&userIdsTemp)

		userIds = append(userIds, userIdsTemp...)
	}

	if len(body.RoleIds) > 0 {
		var userIdsTemp []string

		database.DB.Model(&structs.User{}).Where("role_id IN ?", body.RoleIds).Select("id").Find(&userIdsTemp)

		userIds = append(userIds, userIdsTemp...)
	}

	// no users found
	if len(userIds) == 0 {
		// c not defined when called from code itself
		if c == nil {
			return nil
		}

		return c.SendStatus(fiber.StatusUnprocessableEntity)
	}

	// remove duplicates
	userIds = unique(userIds)

	// add notification to database
	for _, userId := range userIds {
		notify := structs.Notification{
			Id:        uuid.New().String(),
			UserId:    userId,
			Type:      body.Type,
			Title:     body.Title,
			CreatedAt: time.Now(),
		}

		database.DB.Create(&notify)

		// send notification to user
		socketclients.SendMessageToUser(userId, "", structs.SendSocketMessage{
			Cmd: utils.SentCmdNewNotification,
			Body: struct {
				Notification structs.Notification
				TotalPages   int
			}{
				Notification: notify,
				TotalPages: rspagination.GetTotalPages(database.DB, utils.NotificationsPaginationLimit,
					[]structs.Notification{},
					"user_id = ?",
					userId),
			},
		})
	}

	requestclient.TelegramBotManagerRequestClient(structs.TelegramBotManagerRequestBody{
		UserIds: userIds,
		Title:   body.Title,
		Type:    body.Type,
	})

	// c not defined when called from code itself
	if c == nil {
		return nil
	}

	return c.SendStatus(fiber.StatusOK)
}

func unique(slice []string) []string {
	keys := make(map[string]bool)
	list := []string{}

	for _, entry := range slice {
		if _, value := keys[entry]; !value {
			keys[entry] = true
			list = append(list, entry)
		}
	}

	return list
}

func DeleteAllNotifications(userId string) {
	database.DB.Delete(&structs.Notification{}, "user_id = ?", userId)

	socketclients.SendMessageToUser(userId, "", structs.SendSocketMessage{
		Cmd: utils.SentCmdAllNotificationsDeleted,
	})
}

func DeleteOneNotification(userId string, notificationId string) {
	database.DB.Delete(&structs.Notification{}, "user_id = ? AND id = ?", userId, notificationId)

	socketclients.SendMessageToUser(userId, "", structs.SendSocketMessage{
		Cmd:  utils.SentCmdOneNotificationDeleted,
		Body: notificationId,
	})
}
