package systempermissions

import (
	"jannex/admin-dashboard-backend/modules/cache"
	"jannex/admin-dashboard-backend/modules/database"
	"jannex/admin-dashboard-backend/modules/structs"
	"jannex/admin-dashboard-backend/modules/utils"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/rs/zerolog/log"
	"golang.org/x/crypto/bcrypt"
)

func AddDynamicGroupTasksPermissionsByCategory(category string) []string {
	var newRolePermissions []structs.RolePermission
	var newPermissions []string

	for _, dynamicGroupTasksPermission := range utils.DynamicGroupTasksPermissions {
		convertedPermission := ConvertXYPermission(dynamicGroupTasksPermission, category)

		newRolePermissions = append(newRolePermissions, structs.RolePermission{
			RoleId:       GetMasterRoleId(),
			PermissionId: convertedPermission})

		newPermissions = append(newPermissions, convertedPermission)
	}

	database.DB.Create(newRolePermissions)

	return newPermissions
}

func RemoveDynamicGroupTasksPermissionsByCategory(category string) []string {
	var permissions []string

	for _, dynamicGroupTasksPermission := range utils.DynamicGroupTasksPermissions {
		convertedPermission := ConvertXYPermission(dynamicGroupTasksPermission, category)
		permissions = append(permissions, convertedPermission)

		database.DB.Where("permission_id = ?", convertedPermission).Delete(&structs.RolePermission{})
	}

	return permissions
}

func CreateMasterRoleIfNotExist() {
	masterRoleId := handleMasterRolePermissions()
	createDefaultAdminUser(masterRoleId)
}

func createDefaultAdminUser(masterRoleId string) {
	var userCount int64
	database.DB.Model(&structs.User{}).Count(&userCount)

	if userCount > 0 {
		return
	}

	log.Info().Msg("No users found. Master account was created:")

	pw := []byte("adminadmin")

	hashedPassword, err := bcrypt.GenerateFromPassword(pw, bcrypt.DefaultCost)

	if err != nil {
		panic(err)
	}

	user := structs.User{
		Id:        uuid.New().String(),
		RoleId:    masterRoleId,
		Username:  "admin",
		Email:     "admin@roese.dev",
		Password:  string(hashedPassword),
		CreatedAt: time.Now(),
	}

	database.DB.Create(&user)

	log.Info().Msgf("Username: %s", user.Username)
	log.Info().Msgf("Password: %s", string(pw))
}

func handleMasterRolePermissions() (roleId string) {
	// create admin role if not already existing
	role := structs.Role{
		Id:           uuid.New().String(),
		Master:       true,
		DisplayName:  "Admin",
		Description:  "Management board",
		CreatedAt:    time.Now(),
		SortingOrder: 0,
	}

	masterRoleId := GetMasterRoleId()

	if masterRoleId == "" {
		result := database.DB.Create(&role)

		if result.Error != nil {
			panic(result.Error)
		}

		masterRoleId = role.Id
	}

	// looking for role permissions
	var foundRolePermissions []structs.RolePermission

	database.DB.Where("role_id = ?", masterRoleId).Find(&foundRolePermissions)

	systemPermissions := getSystemPermissions()

	if len(foundRolePermissions) > 0 {
		// add new permissions if not already present
		var newPermissions []string

		for _, systemPermission := range systemPermissions {
			if !hasPermission(foundRolePermissions, systemPermission) {
				newPermissions = append(newPermissions, systemPermission)
			}
		}

		if len(newPermissions) > 0 {
			var newRolePermissions []structs.RolePermission

			for _, newPermission := range newPermissions {
				newRolePermissions = append(newRolePermissions, structs.RolePermission{
					RoleId:       masterRoleId,
					PermissionId: newPermission,
				})
			}

			database.DB.Create(newRolePermissions)
		}

		// deleting permissions that are no longer supported
		var outdatedPermissions []structs.RolePermission

		for _, foundRolePermission := range foundRolePermissions {
			if isPermissionOutdated(systemPermissions, foundRolePermission.PermissionId) {
				outdatedPermissions = append(outdatedPermissions, foundRolePermission)
			}
		}

		if len(outdatedPermissions) > 0 {
			for _, outdatedPermission := range outdatedPermissions {
				// delete old permissions for all roles
				database.DB.Where("permission_id = ?", outdatedPermission.PermissionId).Delete(&outdatedPermission)
			}
		}
	} else { // admin role has no permissions - grant all permissions
		var newRolePermissions []structs.RolePermission

		for _, systemPermission := range systemPermissions {
			newRolePermissions = append(newRolePermissions, structs.RolePermission{
				RoleId:       masterRoleId,
				PermissionId: systemPermission,
			})
		}

		database.DB.Create(newRolePermissions)
	}

	return masterRoleId
}

func hasPermission(rolePermissions []structs.RolePermission, permission string) bool {
	for _, rolePermission := range rolePermissions {
		if rolePermission.PermissionId == permission {
			return true
		}
	}

	return false
}

func isPermissionOutdated(systemPermissions []string, permission string) bool {
	for _, systemPermission := range systemPermissions {
		if systemPermission == permission {
			return false
		}
	}

	return true
}

func GetRoleSortingOrder() int {
	var lastSortingOrder int

	if err := database.DB.Model(&structs.Role{}).Select("MAX(sorting_order)").Scan(&lastSortingOrder).Error; err != nil {
		log.Error().Msgf("Error getting role sorting order %v", err.Error())
		return 0
	}

	return lastSortingOrder + 1
}

func ConvertXYPermission(permission string, category string) string {
	return strings.Replace(permission, "XY", strings.ToLower(category), 1)
}

func GetMasterRoleId() string {
	var masterRole structs.Role

	database.DB.Select("Id").First(&masterRole, "master = ?", true)

	return masterRole.Id
}

func getSystemPermissions() []string {
	permissions := utils.SystemPermissions

	for _, categoryGroup := range cache.GetCategoryGroups() {
		for _, dynamicGroupTasksPermission := range utils.DynamicGroupTasksPermissions {
			permissions = append(permissions, ConvertXYPermission(dynamicGroupTasksPermission, categoryGroup.Category))
		}
	}

	return permissions
}
