package datasets

import (
	"maps"
	"slices"

	"github.com/authzed/spicedb/internal/caveats"
	core "github.com/authzed/spicedb/pkg/proto/core/v1"
	"github.com/authzed/spicedb/pkg/spiceerrors"
	"github.com/authzed/spicedb/pkg/tuple"
)

var (
	caveatAnd        = caveats.And
	caveatOr         = caveats.Or
	caveatInvert     = caveats.Invert
	shortcircuitedOr = caveats.ShortcircuitedOr
)

// Subject is a subject that can be placed into a BaseSubjectSet. It is defined in a generic
// manner to allow implementations that wrap BaseSubjectSet to add their own additional bookkeeping
// to the base implementation.
type Subject[T any] interface {
	// GetSubjectId returns the ID of the subject. For wildcards, this should be `*`.
	GetSubjectId() string

	// GetCaveatExpression returns the caveat expression for this subject, if it is conditional.
	GetCaveatExpression() *core.CaveatExpression

	// GetExcludedSubjects returns the list of subjects excluded. Must only have values
	// for wildcards and must never be nested.
	GetExcludedSubjects() []T
}

// BaseSubjectSet defines a set that tracks accessible subjects, their exclusions (if wildcards),
// and all conditional expressions applied due to caveats.
//
// It is generic to allow other implementations to define the kind of tracking information
// associated with each subject.
//
// NOTE: Unlike a traditional set, unions between wildcards and a concrete subject will result
// in *both* being present in the set, to maintain the proper set semantics around wildcards.
type BaseSubjectSet[T Subject[T]] struct {
	constructor constructor[T]
	concrete    map[string]T
	wildcard    *handle[T]
}

// NewBaseSubjectSet creates a new base subject set for use underneath well-typed implementation.
//
// The constructor function returns a new instance of type T for a particular subject ID.
func NewBaseSubjectSet[T Subject[T]](constructor constructor[T]) BaseSubjectSet[T] {
	return BaseSubjectSet[T]{
		constructor: constructor,
		concrete:    map[string]T{},
		wildcard:    newHandle[T](),
	}
}

// constructor defines a function for constructing a new instance of the Subject type T for
// a subject ID, its (optional) conditional expression, any excluded subjects, and any sources
// for bookkeeping. The sources are those other subjects that were combined to create the current
// subject.
type constructor[T Subject[T]] func(subjectID string, conditionalExpression *core.CaveatExpression, excludedSubjects []T, sources ...T) T

// MustAdd adds the found subject to the set. This is equivalent to a Union operation between the
// existing set of subjects and a set containing the single subject, but modifies the set
// *in place*.
func (bss BaseSubjectSet[T]) MustAdd(foundSubject T) {
	err := bss.Add(foundSubject)
	if err != nil {
		panic(err)
	}
}

// Add adds the found subject to the set. This is equivalent to a Union operation between the
// existing set of subjects and a set containing the single subject, but modifies the set
// *in place*.
func (bss BaseSubjectSet[T]) Add(foundSubject T) error {
	if foundSubject.GetSubjectId() == tuple.PublicWildcard {
		existing := bss.wildcard.getOrNil()
		updated, err := unionWildcardWithWildcard(existing, foundSubject, bss.constructor)
		if err != nil {
			return err
		}

		bss.wildcard.setOrNil(updated)

		for _, concrete := range bss.concrete {
			updated = unionWildcardWithConcrete(updated, concrete, bss.constructor)
		}
		bss.wildcard.setOrNil(updated)
		return nil
	}

	var updatedOrNil *T
	if updated, ok := bss.concrete[foundSubject.GetSubjectId()]; ok {
		updatedOrNil = &updated
	}
	bss.setConcrete(foundSubject.GetSubjectId(), unionConcreteWithConcrete(updatedOrNil, &foundSubject, bss.constructor))

	wildcard := bss.wildcard.getOrNil()
	wildcard = unionWildcardWithConcrete(wildcard, foundSubject, bss.constructor)
	bss.wildcard.setOrNil(wildcard)
	return nil
}

func (bss BaseSubjectSet[T]) setConcrete(subjectID string, subjectOrNil *T) {
	if subjectOrNil == nil {
		delete(bss.concrete, subjectID)
		return
	}

	subject := *subjectOrNil
	bss.concrete[subject.GetSubjectId()] = subject
}

// Subtract subtracts the given subject found the set.
func (bss BaseSubjectSet[T]) Subtract(toRemove T) {
	if toRemove.GetSubjectId() == tuple.PublicWildcard {
		for _, concrete := range bss.concrete {
			bss.setConcrete(concrete.GetSubjectId(), subtractWildcardFromConcrete(concrete, toRemove, bss.constructor))
		}

		existing := bss.wildcard.getOrNil()
		updatedWildcard, concretesToAdd := subtractWildcardFromWildcard(existing, toRemove, bss.constructor)
		bss.wildcard.setOrNil(updatedWildcard)
		for _, concrete := range concretesToAdd {
			concrete := concrete
			bss.setConcrete(concrete.GetSubjectId(), &concrete)
		}
		return
	}

	if existing, ok := bss.concrete[toRemove.GetSubjectId()]; ok {
		bss.setConcrete(toRemove.GetSubjectId(), subtractConcreteFromConcrete(existing, toRemove, bss.constructor))
	}

	wildcard, ok := bss.wildcard.get()
	if ok {
		bss.wildcard.setOrNil(subtractConcreteFromWildcard(wildcard, toRemove, bss.constructor))
	}
}

// SubtractAll subtracts the other set of subjects from this set of subtracts, modifying this
// set *in place*.
func (bss BaseSubjectSet[T]) SubtractAll(other BaseSubjectSet[T]) {
	for _, otherSubject := range other.AsSlice() {
		bss.Subtract(otherSubject)
	}
}

// MustIntersectionDifference performs an intersection between this set and the other set, modifying
// this set *in place*.
func (bss BaseSubjectSet[T]) MustIntersectionDifference(other BaseSubjectSet[T]) {
	err := bss.IntersectionDifference(other)
	if err != nil {
		panic(err)
	}
}

// IntersectionDifference performs an intersection between this set and the other set, modifying
// this set *in place*.
func (bss BaseSubjectSet[T]) IntersectionDifference(other BaseSubjectSet[T]) error {
	// Intersect the wildcards of the sets, if any.
	existingWildcard := bss.wildcard.getOrNil()
	otherWildcard := other.wildcard.getOrNil()

	intersection, err := intersectWildcardWithWildcard(existingWildcard, otherWildcard, bss.constructor)
	if err != nil {
		return err
	}

	bss.wildcard.setOrNil(intersection)

	// Intersect the concretes of each set, as well as with the wildcards.
	updatedConcretes := make(map[string]T, len(bss.concrete))

	for _, concreteSubject := range bss.concrete {
		var otherConcreteOrNil *T
		if otherConcrete, ok := other.concrete[concreteSubject.GetSubjectId()]; ok {
			otherConcreteOrNil = &otherConcrete
		}

		concreteIntersected := intersectConcreteWithConcrete(concreteSubject, otherConcreteOrNil, bss.constructor)
		otherWildcardIntersected, err := intersectConcreteWithWildcard(concreteSubject, otherWildcard, bss.constructor)
		if err != nil {
			return err
		}

		result := unionConcreteWithConcrete(concreteIntersected, otherWildcardIntersected, bss.constructor)
		if result != nil {
			updatedConcretes[concreteSubject.GetSubjectId()] = *result
		}
	}

	if existingWildcard != nil {
		for _, otherSubject := range other.concrete {
			existingWildcardIntersect, err := intersectConcreteWithWildcard(otherSubject, existingWildcard, bss.constructor)
			if err != nil {
				return err
			}

			if existingUpdated, ok := updatedConcretes[otherSubject.GetSubjectId()]; ok {
				result := unionConcreteWithConcrete(&existingUpdated, existingWildcardIntersect, bss.constructor)
				updatedConcretes[otherSubject.GetSubjectId()] = *result
			} else if existingWildcardIntersect != nil {
				updatedConcretes[otherSubject.GetSubjectId()] = *existingWildcardIntersect
			}
		}
	}

	clear(bss.concrete)
	maps.Copy(bss.concrete, updatedConcretes)
	return nil
}

// UnionWith adds the given subjects to this set, via a union call.
func (bss BaseSubjectSet[T]) UnionWith(foundSubjects []T) error {
	for _, fs := range foundSubjects {
		err := bss.Add(fs)
		if err != nil {
			return err
		}
	}
	return nil
}

// UnionWithSet performs a union operation between this set and the other set, modifying this
// set *in place*.
func (bss BaseSubjectSet[T]) UnionWithSet(other BaseSubjectSet[T]) error {
	return bss.UnionWith(other.AsSlice())
}

// MustUnionWithSet performs a union operation between this set and the other set, modifying this
// set *in place*.
func (bss BaseSubjectSet[T]) MustUnionWithSet(other BaseSubjectSet[T]) {
	err := bss.UnionWithSet(other)
	if err != nil {
		panic(err)
	}
}

// Get returns the found subject with the given ID in the set, if any.
func (bss BaseSubjectSet[T]) Get(id string) (T, bool) {
	if id == tuple.PublicWildcard {
		return bss.wildcard.get()
	}

	found, ok := bss.concrete[id]
	return found, ok
}

// IsEmpty returns whether the subject set is empty.
func (bss BaseSubjectSet[T]) IsEmpty() bool {
	return bss.wildcard.getOrNil() == nil && len(bss.concrete) == 0
}

// AsSlice returns the contents of the subject set as a slice of found subjects.
func (bss BaseSubjectSet[T]) AsSlice() []T {
	values := slices.Collect(maps.Values(bss.concrete))
	if wildcard, ok := bss.wildcard.get(); ok {
		values = append(values, wildcard)
	}
	return values
}

// Clone returns a clone of this subject set. Note that this is a shallow clone.
// NOTE: Should only be used when performance is not a concern.
func (bss BaseSubjectSet[T]) Clone() BaseSubjectSet[T] {
	return BaseSubjectSet[T]{
		constructor: bss.constructor,
		concrete:    maps.Clone(bss.concrete),
		wildcard:    bss.wildcard.clone(),
	}
}

// UnsafeRemoveExact removes the *exact* matching subject, with no wildcard handling.
// This should ONLY be used for testing.
func (bss BaseSubjectSet[T]) UnsafeRemoveExact(foundSubject T) {
	if foundSubject.GetSubjectId() == tuple.PublicWildcard {
		bss.wildcard.clear()
		return
	}

	delete(bss.concrete, foundSubject.GetSubjectId())
}

// WithParentCaveatExpression returns a copy of the subject set with the parent caveat expression applied
// to all members of this set.
func (bss BaseSubjectSet[T]) WithParentCaveatExpression(parentCaveatExpr *core.CaveatExpression) BaseSubjectSet[T] {
	clone := bss.Clone()

	// Apply the parent caveat expression to the wildcard, if any.
	if wildcard, ok := clone.wildcard.get(); ok {
		constructed := bss.constructor(
			tuple.PublicWildcard,
			caveatAnd(parentCaveatExpr, wildcard.GetCaveatExpression()),
			wildcard.GetExcludedSubjects(),
			wildcard,
		)
		clone.wildcard.setOrNil(&constructed)
	}

	// Apply the parent caveat expression to each concrete.
	for subjectID, concrete := range clone.concrete {
		clone.concrete[subjectID] = bss.constructor(
			subjectID,
			caveatAnd(parentCaveatExpr, concrete.GetCaveatExpression()),
			nil,
			concrete,
		)
	}

	return clone
}

// unionWildcardWithWildcard performs a union operation over two wildcards, returning the updated
// wildcard (if any).
func unionWildcardWithWildcard[T Subject[T]](existing *T, adding T, constructor constructor[T]) (*T, error) {
	// If there is no existing wildcard, return the added one.
	if existing == nil {
		return &adding, nil
	}

	// Otherwise, union together the conditionals for the wildcards and *intersect* their exclusion
	// sets.
	existingWildcard := *existing
	expression := shortcircuitedOr(existingWildcard.GetCaveatExpression(), adding.GetCaveatExpression())

	// Exclusion sets are intersected because if an exclusion is missing from one wildcard
	// but not the other, the missing element will be, by definition, in that other wildcard.
	//
	// Examples:
	//
	//	{*} + {*} => {*}
	//	{* - {user:tom}} + {*} => {*}
	//	{* - {user:tom}} + {* - {user:sarah}} => {*}
	//	{* - {user:tom, user:sarah}} + {* - {user:sarah}} => {* - {user:sarah}}
	//	{*}[c1] + {*} => {*}
	//	{*}[c1] + {*}[c2] => {*}[c1 || c2]

	// NOTE: since we're only using concretes here, it is safe to reuse the BaseSubjectSet itself.
	exisingConcreteExclusions := NewBaseSubjectSet(constructor)
	for _, excludedSubject := range existingWildcard.GetExcludedSubjects() {
		if excludedSubject.GetSubjectId() == tuple.PublicWildcard {
			return nil, spiceerrors.MustBugf("wildcards are not allowed in exclusions")
		}

		err := exisingConcreteExclusions.Add(excludedSubject)
		if err != nil {
			return nil, err
		}
	}

	foundConcreteExclusions := NewBaseSubjectSet(constructor)
	for _, excludedSubject := range adding.GetExcludedSubjects() {
		if excludedSubject.GetSubjectId() == tuple.PublicWildcard {
			return nil, spiceerrors.MustBugf("wildcards are not allowed in exclusions")
		}

		err := foundConcreteExclusions.Add(excludedSubject)
		if err != nil {
			return nil, err
		}
	}

	err := exisingConcreteExclusions.IntersectionDifference(foundConcreteExclusions)
	if err != nil {
		return nil, err
	}

	constructed := constructor(
		tuple.PublicWildcard,
		expression,
		exisingConcreteExclusions.AsSlice(),
		*existing,
		adding)
	return &constructed, nil
}

// unionWildcardWithConcrete performs a union operation between a wildcard and a concrete subject
// being added to the set, returning the updated wildcard (if applicable).
func unionWildcardWithConcrete[T Subject[T]](existing *T, adding T, constructor constructor[T]) *T {
	// If there is no existing wildcard, nothing more to do.
	if existing == nil {
		return nil
	}

	// If the concrete is in the exclusion set, remove it if not conditional. Otherwise, mark
	// it as conditional.
	//
	// Examples:
	//  {*} | {user:tom} => {*} (and user:tom in the concrete)
	//  {* - {user:tom}} | {user:tom} => {*} (and user:tom in the concrete)
	//  {* - {user:tom}[c1]} | {user:tom}[c2] => {* - {user:tom}[c1 && !c2]} (and user:tom in the concrete)
	existingWildcard := *existing
	updatedExclusions := make([]T, 0, len(existingWildcard.GetExcludedSubjects()))
	for _, existingExclusion := range existingWildcard.GetExcludedSubjects() {
		if existingExclusion.GetSubjectId() == adding.GetSubjectId() {
			// If the conditional on the concrete is empty, then the concrete is always present, so
			// we remove the exclusion entirely.
			if adding.GetCaveatExpression() == nil {
				continue
			}

			// Otherwise, the conditional expression for the new exclusion is the existing expression &&
			// the *inversion* of the concrete's expression, as the exclusion will only apply if the
			// concrete subject is not present and the exclusion's expression is true.
			exclusionConditionalExpression := caveatAnd(
				existingExclusion.GetCaveatExpression(),
				caveatInvert(adding.GetCaveatExpression()),
			)

			updatedExclusions = append(updatedExclusions, constructor(
				adding.GetSubjectId(),
				exclusionConditionalExpression,
				nil,
				existingExclusion,
				adding),
			)
		} else {
			updatedExclusions = append(updatedExclusions, existingExclusion)
		}
	}

	constructed := constructor(
		tuple.PublicWildcard,
		existingWildcard.GetCaveatExpression(),
		updatedExclusions,
		existingWildcard)
	return &constructed
}

// unionConcreteWithConcrete performs a union operation between two concrete subjects and returns
// the concrete subject produced, if any.
func unionConcreteWithConcrete[T Subject[T]](existing *T, adding *T, constructor constructor[T]) *T {
	// Check for union with other concretes.
	if existing == nil {
		return adding
	}

	if adding == nil {
		return existing
	}

	existingConcrete := *existing
	addingConcrete := *adding

	// A union of a concrete subjects has the conditionals of each concrete merged.
	constructed := constructor(
		existingConcrete.GetSubjectId(),
		shortcircuitedOr(
			existingConcrete.GetCaveatExpression(),
			addingConcrete.GetCaveatExpression(),
		),
		nil,
		existingConcrete, addingConcrete)
	return &constructed
}

// subtractWildcardFromWildcard performs a subtraction operation of wildcard from another, returning
// the updated wildcard (if any), as well as any concrete subjects produced by the subtraction
// operation due to exclusions.
func subtractWildcardFromWildcard[T Subject[T]](existing *T, toRemove T, constructor constructor[T]) (*T, []T) {
	// If there is no existing wildcard, nothing more to do.
	if existing == nil {
		return nil, nil
	}

	// If there is no condition on the wildcard and the new wildcard has no exclusions, then this wildcard goes away.
	// Example: {*} - {*} => {}
	if toRemove.GetCaveatExpression() == nil && len(toRemove.GetExcludedSubjects()) == 0 {
		return nil, nil
	}

	// Otherwise, we construct a new wildcard and return any concrete subjects that might result from this subtraction.
	existingWildcard := *existing
	existingExclusions := exclusionsMapFor(existingWildcard)

	// Calculate the exclusions which turn into concrete subjects.
	// This occurs when a wildcard with exclusions is subtracted from a wildcard
	// (with, or without *matching* exclusions).
	//
	// Example:
	// Given the two wildcards `* - {user:sarah}` and `* - {user:tom, user:amy, user:sarah}`,
	// the resulting concrete subjects are {user:tom, user:amy} because the first set contains
	// `tom` and `amy` (but not `sarah`) and the second set contains all three.
	resultingConcreteSubjects := make([]T, 0, len(toRemove.GetExcludedSubjects()))
	for _, excludedSubject := range toRemove.GetExcludedSubjects() {
		if existingExclusion, isExistingExclusion := existingExclusions[excludedSubject.GetSubjectId()]; !isExistingExclusion || existingExclusion.GetCaveatExpression() != nil {
			// The conditional expression for the now-concrete subject type is the conditional on the provided exclusion
			// itself.
			//
			// As an example, subtracting the wildcards
			// {*[caveat1] - {user:tom}}
			// -
			// {*[caveat3] - {user:sarah[caveat4]}}
			//
			// the resulting expression to produce a *concrete* `user:sarah` is
			// `caveat1 && caveat3 && caveat4`, because the concrete subject only appears if the first
			// wildcard applies, the *second* wildcard applies and its exclusion applies.
			exclusionConditionalExpression := caveatAnd(
				caveatAnd(
					existingWildcard.GetCaveatExpression(),
					toRemove.GetCaveatExpression(),
				),
				excludedSubject.GetCaveatExpression(),
			)

			// If there is an existing exclusion, then its caveat expression is added as well, but inverted.
			//
			// As an example, subtracting the wildcards
			// {*[caveat1] - {user:tom[caveat2]}}
			// -
			// {*[caveat3] - {user:sarah[caveat4]}}
			//
			// the resulting expression to produce a *concrete* `user:sarah` is
			// `caveat1 && !caveat2 && caveat3 && caveat4`, because the concrete subject only appears
			// if the first wildcard applies, the *second* wildcard applies, the first exclusion
			// does *not* apply (ensuring the concrete is in the first wildcard) and the second exclusion
			// *does* apply (ensuring it is not in the second wildcard).
			if existingExclusion.GetCaveatExpression() != nil {
				exclusionConditionalExpression = caveatAnd(
					caveatAnd(
						caveatAnd(
							existingWildcard.GetCaveatExpression(),
							toRemove.GetCaveatExpression(),
						),
						caveatInvert(existingExclusion.GetCaveatExpression()),
					),
					excludedSubject.GetCaveatExpression(),
				)
			}

			resultingConcreteSubjects = append(resultingConcreteSubjects, constructor(
				excludedSubject.GetSubjectId(),
				exclusionConditionalExpression,
				nil, excludedSubject))
		}
	}

	// Create the combined conditional: the wildcard can only exist when it is present and the other wildcard is not.
	combinedConditionalExpression := caveatAnd(existingWildcard.GetCaveatExpression(), caveatInvert(toRemove.GetCaveatExpression()))
	if combinedConditionalExpression != nil {
		constructed := constructor(
			tuple.PublicWildcard,
			combinedConditionalExpression,
			existingWildcard.GetExcludedSubjects(),
			existingWildcard,
			toRemove)
		return &constructed, resultingConcreteSubjects
	}

	return nil, resultingConcreteSubjects
}

// subtractWildcardFromConcrete subtracts a wildcard from a concrete element, returning the updated
// concrete subject, if any.
func subtractWildcardFromConcrete[T Subject[T]](existingConcrete T, wildcardToRemove T, constructor constructor[T]) *T {
	// Subtraction of a wildcard removes *all* elements of the concrete set, except those that
	// are found in the excluded list. If the wildcard *itself* is conditional, then instead of
	// items being removed, they are made conditional on the inversion of the wildcard's expression,
	// and the exclusion's conditional, if any.
	//
	// Examples:
	//  {user:sarah, user:tom} - {*} => {}
	//  {user:sarah, user:tom} - {*[somecaveat]} => {user:sarah[!somecaveat], user:tom[!somecaveat]}
	//  {user:sarah, user:tom} - {* - {user:tom}} => {user:tom}
	//  {user:sarah, user:tom} - {*[somecaveat] - {user:tom}} => {user:sarah[!somecaveat], user:tom}
	//  {user:sarah, user:tom} - {* - {user:tom[c2]}}[somecaveat] => {user:sarah[!somecaveat], user:tom[c2]}
	//  {user:sarah[c1], user:tom} - {*[somecaveat] - {user:tom}} => {user:sarah[c1 && !somecaveat], user:tom}
	exclusions := exclusionsMapFor(wildcardToRemove)
	exclusion, isExcluded := exclusions[existingConcrete.GetSubjectId()]
	if !isExcluded {
		// If the subject was not excluded within the wildcard, it is either removed directly
		// (in the case where the wildcard is not conditional), or has its condition updated to
		// reflect that it is only present when the condition for the wildcard is *false*.
		if wildcardToRemove.GetCaveatExpression() == nil {
			return nil
		}

		constructed := constructor(
			existingConcrete.GetSubjectId(),
			caveatAnd(existingConcrete.GetCaveatExpression(), caveatInvert(wildcardToRemove.GetCaveatExpression())),
			nil,
			existingConcrete)
		return &constructed
	}

	// If the exclusion is not conditional, then the subject is always present.
	if exclusion.GetCaveatExpression() == nil {
		return &existingConcrete
	}

	// The conditional of the exclusion is that of the exclusion itself OR the caveatInverted case of
	// the wildcard, which would mean the wildcard itself does not apply.
	exclusionConditional := caveatOr(caveatInvert(wildcardToRemove.GetCaveatExpression()), exclusion.GetCaveatExpression())

	constructed := constructor(
		existingConcrete.GetSubjectId(),
		caveatAnd(existingConcrete.GetCaveatExpression(), exclusionConditional),
		nil,
		existingConcrete)
	return &constructed
}

// subtractConcreteFromConcrete subtracts a concrete subject from another concrete subject.
func subtractConcreteFromConcrete[T Subject[T]](existingConcrete T, toRemove T, constructor constructor[T]) *T {
	// Subtraction of a concrete type removes the entry from the concrete list
	// *unless* the subtraction is conditional, in which case the conditional is updated
	// to remove the element when it is true.
	//
	// Examples:
	//  {user:sarah} - {user:tom} => {user:sarah}
	//  {user:tom} - {user:tom} => {}
	//  {user:tom[c1]} - {user:tom} => {user:tom}
	//  {user:tom} - {user:tom[c2]} => {user:tom[!c2]}
	//  {user:tom[c1]} - {user:tom[c2]} => {user:tom[c1 && !c2]}
	if toRemove.GetCaveatExpression() == nil {
		return nil
	}

	// Otherwise, adjust the conditional of the existing item to remove it if it is true.
	expression := caveatAnd(
		existingConcrete.GetCaveatExpression(),
		caveatInvert(
			toRemove.GetCaveatExpression(),
		),
	)

	constructed := constructor(
		existingConcrete.GetSubjectId(),
		expression,
		nil,
		existingConcrete, toRemove)
	return &constructed
}

// subtractConcreteFromWildcard subtracts a concrete element from a wildcard.
func subtractConcreteFromWildcard[T Subject[T]](wildcard T, concreteToRemove T, constructor constructor[T]) *T {
	// Subtracting a concrete type from a wildcard adds the concrete to the exclusions for the wildcard.
	// Examples:
	//  {*} - {user:tom} => {* - {user:tom}}
	//  {*} - {user:tom[c1]} => {* - {user:tom[c1]}}
	//  {* - {user:tom[c1]}} - {user:tom} => {* - {user:tom}}
	//  {* - {user:tom[c1]}} - {user:tom[c2]} => {* - {user:tom[c1 || c2]}}
	updatedExclusions := make([]T, 0, len(wildcard.GetExcludedSubjects())+1)
	wasFound := false
	for _, existingExclusion := range wildcard.GetExcludedSubjects() {
		if existingExclusion.GetSubjectId() == concreteToRemove.GetSubjectId() {
			// The conditional expression for the exclusion is a combination on the existing exclusion or
			// the new expression. The caveat is short-circuited here because if either the exclusion or
			// the concrete is non-caveated, then the whole exclusion is non-caveated.
			exclusionConditionalExpression := shortcircuitedOr(
				existingExclusion.GetCaveatExpression(),
				concreteToRemove.GetCaveatExpression(),
			)

			updatedExclusions = append(updatedExclusions, constructor(
				concreteToRemove.GetSubjectId(),
				exclusionConditionalExpression,
				nil,
				existingExclusion,
				concreteToRemove),
			)
			wasFound = true
		} else {
			updatedExclusions = append(updatedExclusions, existingExclusion)
		}
	}

	if !wasFound {
		updatedExclusions = append(updatedExclusions, concreteToRemove)
	}

	constructed := constructor(
		tuple.PublicWildcard,
		wildcard.GetCaveatExpression(),
		updatedExclusions,
		wildcard)
	return &constructed
}

// intersectConcreteWithConcrete performs intersection between two concrete subjects, returning the
// resolved concrete subject, if any.
func intersectConcreteWithConcrete[T Subject[T]](first T, second *T, constructor constructor[T]) *T {
	// Intersection of concrete subjects is a standard intersection operation, where subjects
	// must be in both sets, with a combination of the two elements into one for conditionals.
	// Otherwise, `and` together conditionals.
	if second == nil {
		return nil
	}

	secondConcrete := *second
	constructed := constructor(
		first.GetSubjectId(),
		caveatAnd(first.GetCaveatExpression(), secondConcrete.GetCaveatExpression()),
		nil,
		first,
		secondConcrete)

	return &constructed
}

// intersectWildcardWithWildcard performs intersection between two wildcards, returning the resolved
// wildcard subject, if any.
func intersectWildcardWithWildcard[T Subject[T]](first *T, second *T, constructor constructor[T]) (*T, error) {
	// If either wildcard does not exist, then no wildcard is placed into the resulting set.
	if first == nil || second == nil {
		return nil, nil
	}

	// If the other wildcard exists, then the intersection between the two wildcards is an && of
	// their conditionals, and a *union* of their exclusions.
	firstWildcard := *first
	secondWildcard := *second

	concreteExclusions := NewBaseSubjectSet(constructor)
	for _, excludedSubject := range firstWildcard.GetExcludedSubjects() {
		if excludedSubject.GetSubjectId() == tuple.PublicWildcard {
			return nil, spiceerrors.MustBugf("wildcards are not allowed in exclusions")
		}

		err := concreteExclusions.Add(excludedSubject)
		if err != nil {
			return nil, err
		}
	}

	for _, excludedSubject := range secondWildcard.GetExcludedSubjects() {
		if excludedSubject.GetSubjectId() == tuple.PublicWildcard {
			return nil, spiceerrors.MustBugf("wildcards are not allowed in exclusions")
		}

		err := concreteExclusions.Add(excludedSubject)
		if err != nil {
			return nil, err
		}
	}

	constructed := constructor(
		tuple.PublicWildcard,
		caveatAnd(firstWildcard.GetCaveatExpression(), secondWildcard.GetCaveatExpression()),
		concreteExclusions.AsSlice(),
		firstWildcard,
		secondWildcard)
	return &constructed, nil
}

// intersectConcreteWithWildcard performs intersection between a concrete subject and a wildcard
// subject, returning the concrete, if any.
func intersectConcreteWithWildcard[T Subject[T]](concrete T, wildcard *T, constructor constructor[T]) (*T, error) {
	// If no wildcard exists, then the concrete cannot exist (for this branch)
	if wildcard == nil {
		return nil, nil
	}

	wildcardToIntersect := *wildcard
	exclusionsMap := exclusionsMapFor(wildcardToIntersect)
	exclusion, isExcluded := exclusionsMap[concrete.GetSubjectId()]

	// Cases:
	// - The concrete subject is not excluded and the wildcard is not conditional => concrete is kept
	// - The concrete subject is excluded and the wildcard is not conditional but the exclusion *is* conditional => concrete is made conditional
	// - The concrete subject is excluded and the wildcard is not conditional => concrete is removed
	// - The concrete subject is not excluded but the wildcard is conditional => concrete is kept, but made conditional
	// - The concrete subject is excluded and the wildcard is conditional => concrete is removed, since it is always excluded
	// - The concrete subject is excluded and the wildcard is conditional and the exclusion is conditional => combined conditional
	switch {
	case !isExcluded && wildcardToIntersect.GetCaveatExpression() == nil:
		// If the concrete is not excluded and the wildcard conditional is empty, then the concrete is always found.
		// Example: {user:tom} & {*} => {user:tom}
		return &concrete, nil

	case !isExcluded && wildcardToIntersect.GetCaveatExpression() != nil:
		// The concrete subject is only included if the wildcard's caveat is true.
		// Example: {user:tom}[acaveat] & {* - user:tom}[somecaveat] => {user:tom}[acaveat && somecaveat]
		constructed := constructor(
			concrete.GetSubjectId(),
			caveatAnd(concrete.GetCaveatExpression(), wildcardToIntersect.GetCaveatExpression()),
			nil,
			concrete,
			wildcardToIntersect)
		return &constructed, nil

	case isExcluded && exclusion.GetCaveatExpression() == nil:
		// If the concrete is excluded and the exclusion is not conditional, then the concrete can never show up,
		// regardless of whether the wildcard is conditional.
		// Example: {user:tom} & {* - user:tom}[somecaveat] => {}
		return nil, nil

	case isExcluded && exclusion.GetCaveatExpression() != nil:
		// NOTE: whether the wildcard is itself conditional or not is handled within the expression combinators below.
		// The concrete subject is included if the wildcard's caveat is true and the exclusion's caveat is *false*.
		// Example: {user:tom}[acaveat] & {* - user:tom[ecaveat]}[wcaveat] => {user:tom[acaveat && wcaveat && !ecaveat]}
		constructed := constructor(
			concrete.GetSubjectId(),
			caveatAnd(
				concrete.GetCaveatExpression(),
				caveatAnd(
					wildcardToIntersect.GetCaveatExpression(),
					caveatInvert(exclusion.GetCaveatExpression()),
				)),
			nil,
			concrete,
			wildcardToIntersect,
			exclusion)
		return &constructed, nil

	default:
		return nil, spiceerrors.MustBugf("unhandled case in basesubjectset intersectConcreteWithWildcard: %v & %v", concrete, wildcardToIntersect)
	}
}

type handle[T any] struct {
	value *T
}

func newHandle[T any]() *handle[T] {
	return &handle[T]{}
}

func (h *handle[T]) getOrNil() *T {
	return h.value
}

func (h *handle[T]) setOrNil(value *T) {
	h.value = value
}

func (h *handle[T]) get() (T, bool) {
	if h.value != nil {
		return *h.value, true
	}

	return *new(T), false
}

func (h *handle[T]) clear() {
	h.value = nil
}

func (h *handle[T]) clone() *handle[T] {
	return &handle[T]{
		value: h.value,
	}
}

// exclusionsMapFor creates a map of all the exclusions on a wildcard, by subject ID.
func exclusionsMapFor[T Subject[T]](wildcard T) map[string]T {
	exclusions := make(map[string]T, len(wildcard.GetExcludedSubjects()))
	for _, excludedSubject := range wildcard.GetExcludedSubjects() {
		exclusions[excludedSubject.GetSubjectId()] = excludedSubject
	}
	return exclusions
}
