// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package pipelines

import (
	"errors"
	"testing"

	"github.com/stretchr/testify/require"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/confmap/xconfmap"
	"go.opentelemetry.io/collector/featuregate"
	"go.opentelemetry.io/collector/pipeline"
	"go.opentelemetry.io/collector/pipeline/xpipeline"
)

func TestConfigValidate(t *testing.T) {
	testCases := []struct {
		name     string // test case name (also file name containing config yaml)
		cfgFn    func(*testing.T) Config
		expected error
	}{
		{
			name:     "valid",
			cfgFn:    generateConfig,
			expected: nil,
		},
		{
			name: "duplicate-processor-reference",
			cfgFn: func(*testing.T) Config {
				cfg := generateConfig(t)
				pipe := cfg[pipeline.NewID(pipeline.SignalTraces)]
				pipe.Processors = append(pipe.Processors, pipe.Processors...)
				return cfg
			},
			expected: errors.New(`references processor "nop" multiple times`),
		},
		{
			name: "missing-pipeline-receivers",
			cfgFn: func(*testing.T) Config {
				cfg := generateConfig(t)
				cfg[pipeline.NewID(pipeline.SignalTraces)].Receivers = nil
				return cfg
			},
			expected: errMissingServicePipelineReceivers,
		},
		{
			name: "missing-pipeline-exporters",
			cfgFn: func(*testing.T) Config {
				cfg := generateConfig(t)
				cfg[pipeline.NewID(pipeline.SignalTraces)].Exporters = nil
				return cfg
			},
			expected: errMissingServicePipelineExporters,
		},
		{
			name: "missing-pipelines",
			cfgFn: func(*testing.T) Config {
				return nil
			},
			expected: errMissingServicePipelines,
		},
		{
			name: "invalid-service-pipeline-type",
			cfgFn: func(*testing.T) Config {
				cfg := generateConfig(t)
				cfg[pipeline.MustNewID("wrongtype")] = &PipelineConfig{
					Receivers:  []component.ID{component.MustNewID("nop")},
					Processors: []component.ID{component.MustNewID("nop")},
					Exporters:  []component.ID{component.MustNewID("nop")},
				}
				return cfg
			},
			expected: errors.New(`unknown signal "wrongtype"`),
		},
		{
			name: "disabled-featuregate-profiles",
			cfgFn: func(*testing.T) Config {
				cfg := generateConfig(t)
				cfg[pipeline.NewID(xpipeline.SignalProfiles)] = &PipelineConfig{
					Receivers:  []component.ID{component.MustNewID("nop")},
					Processors: []component.ID{component.MustNewID("nop")},
					Exporters:  []component.ID{component.MustNewID("nop")},
				}
				return cfg
			},
			expected: errors.New(`profiling signal support is at alpha level, gated under the "service.profilesSupport" feature gate`),
		},
		{
			name: "enabled-featuregate-profiles",
			cfgFn: func(t *testing.T) Config {
				require.NoError(t, featuregate.GlobalRegistry().Set(serviceProfileSupportGateID, true))

				cfg := generateConfig(t)
				cfg[pipeline.NewID(xpipeline.SignalProfiles)] = &PipelineConfig{
					Receivers:  []component.ID{component.MustNewID("nop")},
					Processors: []component.ID{component.MustNewID("nop")},
					Exporters:  []component.ID{component.MustNewID("nop")},
				}
				return cfg
			},
			expected: nil,
		},
	}

	for _, tt := range testCases {
		t.Run(tt.name, func(t *testing.T) {
			cfg := tt.cfgFn(t)
			if tt.expected != nil {
				require.ErrorContains(t, xconfmap.Validate(cfg), tt.expected.Error())
			} else {
				require.NoError(t, xconfmap.Validate(cfg))
			}

			// Clean up the profiles support gate, which may have been enabled in `cfgFn`.
			require.NoError(t, featuregate.GlobalRegistry().Set(serviceProfileSupportGateID, false))
		})
	}
}

func TestNoPipelinesFeatureGate(t *testing.T) {
	cfg := Config{}

	require.Error(t, xconfmap.Validate(cfg))

	gate := AllowNoPipelines
	err := featuregate.GlobalRegistry().Set(gate.ID(), true)
	require.NoError(t, err)
	defer func() {
		require.NoError(t, featuregate.GlobalRegistry().Set(gate.ID(), false))
	}()

	require.NoError(t, xconfmap.Validate(cfg))
}

func generateConfig(t *testing.T) Config {
	t.Helper()

	return map[pipeline.ID]*PipelineConfig{
		pipeline.NewID(pipeline.SignalTraces): {
			Receivers:  []component.ID{component.MustNewID("nop")},
			Processors: []component.ID{component.MustNewID("nop")},
			Exporters:  []component.ID{component.MustNewID("nop")},
		},
	}
}
