package oauthroutes

import (
	"strings"
	"sync"
)

// ScopeOwner binds a product-contributed OAuth route scope to the app packs
// that may claim it. The registry is process assembly state, not metadata.
type ScopeOwner struct {
	Scope          string
	OwningAppPacks []string
}

type ScopePolicy struct{}

var routeScopes = struct {
	sync.RWMutex
	owners map[string]map[string]struct{}
}{owners: map[string]map[string]struct{}{}}

func NormalizeScope(raw string) string { return strings.ToLower(strings.TrimSpace(raw)) }

func (ScopePolicy) Allows(scope string) bool {
	normalized := NormalizeScope(scope)
	if normalized == "" {
		return false
	}
	if IsPlatformScope(normalized) {
		return true
	}
	routeScopes.RLock()
	defer routeScopes.RUnlock()
	_, ok := routeScopes.owners[normalized]
	return ok
}

func (ScopePolicy) AllowsForPack(scope, packName string) bool {
	normalized := NormalizeScope(scope)
	packName = normalizePackName(packName)
	if normalized == "" || packName == "" {
		return false
	}
	if IsPlatformScope(normalized) {
		return true
	}
	routeScopes.RLock()
	defer routeScopes.RUnlock()
	owners, ok := routeScopes.owners[normalized]
	if !ok {
		return false
	}
	_, ok = owners[packName]
	return ok
}

func SetScopeOwners(scopes []ScopeOwner) func() {
	next := make(map[string]map[string]struct{}, len(scopes))
	for _, scope := range scopes {
		normalized := NormalizeScope(scope.Scope)
		if normalized == "" || IsPlatformScope(normalized) {
			continue
		}
		owners := next[normalized]
		if owners == nil {
			owners = map[string]struct{}{}
			next[normalized] = owners
		}
		for _, packName := range scope.OwningAppPacks {
			if packName = normalizePackName(packName); packName != "" {
				owners[packName] = struct{}{}
			}
		}
	}
	routeScopes.Lock()
	previous := routeScopes.owners
	routeScopes.owners = next
	routeScopes.Unlock()
	return func() {
		routeScopes.Lock()
		routeScopes.owners = previous
		routeScopes.Unlock()
	}
}

func IsPlatformScope(scope string) bool { return NormalizeScope(scope) == "openid" }

func normalizePackName(packName string) string { return strings.ToLower(strings.TrimSpace(packName)) }
