package delegationv1

import "errors"

// CapabilityGrantSets is the complete P6a issuance intersection. Fixed named
// axes prevent an issuer from accidentally omitting one authority and thereby
// widening the result. Session contains the result of the Connected App scope
// and session-class mapping; an empty or unknown mapping is an empty slice.
type CapabilityGrantSets struct {
	Workload        []string
	AppInstallation []string
	Session         []string
	Requested       []string
}

// ValidateCapabilityGrants validates one bounded, canonical authority set.
// Empty sets are valid and grant nothing. This does not validate requested or
// signed-token capability sets, which retain the smaller MaxCapabilities bound.
func ValidateCapabilityGrants(grants []string) error {
	return validateCapabilityGrants(grants, MaxAuthorityCapabilities)
}

func validateCapabilityGrants(grants []string, maximum int) error {
	if len(grants) > maximum {
		return errors.New("runtime delegation: too many capability grants")
	}
	previous := ""
	for _, capability := range grants {
		if !validCapability(capability) || len(capability) > MaxCapabilityBytes || capability <= previous {
			return errors.New("runtime delegation: capability grants must be canonical")
		}
		previous = capability
	}
	return nil
}

// IntersectCapabilities returns the sorted intersection of independently
// authoritative capability grants. An empty grant set (including an unknown
// scope or session-class mapping represented as no grants) yields no Runtime
// capability. It never unions grants or treats unknown values as pass-through.
func IntersectCapabilities(grants CapabilityGrantSets) ([]string, error) {
	if err := validateCapabilityGrants(grants.Requested, MaxCapabilities); err != nil {
		return nil, err
	}
	authorities := [3][]string{grants.Workload, grants.AppInstallation, grants.Session}
	sets := make([]map[string]struct{}, len(authorities))
	for i, authority := range authorities {
		if err := ValidateCapabilityGrants(authority); err != nil {
			return nil, err
		}
		sets[i] = make(map[string]struct{}, len(authority))
		for _, capability := range authority {
			sets[i][capability] = struct{}{}
		}
	}
	result := make([]string, 0, len(grants.Requested))
	for _, capability := range grants.Requested {
		presentEverywhere := true
		for _, authority := range sets {
			if _, present := authority[capability]; !present {
				presentEverywhere = false
				break
			}
		}
		if presentEverywhere {
			result = append(result, capability)
		}
	}
	return result, nil
}
