package services

import (
	"errors"
	"lost-and-found/internal/models"
	"lost-and-found/internal/repositories"

	"gorm.io/gorm"
)

type RoleService struct {
	roleRepo *repositories.RoleRepository
}

func NewRoleService(db *gorm.DB) *RoleService {
	return &RoleService{
		roleRepo: repositories.NewRoleRepository(db),
	}
}

// Structs for Requests
type CreateRoleRequest struct {
	Name          string `json:"name" binding:"required"`
	Description   string `json:"description"`
	PermissionIDs []uint `json:"permission_ids"`
}

type UpdateRoleRequest struct {
	Name          string `json:"name"`
	Description   string `json:"description"`
	PermissionIDs []uint `json:"permission_ids"`
}

// GetAllRoles returns all roles with permissions
func (s *RoleService) GetAllRoles() ([]models.Role, error) {
	return s.roleRepo.FindAllWithPermissions()
}

// GetAllPermissions returns list of all permissions
func (s *RoleService) GetAllPermissions() ([]models.Permission, error) {
	return s.roleRepo.FindAllPermissions()
}

// CreateRole creates a new role and assigns permissions
func (s *RoleService) CreateRole(req CreateRoleRequest) (*models.Role, error) {
	// Check if role name already exists
	existing, _ := s.roleRepo.FindByName(req.Name)
	if existing != nil {
		return nil, errors.New("role name already exists")
	}

	role := &models.Role{
		Name:        req.Name,
		Description: req.Description,
	}

	// 1. Create Role
	if err := s.roleRepo.Create(role); err != nil {
		return nil, err
	}

	// 2. Assign Permissions
	if len(req.PermissionIDs) > 0 {
		if err := s.roleRepo.UpdatePermissions(role, req.PermissionIDs); err != nil {
			return nil, err
		}
	}

	// Reload to return complete object
	return s.roleRepo.FindByID(role.ID)
}

// UpdateRole updates role details and permissions
func (s *RoleService) UpdateRole(id uint, req UpdateRoleRequest) (*models.Role, error) {
	role, err := s.roleRepo.FindByID(id)
	if err != nil {
		return nil, errors.New("role not found")
	}

	// Protect core roles from name changes
	if (role.Name == "admin" || role.Name == "user" || role.Name == "manager") && req.Name != role.Name {
		return nil, errors.New("cannot change name of system roles")
	}

	// Update fields
	if req.Name != "" {
		role.Name = req.Name
	}
	role.Description = req.Description

	// Update Permissions
	if err := s.roleRepo.UpdatePermissions(role, req.PermissionIDs); err != nil {
		return nil, err
	}

	return s.roleRepo.FindByID(id)
}

// DeleteRole deletes a role
func (s *RoleService) DeleteRole(id uint) error {
	role, err := s.roleRepo.FindByID(id)
	if err != nil {
		return errors.New("role not found")
	}

	// Prevent deleting core system roles
	if role.Name == "admin" || role.Name == "user" || role.Name == "manager" {
		return errors.New("cannot delete core system roles")
	}

	return s.roleRepo.Delete(id)
}