package connector_workqueue

import (
	"context"
	"strings"
)

type claimQueueClassPreferenceContextKey struct{}
type claimWorkTypesContextKey struct{}
type distributedClaimContextKey struct{}

// WithClaimQueueClassPreference attaches an optional first-choice queue-class
// tier to one WorkItem claim request. It is a non-durable soft affinity:
// QueueClasses in ClaimWorkItemInput still controls eligibility, and stale-only
// claims ignore the preference. Classes outside a non-empty eligibility set are
// discarded.
func WithClaimQueueClassPreference(ctx context.Context, queueClasses []string) context.Context {
	if ctx == nil {
		ctx = context.Background()
	}
	return context.WithValue(ctx, claimQueueClassPreferenceContextKey{}, normalizeClaimQueueClasses("", queueClasses))
}

// ClaimQueueClassPreference returns the normalized request-local preference.
// Callers normally only need WithClaimQueueClassPreference; this accessor keeps
// adapters able to preserve the capability when forwarding a request.
func ClaimQueueClassPreference(ctx context.Context) []string {
	if ctx == nil {
		return nil
	}
	queueClasses, _ := ctx.Value(claimQueueClassPreferenceContextKey{}).([]string)
	return append([]string(nil), queueClasses...)
}

// WithClaimWorkTypes scopes a shared WorkItem queue to the types understood by
// one executor. An empty scope preserves the generic repository behavior.
func WithClaimWorkTypes(ctx context.Context, workTypes []string) context.Context {
	if ctx == nil {
		ctx = context.Background()
	}
	return context.WithValue(ctx, claimWorkTypesContextKey{}, normalizeWorkTypes(workTypes))
}

// ClaimWorkTypes returns the normalized request-local executor scope.
func ClaimWorkTypes(ctx context.Context) []string {
	if ctx == nil {
		return nil
	}
	workTypes, _ := ctx.Value(claimWorkTypesContextKey{}).([]string)
	return append([]string(nil), workTypes...)
}

func claimWorkTypeAllowed(ctx context.Context, workType string) bool {
	scope := ClaimWorkTypes(ctx)
	if len(scope) == 0 {
		return true
	}
	workType = strings.TrimSpace(workType)
	for _, allowed := range scope {
		if workType == allowed {
			return true
		}
	}
	return false
}

// WithDistributedClaim makes WorkItem row CAS and lane-head ordering the claim
// authority for this request. The pack-wide SchedulerLease remains available
// to older callers, but it does not gate distributed runtime executors.
func WithDistributedClaim(ctx context.Context) context.Context {
	if ctx == nil {
		ctx = context.Background()
	}
	return context.WithValue(ctx, distributedClaimContextKey{}, true)
}

// DistributedClaim reports whether this request uses WorkItem-local fencing.
func DistributedClaim(ctx context.Context) bool {
	if ctx == nil {
		return false
	}
	distributed, _ := ctx.Value(distributedClaimContextKey{}).(bool)
	return distributed
}

func claimQueueClassPreferenceForEligibility(ctx context.Context, eligible []string) []string {
	preferred := ClaimQueueClassPreference(ctx)
	if len(eligible) == 0 || len(preferred) == 0 {
		return preferred
	}
	allowed := make(map[string]struct{}, len(eligible))
	for _, class := range eligible {
		allowed[strings.TrimSpace(class)] = struct{}{}
	}
	filtered := make([]string, 0, len(preferred))
	for _, class := range preferred {
		if _, ok := allowed[class]; ok {
			filtered = append(filtered, class)
		}
	}
	return filtered
}
