package config

import (
	"strings"
	"testing"

	"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
)

func TestValidationPolicyFileConfigurationIsStrictAndPresenceAware(t *testing.T) {
	const valid = `version: 4
pipelines:
  main:
    validation_policy:
      producer_structural_failure: reject_output
      semantic_rejection: fail_run
    input: seriatim
    chunk:
      module: generic
      validation_policy:
        validator_failure: fail_run
    artifacts:
      lane:
        extract:
          module: extract
          validation_policy:
            semantic_rejection: reject_output
`
	cfg := applyFileConfig(t, valid)
	profile := cfg.Pipelines["main"]
	if profile.ValidationPolicy == nil || profile.ValidationPolicy.ProducerStructuralFailure == nil || *profile.ValidationPolicy.ProducerStructuralFailure != pipeline.ProducerStructuralFailureRejectOutput || profile.ValidationPolicy.SemanticRejection == nil || *profile.ValidationPolicy.SemanticRejection != pipeline.SemanticRejectionFailRun || profile.ValidationPolicy.ValidatorFailure != nil {
		t.Fatalf("pipeline validation policy = %#v", profile.ValidationPolicy)
	}
	if profile.Chunk.ValidationPolicy == nil || profile.Chunk.ValidationPolicy.ValidatorFailure == nil || *profile.Chunk.ValidationPolicy.ValidatorFailure != pipeline.ValidatorFailureFailRun {
		t.Fatalf("chunk validation policy = %#v", profile.Chunk.ValidationPolicy)
	}
	lane := profile.Artifacts["lane"]
	if lane.Extract.ValidationPolicy == nil || lane.Extract.ValidationPolicy.SemanticRejection == nil || *lane.Extract.ValidationPolicy.SemanticRejection != pipeline.SemanticRejectionRejectOutput {
		t.Fatalf("extract validation policy = %#v", lane.Extract.ValidationPolicy)
	}

	for _, test := range []struct {
		name string
		yaml string
	}{
		{"null object", strings.Replace(valid, "validation_policy:\n      producer_structural_failure: reject_output\n      semantic_rejection: fail_run", "validation_policy: null", 1)},
		{"null field", strings.Replace(valid, "semantic_rejection: fail_run", "semantic_rejection: null", 1)},
		{"unknown field", strings.Replace(valid, "semantic_rejection: fail_run", "unknown: fail_run", 1)},
		{"duplicate field", strings.Replace(valid, "semantic_rejection: fail_run", "semantic_rejection: fail_run\n      semantic_rejection: reject_output", 1)},
		{"invalid enum", strings.Replace(valid, "semantic_rejection: fail_run", "semantic_rejection: continue", 1)},
	} {
		t.Run(test.name, func(t *testing.T) {
			if _, err := ParseFileConfigYAML([]byte(test.yaml)); err == nil {
				t.Fatal("ParseFileConfigYAML() error = nil, want strict validation-policy rejection")
			}
		})
	}
}

func TestValidationPolicyPlacementRules(t *testing.T) {
	policy := &pipeline.ValidationPolicyOverride{}
	semantic := pipeline.SemanticRejectionRejectOutput
	policy.SemanticRejection = &semantic
	base := pipeline.PipelineProfile{
		ID:    "main",
		Input: pipeline.Binding("input"),
		Artifacts: map[string]pipeline.ArtifactLaneProfile{
			"lane": {Extract: pipeline.Binding("extract")},
		},
	}
	for _, test := range []struct {
		name   string
		mutate func(*pipeline.PipelineProfile)
	}{
		{"input", func(profile *pipeline.PipelineProfile) { profile.Input.ValidationPolicy = policy }},
		{"output", func(profile *pipeline.PipelineProfile) {
			profile.Output = pipeline.Binding("output")
			profile.Output.ValidationPolicy = policy
		}},
		{"validator", func(profile *pipeline.PipelineProfile) {
			lane := profile.Artifacts["lane"]
			lane.Extract.Validators = pipeline.ValidatorOverride{Set: true, Validators: []pipeline.ModuleBinding{{Module: "validator", ValidationPolicy: policy}}}
			profile.Artifacts["lane"] = lane
		}},
	} {
		t.Run(test.name, func(t *testing.T) {
			profile := base
			profile.Artifacts = map[string]pipeline.ArtifactLaneProfile{"lane": base.Artifacts["lane"]}
			test.mutate(&profile)
			cfg := Default()
			cfg.Pipelines = map[string]pipeline.PipelineProfile{"main": profile}
			if err := cfg.Validate(); err == nil || !strings.Contains(err.Error(), "validation_policy") {
				t.Fatalf("Config.Validate() error = %v, want placement rejection", err)
			}
		})
	}
}
