package dynamicconfig

import (
	"fmt"
	"strings"
	"testing"

	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func newDynamicConfig(content string) (DynamicConfig, error) {
	var cfg config

	viper.SetConfigType("yaml")
	err := viper.ReadConfig(strings.NewReader(content))
	if err != nil {
		return nil, fmt.Errorf("failed to read dynamic config: %w", err)
	}

	err = viper.Unmarshal(&cfg)
	if err != nil {
		return nil, fmt.Errorf("failed to unmarshal dynamic config: %w", err)
	}

	return &cfg, nil
}

func TestGetByDimensions_ComplexScenarios(t *testing.T) {
	configYaml := `dimensionConfig:
  rule_precedence_test:
    - rules:
        region:
          enabledFor: ["us-west-1"]
        environment:
          enabledFor: ["production"]
        tier:
          allEnabled: true
      values:
        config_source: "first_rule"
        priority: "1"
    - rules:
        region:
          enabledFor: ["us-west-1", "us-west-2"]
        environment:
          enabledFor: ["production"]
        tier:
          enabledFor: ["premium"]
      values:
        config_source: "second_rule"
        priority: "2"
        premium_feature: "enabled"
    - rules:
        region:
          allEnabled: true
        environment:
          enabledFor: ["production"]
        tier:
          enabledFor: ["basic", "premium"]
      values:
        config_source: "third_rule"
        priority: "3"
        global_premium: "true"
  feature_with_many_dimensions:
    - rules:
        region:
          enabledFor: ["us-west-1", "us-east-1"]
        environment:
          enabledFor: ["production", "staging"]
        tier:
          enabledFor: ["enterprise", "premium"]
        cluster_type:
          enabledFor: ["dedicated"]
        version:
          enabledFor: ["v2.0", "v2.1"]
      values:
        feature_enabled: "true"
        max_throughput: "1000"
        security_level: "high"
    - rules:
        region:
          allEnabled: true
        environment:
          enabledFor: ["development"]
        tier:
          allEnabled: true
        cluster_type:
          allEnabled: true
        version:
          allEnabled: true
      values:
        feature_enabled: "false"
        max_throughput: "100"
        security_level: "low"
        debug_logging: "true"`

	cfg, err := newDynamicConfig(configYaml)
	require.NoError(t, err)

	ctx := t.Context()

	t.Run("rule_precedence_multiple_matches", func(t *testing.T) {
		// This should match all three rules, but return the last one (third_rule)
		dimensions := map[string]string{
			"region":      "us-west-1",
			"environment": "production",
			"tier":        "premium",
		}

		got := cfg.GetByDimensions(ctx, "rule_precedence_test", dimensions)
		expected := map[string]string{
			"config_source":  "third_rule",
			"priority":       "3",
			"global_premium": "true",
		}

		assert.Equal(t, expected, got, "Should return the last matching rule")
	})

	t.Run("rule_precedence_partial_match", func(t *testing.T) {
		// This should match only the first and third rules, return third
		dimensions := map[string]string{
			"region":      "us-west-1",
			"environment": "production",
			"tier":        "basic", // This won't match second rule
		}

		got := cfg.GetByDimensions(ctx, "rule_precedence_test", dimensions)
		expected := map[string]string{
			"config_source":  "third_rule",
			"priority":       "3",
			"global_premium": "true",
		}

		assert.Equal(t, expected, got, "Should return the last matching rule among partial matches")
	})

	t.Run("many_dimensions_all_match_first_item", func(t *testing.T) {
		dimensions := map[string]string{
			"region":       "us-west-1",
			"environment":  "production",
			"tier":         "enterprise",
			"cluster_type": "dedicated",
			"version":      "v2.0",
		}

		got := cfg.GetByDimensions(ctx, "feature_with_many_dimensions", dimensions)
		expected := map[string]string{
			"feature_enabled": "true",
			"max_throughput":  "1000",
			"security_level":  "high",
		}

		assert.Equal(t, expected, got, "All dimensions should match first rule")
	})

	t.Run("many_dimensions_development_all_enabled", func(t *testing.T) {
		dimensions := map[string]string{
			"region":       "ap-south-1", // Any region
			"environment":  "development",
			"tier":         "basic",  // Any tier
			"cluster_type": "shared", // Any cluster type
			"version":      "v1.0",   // Any version
		}

		got := cfg.GetByDimensions(ctx, "feature_with_many_dimensions", dimensions)
		expected := map[string]string{
			"feature_enabled": "false",
			"max_throughput":  "100",
			"security_level":  "low",
			"debug_logging":   "true",
		}

		assert.Equal(t, expected, got, "Should match development rule")
	})

	t.Run("many_dimensions_no_match", func(t *testing.T) {
		dimensions := map[string]string{
			"region":       "us-west-1",
			"environment":  "production", // Production but not development
			"tier":         "basic",      // Not enterprise/premium
			"cluster_type": "shared",     // Not dedicated
			"version":      "v1.0",       // Not v2.x
		}

		got := cfg.GetByDimensions(ctx, "feature_with_many_dimensions", dimensions)
		assert.Nil(t, got, "No rules should match these dimensions")
	})
}

func TestGetByDimensions_ErrorHandling(t *testing.T) {
	configYaml := `dimensionConfig:
  test_feature:
    - rules:
        region:
          enabledFor: ["us-west-1"]
        environment:
          enabledFor: ["production"]
      values:
        key1: value1
    - rules:
        region:
          enabledFor: ["us-west-1"]
        # Missing environment dimension - should cause validation error
      values:
        key2: value2`

	cfg, err := newDynamicConfig(configYaml)
	require.NoError(t, err)

	ctx := t.Context()

	t.Run("dimension_validation_error_skipped", func(t *testing.T) {
		dimensions := map[string]string{
			"region":      "us-west-1",
			"environment": "production",
		}

		// Should get the first rule's values, second rule should be skipped due to validation error
		got := cfg.GetByDimensions(ctx, "test_feature", dimensions)
		expected := map[string]string{
			"key1": "value1",
		}

		assert.Equal(t, expected, got, "Should return first rule despite second rule having validation error")
	})

	t.Run("empty_dimensions", func(t *testing.T) {
		got := cfg.GetByDimensions(ctx, "test_feature", map[string]string{})
		assert.Nil(t, got, "Empty dimensions should return nil")
	})

	t.Run("nil_dimensions", func(t *testing.T) {
		got := cfg.GetByDimensions(ctx, "test_feature", nil)
		assert.Nil(t, got, "Nil dimensions should return nil")
	})
}

func TestAreDimensionsMatched(t *testing.T) {
	tests := []struct {
		name       string
		item       DimensionConfigItem
		dimensions map[string]string
		want       bool
		wantErr    bool
	}{
		{
			name: "all enabled match",
			item: DimensionConfigItem{
				Rules: map[string]DimensionConfigRule{
					"region": {AllEnabled: true},
					"tier":   {AllEnabled: true},
				},
			},
			dimensions: map[string]string{
				"region": "any-region",
				"tier":   "any-tier",
			},
			want:    true,
			wantErr: false,
		},
		{
			name: "partial match with enabledFor",
			item: DimensionConfigItem{
				Rules: map[string]DimensionConfigRule{
					"region": {AllEnabled: true},
					"tier":   {EnabledFor: []string{"tier1", "tier2"}},
				},
			},
			dimensions: map[string]string{
				"region": "any-region",
				"tier":   "tier1",
			},
			want:    true,
			wantErr: false,
		},
		{
			name: "no match with enabledFor",
			item: DimensionConfigItem{
				Rules: map[string]DimensionConfigRule{
					"region": {EnabledFor: []string{"us-west"}},
					"tier":   {EnabledFor: []string{"tier1"}},
				},
			},
			dimensions: map[string]string{
				"region": "us-east",
				"tier":   "tier1",
			},
			want:    false,
			wantErr: false,
		},
		{
			name: "dimension mismatch",
			item: DimensionConfigItem{
				Rules: map[string]DimensionConfigRule{
					"region": {AllEnabled: true},
				},
			},
			dimensions: map[string]string{
				"region": "us-west",
				"tier":   "tier1",
			},
			want:    false,
			wantErr: true,
		},
		{
			name: "empty rules",
			item: DimensionConfigItem{
				Rules: map[string]DimensionConfigRule{},
			},
			dimensions: map[string]string{
				"region": "us-west",
			},
			want:    false,
			wantErr: true,
		},
		{
			name: "empty dimensions",
			item: DimensionConfigItem{
				Rules: map[string]DimensionConfigRule{
					"region": {AllEnabled: true},
				},
			},
			dimensions: map[string]string{},
			want:       false,
			wantErr:    true,
		},
		{
			name: "undefined dimension",
			item: DimensionConfigItem{
				Rules: map[string]DimensionConfigRule{
					"region": {AllEnabled: true},
				},
			},
			dimensions: map[string]string{
				"undefined": "value",
			},
			want:    false,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := areDimensionsMatched(tt.item, tt.dimensions)
			if tt.wantErr {
				require.Error(t, err)
			} else {
				require.NoError(t, err)
			}
			assert.Equal(t, tt.want, got)
		})
	}
}
