package maximizer

// ConstraintRuleSet is the top-level container loaded from maximizer_constraints_v1.json.
type ConstraintRuleSet struct {
	Version     string           `json:"version"`
	Regulations []string         `json:"regulations"`
	Rules       []ConstraintRule `json:"rules"`
}

// ConstraintRule maps a regulatory obligation to dimension restrictions.
type ConstraintRule struct {
	ID           string       `json:"id"`
	ObligationID string       `json:"obligation_id"`
	Regulation   string       `json:"regulation"`
	ArticleRef   string       `json:"article_ref"`
	Title        string       `json:"title"`
	Description  string       `json:"description"`
	RuleType     string       `json:"rule_type"` // hard_prohibition, requirement, classification_rule, optimizer_rule, escalation_gate
	Constraints  []Constraint `json:"constraints"`
}

// Constraint is a single if-then rule on the dimension space.
type Constraint struct {
	If   ConditionSet `json:"if"`
	Then EffectSet    `json:"then"`
}

// ConditionSet maps dimension names to their required values.
// Values can be a string (exact match) or []string (any of).
type ConditionSet map[string]interface{}

// EffectSet defines what must be true when the condition matches.
type EffectSet struct {
	// Allowed=false means hard block — no optimization possible for this rule
	Allowed *bool `json:"allowed,omitempty"`

	// RequiredValues: dimension must have exactly this value
	RequiredValues map[string]string `json:"required_values,omitempty"`

	// RequiredControls: organizational/technical controls needed
	RequiredControls []string `json:"required_controls,omitempty"`

	// RequiredPatterns: architectural patterns needed
	RequiredPatterns []string `json:"required_patterns,omitempty"`

	// Classification overrides
	SetRiskClassification string `json:"set_risk_classification,omitempty"`
}

// Matches checks if a DimensionConfig satisfies all conditions in this set.
func (cs ConditionSet) Matches(config *DimensionConfig) bool {
	for dim, expected := range cs {
		actual := config.GetValue(dim)
		if actual == "" {
			return false
		}
		switch v := expected.(type) {
		case string:
			if actual != v {
				return false
			}
		case []interface{}:
			found := false
			for _, item := range v {
				if s, ok := item.(string); ok && actual == s {
					found = true
					break
				}
			}
			if !found {
				return false
			}
		}
	}
	return true
}
