package training

import (
	"context"
	"time"

	"github.com/google/uuid"
)

// timeNow is a package-level function for testing
var timeNow = time.Now

// ComputeAssignments calculates all necessary assignments for a user based on
// their roles, existing assignments, and deadlines. Returns new assignments
// that need to be created.
func ComputeAssignments(ctx context.Context, store *Store, tenantID uuid.UUID,
	userID uuid.UUID, userName, userEmail string, roleCodes []string, trigger string) ([]TrainingAssignment, error) {

	if trigger == "" {
		trigger = string(TriggerManual)
	}

	// Get all required modules for the user's roles
	requiredModules, err := ComputeRequiredModules(ctx, store, tenantID, roleCodes)
	if err != nil {
		return nil, err
	}

	// Get existing active assignments for this user
	existingAssignments, _, err := store.ListAssignments(ctx, tenantID, &AssignmentFilters{
		UserID: &userID,
		Limit:  1000,
	})
	if err != nil {
		return nil, err
	}

	// Build a map of existing assignments by module_id for quick lookup
	existingByModule := make(map[uuid.UUID]*TrainingAssignment)
	for i := range existingAssignments {
		a := &existingAssignments[i]
		// Only consider non-expired, non-completed-and-expired assignments
		if a.Status != AssignmentStatusExpired {
			existingByModule[a.ModuleID] = a
		}
	}

	var newAssignments []TrainingAssignment
	now := timeNow().UTC()

	for _, module := range requiredModules {
		existing, hasExisting := existingByModule[module.ID]

		// Skip if there's an active, valid assignment
		if hasExisting {
			switch existing.Status {
			case AssignmentStatusCompleted:
				// Check if the completed assignment is still valid
				if existing.CompletedAt != nil {
					validUntil := existing.CompletedAt.AddDate(0, 0, module.ValidityDays)
					if validUntil.After(now) {
						continue // Still valid, skip
					}
				}
			case AssignmentStatusPending, AssignmentStatusInProgress:
				continue // Assignment exists and is active
			case AssignmentStatusOverdue:
				continue // Already tracked as overdue
			}
		}

		// Determine the role code for this assignment
		roleCode := ""
		for _, role := range roleCodes {
			entries, err := store.GetMatrixForRole(ctx, tenantID, role)
			if err != nil {
				return nil, err
			}
			for _, entry := range entries {
				if entry.ModuleID == module.ID {
					roleCode = role
					break
				}
			}
			if roleCode != "" {
				break
			}
		}

		// Calculate deadline based on frequency
		var deadline time.Time
		switch module.FrequencyType {
		case FrequencyOnboarding:
			deadline = now.AddDate(0, 0, 30)  // 30 days for onboarding
		case FrequencyMicro:
			deadline = now.AddDate(0, 0, 14)  // 14 days for micro
		default:
			deadline = now.AddDate(0, 0, 90)  // 90 days default
		}

		assignment := TrainingAssignment{
			TenantID:    tenantID,
			ModuleID:    module.ID,
			UserID:      userID,
			UserName:    userName,
			UserEmail:   userEmail,
			RoleCode:    roleCode,
			TriggerType: TriggerType(trigger),
			Status:      AssignmentStatusPending,
			Deadline:    deadline,
			ModuleCode:  module.ModuleCode,
			ModuleTitle: module.Title,
		}

		// Create the assignment in the store
		if err := store.CreateAssignment(ctx, &assignment); err != nil {
			return nil, err
		}

		// Log the assignment
		store.LogAction(ctx, &AuditLogEntry{
			TenantID:   tenantID,
			UserID:     &userID,
			Action:     AuditActionAssigned,
			EntityType: AuditEntityAssignment,
			EntityID:   &assignment.ID,
			Details: map[string]interface{}{
				"module_code": module.ModuleCode,
				"trigger":     trigger,
				"role_code":   roleCode,
				"deadline":    deadline.Format(time.RFC3339),
			},
		})

		newAssignments = append(newAssignments, assignment)
	}

	if newAssignments == nil {
		newAssignments = []TrainingAssignment{}
	}

	return newAssignments, nil
}

// BulkAssign assigns a module to all users with specific roles
// Returns the number of assignments created
func BulkAssign(ctx context.Context, store *Store, tenantID uuid.UUID,
	moduleID uuid.UUID, users []UserInfo, trigger string, deadline time.Time) (int, error) {

	if trigger == "" {
		trigger = string(TriggerManual)
	}

	count := 0
	for _, user := range users {
		assignment := TrainingAssignment{
			TenantID:    tenantID,
			ModuleID:    moduleID,
			UserID:      user.UserID,
			UserName:    user.UserName,
			UserEmail:   user.UserEmail,
			RoleCode:    user.RoleCode,
			TriggerType: TriggerType(trigger),
			Status:      AssignmentStatusPending,
			Deadline:    deadline,
		}

		if err := store.CreateAssignment(ctx, &assignment); err != nil {
			return count, err
		}
		count++
	}

	return count, nil
}

// UserInfo contains basic user information for bulk operations
type UserInfo struct {
	UserID   uuid.UUID `json:"user_id"`
	UserName string    `json:"user_name"`
	UserEmail string   `json:"user_email"`
	RoleCode string    `json:"role_code"`
}
