package auth

import (
	"strings"
	"sync"

	"github.com/nexuscrm/kernel/pkg/oauthroutes"
)

type ProductOAuthSessionIssuerClassOwner struct {
	Class          string
	OwningAppPacks []string
}

type ProductOAuthRouteScopeOwner = oauthroutes.ScopeOwner

type ProductOAuthSessionIssuerClassPolicy struct{}

type ProductOAuthRouteScopePolicy struct{}

var productOAuthSessionIssuerClasses = struct {
	sync.RWMutex
	classes map[string]map[string]struct{}
}{
	classes: map[string]map[string]struct{}{},
}

func NormalizeProductCapabilityToken(raw string) string {
	normalized := strings.ToLower(strings.TrimSpace(raw))
	return strings.ReplaceAll(normalized, "-", "_")
}

func NormalizeOAuthScopeName(raw string) string {
	return oauthroutes.NormalizeScope(raw)
}

func (ProductOAuthSessionIssuerClassPolicy) AllowsConnectedAppSessionIssuerClass(class string) bool {
	normalized := NormalizeProductCapabilityToken(class)
	if normalized == "" {
		return false
	}
	productOAuthSessionIssuerClasses.RLock()
	defer productOAuthSessionIssuerClasses.RUnlock()
	_, ok := productOAuthSessionIssuerClasses.classes[normalized]
	return ok
}

func (ProductOAuthSessionIssuerClassPolicy) AllowsConnectedAppSessionIssuerClassForPack(class, packName string) bool {
	normalized := NormalizeProductCapabilityToken(class)
	packName = normalizeOAuthSessionIssuerPackName(packName)
	if normalized == "" || packName == "" {
		return false
	}
	productOAuthSessionIssuerClasses.RLock()
	defer productOAuthSessionIssuerClasses.RUnlock()
	owners, ok := productOAuthSessionIssuerClasses.classes[normalized]
	if !ok {
		return false
	}
	_, ok = owners[packName]
	return ok
}

func (ProductOAuthRouteScopePolicy) AllowsOAuthRouteScope(scope string) bool {
	return oauthroutes.ScopePolicy{}.Allows(scope)
}

func (ProductOAuthRouteScopePolicy) AllowsOAuthRouteScopeForPack(scope, packName string) bool {
	return oauthroutes.ScopePolicy{}.AllowsForPack(scope, packName)
}

func SetProductOAuthSessionIssuerClasses(classes []string) func() {
	owners := make([]ProductOAuthSessionIssuerClassOwner, 0, len(classes))
	for _, class := range classes {
		owners = append(owners, ProductOAuthSessionIssuerClassOwner{Class: class})
	}
	return SetProductOAuthSessionIssuerClassOwners(owners)
}

func SetProductOAuthSessionIssuerClassOwners(classes []ProductOAuthSessionIssuerClassOwner) func() {
	next := make(map[string]map[string]struct{}, len(classes))
	for _, class := range classes {
		normalized := NormalizeProductCapabilityToken(class.Class)
		if normalized == "" {
			continue
		}
		owners := next[normalized]
		if owners == nil {
			owners = map[string]struct{}{}
			next[normalized] = owners
		}
		for _, packName := range class.OwningAppPacks {
			packName = normalizeOAuthSessionIssuerPackName(packName)
			if packName != "" {
				owners[packName] = struct{}{}
			}
		}
	}

	productOAuthSessionIssuerClasses.Lock()
	previous := productOAuthSessionIssuerClasses.classes
	productOAuthSessionIssuerClasses.classes = next
	productOAuthSessionIssuerClasses.Unlock()

	return func() {
		productOAuthSessionIssuerClasses.Lock()
		productOAuthSessionIssuerClasses.classes = previous
		productOAuthSessionIssuerClasses.Unlock()
	}
}

func SetProductOAuthRouteScopeOwners(scopes []ProductOAuthRouteScopeOwner) func() {
	return oauthroutes.SetScopeOwners(scopes)
}

func IsPlatformOAuthRouteScope(scope string) bool {
	return oauthroutes.IsPlatformScope(scope)
}

func normalizeOAuthSessionIssuerPackName(packName string) string {
	return normalizeOAuthProductPackName(packName)
}

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