package d2ir

import (
	
	

	
)

// DefaultMaxGlobExpansion is the maximum amount of work globs may perform
// during one compilation. Explicit source fields do not consume this budget.
const DefaultMaxGlobExpansion int64 = 65_536

// maxGlobCreatedFields independently caps retained materialization. Work is
// normally the tighter bound for deep expansions, while this ceiling bounds
// broad, shallow expansions even when callers raise MaxGlobExpansion.
const maxGlobCreatedFields int64 = 10_000

type globExpansionBudget struct {
	limit         int64
	used          int64
	createdFields int64
}

type globExpansionLimitError struct {
	limit int64
}

func ( *globExpansionLimitError) () string {
	return fmt.Sprintf("glob expansion exceeds limit of %d work units", .limit)
}

type globFieldLimitError struct {
	limit int64
}

func ( *globFieldLimitError) () string {
	return fmt.Sprintf("glob expansion exceeds limit of %d created fields", .limit)
}

func newGlobExpansionBudget( int64) (*globExpansionBudget, error) {
	if  < 0 {
		return nil, fmt.Errorf("MaxGlobExpansion must not be negative")
	}
	if  == 0 {
		 = DefaultMaxGlobExpansion
	}
	return &globExpansionBudget{limit: }, nil
}

func ( *globExpansionBudget) ( int64) error {
	if  < 0 ||  > .limit-.used {
		return &globExpansionLimitError{limit: .limit}
	}
	.used += 
	return nil
}

func ( *globExpansionBudget) () error {
	if .createdFields >= maxGlobCreatedFields {
		return &globFieldLimitError{limit: maxGlobCreatedFields}
	}
	.createdFields++
	return nil
}

func ( *compiler) ( d2ast.Node,  error) bool {
	.globExpansionErr = 
	if  != nil {
		.errorf(, "%v", )
	}
	.halted = true
	return false
}

func ( *compiler) ( d2ast.Node,  int64) bool {
	if .stopped() {
		return false
	}
	if  := .globExpansion.reserve();  != nil {
		return .globExpansionFailure(, )
	}
	return true
}

func ( *compiler) ( d2ast.Node) bool {
	if .stopped() {
		return false
	}
	if  := .globExpansion.reserveField();  != nil {
		return .globExpansionFailure(, )
	}
	return true
}

func ( *compiler) ( *RefContext) d2ast.Node {
	if  != nil && .Key != nil {
		return .Key
	}
	if len(.globRefContextStack) == 0 {
		return nil
	}
	return .globRefContextStack[len(.globRefContextStack)-1].Key
}

// reserveGlobGeneratedFieldWork accounts for the path formatting and applied
// set lookups performed before a glob can materialize a field. Charging the
// prospective depth once per active glob context makes alternating recursive
// rules consume budget before constructing ever-deeper IR paths.
func ( *compiler) ( *Map,  d2ast.Node) bool {
	if .stopped() {
		return false
	}
	 := int64(len(.globRefContextStack))
	if  == 0 {
		return true
	}

	 := int64(1)
	for  := ParentField();  != nil && !.Root(); {
		if  == math.MaxInt64 {
			return .globExpansionFailure(, &globExpansionLimitError{limit: .globExpansion.limit})
		}
		++
		 = ParentMap()
		if  == nil {
			break
		}
		 = ParentField()
	}
	if  > math.MaxInt64/ {
		return .globExpansionFailure(, &globExpansionLimitError{limit: .globExpansion.limit})
	}
	return .reserveGlobWork(, *)
}