package pipeline

import "fmt"

type ProducerStructuralFailureAction string

const (
	ProducerStructuralFailureFailRun      ProducerStructuralFailureAction = "fail_run"
	ProducerStructuralFailureRejectOutput ProducerStructuralFailureAction = "reject_output"
)

type SemanticRejectionAction string

const (
	SemanticRejectionFailRun      SemanticRejectionAction = "fail_run"
	SemanticRejectionRejectOutput SemanticRejectionAction = "reject_output"
)

type ValidatorFailureAction string

const (
	ValidatorFailureWarnContinue ValidatorFailureAction = "warn_continue"
	ValidatorFailureFailRun      ValidatorFailureAction = "fail_run"
)

// ValidationPolicyOverride records only explicitly configured terminal-policy
// values. Nil fields inherit independently from the pipeline or application.
type ValidationPolicyOverride struct {
	ProducerStructuralFailure *ProducerStructuralFailureAction `json:"producer_structural_failure,omitempty"`
	SemanticRejection         *SemanticRejectionAction         `json:"semantic_rejection,omitempty"`
	ValidatorFailure          *ValidatorFailureAction          `json:"validator_failure,omitempty"`
}

// ValidationPolicy is the concrete terminal policy retained by each resolved
// producer. It contains no inherited pointers.
type ValidationPolicy struct {
	ProducerStructuralFailure ProducerStructuralFailureAction `json:"producer_structural_failure"`
	SemanticRejection         SemanticRejectionAction         `json:"semantic_rejection"`
	ValidatorFailure          ValidatorFailureAction          `json:"validator_failure"`
}

func DefaultValidationPolicy() ValidationPolicy {
	return ValidationPolicy{
		ProducerStructuralFailure: ProducerStructuralFailureFailRun,
		SemanticRejection:         SemanticRejectionFailRun,
		ValidatorFailure:          ValidatorFailureWarnContinue,
	}
}

func (override ValidationPolicyOverride) Validate() error {
	if override.ProducerStructuralFailure != nil {
		switch *override.ProducerStructuralFailure {
		case ProducerStructuralFailureFailRun, ProducerStructuralFailureRejectOutput:
		default:
			return fmt.Errorf("producer_structural_failure must be fail_run or reject_output")
		}
	}
	if override.SemanticRejection != nil {
		switch *override.SemanticRejection {
		case SemanticRejectionFailRun, SemanticRejectionRejectOutput:
		default:
			return fmt.Errorf("semantic_rejection must be fail_run or reject_output")
		}
	}
	if override.ValidatorFailure != nil {
		switch *override.ValidatorFailure {
		case ValidatorFailureWarnContinue, ValidatorFailureFailRun:
		default:
			return fmt.Errorf("validator_failure must be warn_continue or fail_run")
		}
	}
	return nil
}

func ResolveValidationPolicy(binding, pipeline *ValidationPolicyOverride) ValidationPolicy {
	resolved := DefaultValidationPolicy()
	applyValidationPolicyOverride(&resolved, pipeline)
	applyValidationPolicyOverride(&resolved, binding)
	return resolved
}

func cloneValidationPolicyOverride(override *ValidationPolicyOverride) *ValidationPolicyOverride {
	if override == nil {
		return nil
	}
	out := *override
	if override.ProducerStructuralFailure != nil {
		value := *override.ProducerStructuralFailure
		out.ProducerStructuralFailure = &value
	}
	if override.SemanticRejection != nil {
		value := *override.SemanticRejection
		out.SemanticRejection = &value
	}
	if override.ValidatorFailure != nil {
		value := *override.ValidatorFailure
		out.ValidatorFailure = &value
	}
	return &out
}

func applyValidationPolicyOverride(policy *ValidationPolicy, override *ValidationPolicyOverride) {
	if override == nil {
		return
	}
	if override.ProducerStructuralFailure != nil {
		policy.ProducerStructuralFailure = *override.ProducerStructuralFailure
	}
	if override.SemanticRejection != nil {
		policy.SemanticRejection = *override.SemanticRejection
	}
	if override.ValidatorFailure != nil {
		policy.ValidatorFailure = *override.ValidatorFailure
	}
}
