package processor

import (
	"context"
	"time"

	"eslogad-be/internal/entities"

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

type UserRoleProcessor interface {
	AssignRoleToUser(ctx context.Context, userID, roleID uuid.UUID) error
	RemoveRoleFromUser(ctx context.Context, userID, roleID uuid.UUID) error
	GetUserRoles(ctx context.Context, userID uuid.UUID) ([]entities.Role, error)
	HasRole(ctx context.Context, userID uuid.UUID, roleCode string) (bool, error)
}

type UserRoleProcessorImpl struct {
	db *gorm.DB
}

func NewUserRoleProcessor(db *gorm.DB) *UserRoleProcessorImpl {
	return &UserRoleProcessorImpl{
		db: db,
	}
}

type UserRoleEntry struct {
	ID         uuid.UUID  `gorm:"type:uuid;primary_key;default:gen_random_uuid()"`
	UserID     uuid.UUID  `gorm:"type:uuid;not null"`
	RoleID     uuid.UUID  `gorm:"type:uuid;not null"`
	AssignedAt time.Time  `gorm:"default:CURRENT_TIMESTAMP"`
	RemovedAt  *time.Time
}

func (UserRoleEntry) TableName() string {
	return "user_role"
}

func (p *UserRoleProcessorImpl) AssignRoleToUser(ctx context.Context, userID, roleID uuid.UUID) error {
	var existingEntry UserRoleEntry
	err := p.db.WithContext(ctx).
		Where("user_id = ? AND role_id = ? AND removed_at IS NULL", userID, roleID).
		First(&existingEntry).Error

	if err == nil {
		return nil
	}

	if err != gorm.ErrRecordNotFound {
		return err
	}

	newEntry := UserRoleEntry{
		UserID:     userID,
		RoleID:     roleID,
		AssignedAt: time.Now(),
	}

	return p.db.WithContext(ctx).Create(&newEntry).Error
}

func (p *UserRoleProcessorImpl) RemoveRoleFromUser(ctx context.Context, userID, roleID uuid.UUID) error {
	now := time.Now()
	return p.db.WithContext(ctx).
		Model(&UserRoleEntry{}).
		Where("user_id = ? AND role_id = ? AND removed_at IS NULL", userID, roleID).
		Update("removed_at", now).Error
}

func (p *UserRoleProcessorImpl) GetUserRoles(ctx context.Context, userID uuid.UUID) ([]entities.Role, error) {
	var roles []entities.Role
	err := p.db.WithContext(ctx).
		Table("roles as r").
		Select("r.*").
		Joins("JOIN user_role ur ON ur.role_id = r.id AND ur.removed_at IS NULL").
		Where("ur.user_id = ?", userID).
		Find(&roles).Error
	return roles, err
}

func (p *UserRoleProcessorImpl) HasRole(ctx context.Context, userID uuid.UUID, roleCode string) (bool, error) {
	var count int64
	err := p.db.WithContext(ctx).
		Table("user_role as ur").
		Joins("JOIN roles r ON r.id = ur.role_id").
		Where("ur.user_id = ? AND r.code = ? AND ur.removed_at IS NULL", userID, roleCode).
		Count(&count).Error
	
	if err != nil {
		return false, err
	}
	
	return count > 0, nil
}