package repository

import (
	"context"

	"eslogad-be/internal/entities"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type RBACRepository struct {
	db *gorm.DB
}

func NewRBACRepository(db *gorm.DB) *RBACRepository { return &RBACRepository{db: db} }

// Permissions
func (r *RBACRepository) CreatePermission(ctx context.Context, p *entities.Permission) error {
	return r.db.WithContext(ctx).Create(p).Error
}
func (r *RBACRepository) UpdatePermission(ctx context.Context, p *entities.Permission) error {
	return r.db.WithContext(ctx).Model(&entities.Permission{}).Where("id = ?", p.ID).Updates(p).Error
}
func (r *RBACRepository) DeletePermission(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.Permission{}, "id = ?", id).Error
}
func (r *RBACRepository) ListPermissions(ctx context.Context) ([]entities.Permission, error) {
	var perms []entities.Permission
	if err := r.db.WithContext(ctx).Preload("Module").Order("code ASC").Find(&perms).Error; err != nil {
		return nil, err
	}
	return perms, nil
}
func (r *RBACRepository) GetPermissionByCode(ctx context.Context, code string) (*entities.Permission, error) {
	var p entities.Permission
	if err := r.db.WithContext(ctx).First(&p, "code = ?", code).Error; err != nil {
		return nil, err
	}
	return &p, nil
}

// Roles
func (r *RBACRepository) CreateRole(ctx context.Context, role *entities.Role) error {
	return r.db.WithContext(ctx).Create(role).Error
}
func (r *RBACRepository) UpdateRole(ctx context.Context, role *entities.Role) error {
	return r.db.WithContext(ctx).Model(&entities.Role{}).Where("id = ?", role.ID).Updates(role).Error
}
func (r *RBACRepository) DeleteRole(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.Role{}, "id = ?", id).Error
}
func (r *RBACRepository) ListRoles(ctx context.Context) ([]entities.Role, error) {
	var roles []entities.Role
	if err := r.db.WithContext(ctx).Order("name ASC").Find(&roles).Error; err != nil {
		return nil, err
	}
	return roles, nil
}
func (r *RBACRepository) GetRoleByCode(ctx context.Context, code string) (*entities.Role, error) {
	var role entities.Role
	if err := r.db.WithContext(ctx).First(&role, "code = ?", code).Error; err != nil {
		return nil, err
	}
	return &role, nil
}

func (r *RBACRepository) SetRolePermissionsByCodes(ctx context.Context, roleID uuid.UUID, permCodes []string) error {
	if err := r.db.WithContext(ctx).Where("role_id = ?", roleID).Delete(&entities.RolePermission{}).Error; err != nil {
		return err
	}
	if len(permCodes) == 0 {
		return nil
	}
	var perms []entities.Permission
	if err := r.db.WithContext(ctx).Where("code IN ?", permCodes).Find(&perms).Error; err != nil {
		return err
	}
	pairs := make([]entities.RolePermission, 0, len(perms))
	for _, p := range perms {
		pairs = append(pairs, entities.RolePermission{RoleID: roleID, PermissionID: p.ID})
	}
	return r.db.WithContext(ctx).Create(&pairs).Error
}

func (r *RBACRepository) GetPermissionsByRoleID(ctx context.Context, roleID uuid.UUID) ([]entities.Permission, error) {
	var perms []entities.Permission
	if err := r.db.WithContext(ctx).
		Preload("Module").
		Table("permissions p").
		Select("p.*").
		Joins("JOIN role_permissions rp ON rp.permission_id = p.id").
		Where("rp.role_id = ?", roleID).
		Find(&perms).Error; err != nil {
		return nil, err
	}
	return perms, nil
}

// Modules
func (r *RBACRepository) CreateModule(ctx context.Context, m *entities.Module) error {
	return r.db.WithContext(ctx).Create(m).Error
}

func (r *RBACRepository) UpdateModule(ctx context.Context, m *entities.Module) error {
	return r.db.WithContext(ctx).Model(&entities.Module{}).Where("id = ?", m.ID).Updates(m).Error
}

func (r *RBACRepository) DeleteModule(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.Module{}, "id = ?", id).Error
}

func (r *RBACRepository) ListModules(ctx context.Context) ([]entities.Module, error) {
	var modules []entities.Module
	if err := r.db.WithContext(ctx).Order("name ASC").Find(&modules).Error; err != nil {
		return nil, err
	}
	return modules, nil
}

func (r *RBACRepository) GetModuleByCode(ctx context.Context, code string) (*entities.Module, error) {
	var m entities.Module
	if err := r.db.WithContext(ctx).First(&m, "code = ?", code).Error; err != nil {
		return nil, err
	}
	return &m, nil
}

func (r *RBACRepository) GetModuleByID(ctx context.Context, id uuid.UUID) (*entities.Module, error) {
	var m entities.Module
	if err := r.db.WithContext(ctx).First(&m, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return &m, nil
}

func (r *RBACRepository) GetPermissionsGroupedByModule(ctx context.Context) ([]entities.Module, error) {
	var modules []entities.Module
	if err := r.db.WithContext(ctx).
		Preload("Permissions", func(db *gorm.DB) *gorm.DB {
			return db.Order("action ASC")
		}).
		Find(&modules).Error; err != nil {
		return nil, err
	}
	return modules, nil
}

func (r *RBACRepository) GetRoleByID(ctx context.Context, id uuid.UUID) (*entities.Role, error) {
	var role entities.Role
	if err := r.db.WithContext(ctx).First(&role, "id = ?", id).Error; err != nil {
		return nil, err
	}
	return &role, nil
}

func (r *RBACRepository) SetRolePermissionsByIDs(ctx context.Context, roleID uuid.UUID, permissionIDs []uuid.UUID) error {
	return r.db.Transaction(func(tx *gorm.DB) error {
		// Delete existing permissions
		if err := tx.WithContext(ctx).Where("role_id = ?", roleID).Delete(&entities.RolePermission{}).Error; err != nil {
			return err
		}
		
		// Add new permissions
		if len(permissionIDs) == 0 {
			return nil
		}
		
		pairs := make([]entities.RolePermission, 0, len(permissionIDs))
		for _, permID := range permissionIDs {
			pairs = append(pairs, entities.RolePermission{RoleID: roleID, PermissionID: permID})
		}
		return tx.WithContext(ctx).Create(&pairs).Error
	})
}
