package auth

import (
	stdErrors "errors"
	"fmt"
	"strings"
)

var ErrTokenResponseProfileConflict = stdErrors.New("multiple OAuth token response profiles match request")

type TokenResponseProfileClaims struct {
	ProfileID string
	Claims    map[string]any
}

func NormalizeTokenResponseProfiles(profiles []TokenResponseProfileRegistration) []TokenResponseProfileRegistration {
	out := make([]TokenResponseProfileRegistration, 0, len(profiles))
	seen := map[string]struct{}{}
	for _, profile := range profiles {
		profileID := NormalizeProductCapabilityToken(profile.ProfileID)
		if profileID == "" || profile.Applies == nil || profile.Build == nil {
			continue
		}
		if _, ok := seen[profileID]; ok {
			continue
		}
		seen[profileID] = struct{}{}
		profile.ProfileID = profileID
		out = append(out, profile)
	}
	return out
}

func BuildTokenResponseProfileClaims(ctx TokenResponseProfileContext, profiles []TokenResponseProfileRegistration) (TokenResponseProfileClaims, error) {
	var match TokenResponseProfileClaims
	for _, profile := range NormalizeTokenResponseProfiles(profiles) {
		if !profile.Applies(ctx) {
			continue
		}
		if match.ProfileID != "" {
			return TokenResponseProfileClaims{}, fmt.Errorf("%w: %s and %s", ErrTokenResponseProfileConflict, match.ProfileID, profile.ProfileID)
		}
		match = TokenResponseProfileClaims{
			ProfileID: profile.ProfileID,
			Claims:    copyTokenResponseProfileClaims(profile.Build(ctx)),
		}
	}
	return match, nil
}

func copyTokenResponseProfileClaims(claims map[string]any) map[string]any {
	if len(claims) == 0 {
		return nil
	}
	copied := make(map[string]any, len(claims))
	for key, value := range claims {
		key = strings.TrimSpace(key)
		if key == "" || isReservedTokenResponseField(key) {
			continue
		}
		copied[key] = value
	}
	return copied
}

func isReservedTokenResponseField(key string) bool {
	switch strings.ToLower(strings.TrimSpace(key)) {
	case "access_token", "token_type", "instance_url", "issued_at":
		return true
	default:
		return false
	}
}
