package services

import (
	"context"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"time"

	"veza-backend-api/internal/models"

	"gorm.io/gorm"
)

// RoleService gère les rôles et permissions
type RoleService struct {
	db *gorm.DB
}

// NewRoleService crée un nouveau service de rôles
func NewRoleService(db *gorm.DB) *RoleService {
	return &RoleService{db: db}
}

// GetRoles récupère tous les rôles avec leurs permissions
func (s *RoleService) GetRoles(ctx context.Context) ([]models.Role, error) {
	var roles []models.Role
	if err := s.db.WithContext(ctx).Preload("Permissions").Find(&roles).Error; err != nil {
		return nil, fmt.Errorf("failed to get roles: %w", err)
	}
	return roles, nil
}

// GetRole récupère un rôle par son ID avec ses permissions
func (s *RoleService) GetRole(ctx context.Context, roleID uuid.UUID) (*models.Role, error) {
	var role models.Role
	if err := s.db.WithContext(ctx).Preload("Permissions").First(&role, roleID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, fmt.Errorf("role not found")
		}
		return nil, fmt.Errorf("failed to get role: %w", err)
	}
	return &role, nil
}

// CreateRole crée un nouveau rôle
func (s *RoleService) CreateRole(ctx context.Context, role *models.Role) error {
	if err := s.db.WithContext(ctx).Create(role).Error; err != nil {
		return fmt.Errorf("failed to create role: %w", err)
	}
	return nil
}

// UpdateRole met à jour un rôle (seulement les rôles non-système)
func (s *RoleService) UpdateRole(ctx context.Context, roleID uuid.UUID, updates *models.Role) error {
	result := s.db.WithContext(ctx).Model(&models.Role{}).Where("id = ? AND is_system = ?", roleID, false).Updates(updates)
	if result.Error != nil {
		return fmt.Errorf("failed to update role: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("role not found or is system role")
	}
	return nil
}

// DeleteRole supprime un rôle (seulement les rôles non-système)
func (s *RoleService) DeleteRole(ctx context.Context, roleID uuid.UUID) error {
	var role models.Role
	if err := s.db.WithContext(ctx).First(&role, roleID).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return fmt.Errorf("role not found")
		}
		return fmt.Errorf("failed to get role: %w", err)
	}
	if role.IsSystem {
		return fmt.Errorf("cannot delete system role")
	}
	if err := s.db.WithContext(ctx).Delete(&role).Error; err != nil {
		return fmt.Errorf("failed to delete role: %w", err)
	}
	return nil
}

// AssignRoleToUser assigne un rôle à un utilisateur
// MIGRATION UUID: userID, roleID et assignedBy migrés vers uuid.UUID
func (s *RoleService) AssignRoleToUser(ctx context.Context, userID uuid.UUID, roleID uuid.UUID, assignedBy uuid.UUID, expiresAt *time.Time) error {
	userRole := &models.UserRole{
		UserID:     userID,
		RoleID:     roleID,
		AssignedBy: &assignedBy, // UUID
		AssignedAt: time.Now(),
		ExpiresAt:  expiresAt,
		IsActive:   true,
	}
	if err := s.db.WithContext(ctx).Create(userRole).Error; err != nil {
		return fmt.Errorf("failed to assign role: %w", err)
	}
	return nil
}

// RevokeRoleFromUser révoque un rôle d'un utilisateur
func (s *RoleService) RevokeRoleFromUser(ctx context.Context, userID uuid.UUID, roleID uuid.UUID) error {
	result := s.db.WithContext(ctx).Model(&models.UserRole{}).
		Where("user_id = ? AND role_id = ?", userID, roleID).
		Update("is_active", false)
	if result.Error != nil {
		return fmt.Errorf("failed to revoke role: %w", result.Error)
	}
	if result.RowsAffected == 0 {
		return fmt.Errorf("role assignment not found")
	}
	return nil
}

// GetUserRoles récupère tous les rôles actifs d'un utilisateur
func (s *RoleService) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]models.Role, error) {
	var roles []models.Role
	if err := s.db.WithContext(ctx).
		Table("roles").
		Joins("JOIN user_roles ON roles.id = user_roles.role_id").
		Where("user_roles.user_id = ? AND user_roles.is_active = ?", userID, true).
		Preload("Permissions").
		Find(&roles).Error; err != nil {
		return nil, fmt.Errorf("failed to get user roles: %w", err)
	}
	return roles, nil
}

// HasRole vérifie si un utilisateur a un rôle spécifique
func (s *RoleService) HasRole(ctx context.Context, userID uuid.UUID, roleName string) (bool, error) {
	var count int64
	if err := s.db.WithContext(ctx).
		Table("user_roles").
		Joins("JOIN roles ON user_roles.role_id = roles.id").
		Where("user_roles.user_id = ? AND user_roles.is_active = ? AND roles.name = ?", userID, true, roleName).
		Count(&count).Error; err != nil {
		return false, fmt.Errorf("failed to check role: %w", err)
	}
	return count > 0, nil
}

// HasPermission vérifie si un utilisateur a une permission spécifique via ses rôles
func (s *RoleService) HasPermission(ctx context.Context, userID uuid.UUID, resource, action string) (bool, error) {
	var count int64
	if err := s.db.WithContext(ctx).
		Table("permissions").
		Joins("JOIN role_permissions ON permissions.id = role_permissions.permission_id").
		Joins("JOIN user_roles ON role_permissions.role_id = user_roles.role_id").
		Where("user_roles.user_id = ? AND user_roles.is_active = ? AND permissions.resource = ? AND permissions.action = ?",
			userID, true, resource, action).
		Count(&count).Error; err != nil {
		return false, fmt.Errorf("failed to check permission: %w", err)
	}
	return count > 0, nil
}
