package services

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"
	"time"

	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/gorm"
)

func setupEnhancedServiceDB(t *testing.T) *gorm.DB {
	db := setupTestDB(t)
	require.NoError(t, db.AutoMigrate(&models.NotificationProvider{}, &models.NotificationConfig{}, &models.Setting{}))
	return db
}

func TestNewEnhancedSecurityNotificationService(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)
	assert.NotNil(t, service)
	assert.Equal(t, db, service.db)
}

func TestGetSettings_FeatureFlagDisabled_ReturnsLegacyConfig(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Set feature flag to false
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "false",
		Type:  "bool",
	}).Error)

	// Create legacy config
	legacyConfig := &models.NotificationConfig{
		NotifyWAFBlocks:     true,
		NotifyACLDenies:     false,
		NotifyRateLimitHits: true,
		WebhookURL:          "https://example.com/webhook",
	}
	require.NoError(t, db.Create(legacyConfig).Error)

	// Test
	config, err := service.GetSettings()
	require.NoError(t, err)
	assert.True(t, config.NotifyWAFBlocks)
	assert.False(t, config.NotifyACLDenies)
	assert.True(t, config.NotifyRateLimitHits)
	assert.Equal(t, "https://example.com/webhook", config.WebhookURL)
}

func TestGetSettings_FeatureFlagEnabled_ReturnsAggregatedConfig(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Set feature flag to true
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "true",
		Type:  "bool",
	}).Error)

	// Create providers with different event types enabled
	providers := []models.NotificationProvider{
		{
			ID:                          "p1",
			Type:                        "discord",
			Enabled:                     true,
			NotifySecurityWAFBlocks:     true,
			NotifySecurityACLDenies:     false,
			NotifySecurityRateLimitHits: false,
			URL:                         "https://discord.com/webhook/1",
		},
		{
			ID:                          "p2",
			Type:                        "discord",
			Enabled:                     true,
			NotifySecurityWAFBlocks:     false,
			NotifySecurityACLDenies:     true,
			NotifySecurityRateLimitHits: true,
			URL:                         "https://discord.com/webhook/2",
		},
	}

	for _, p := range providers {
		require.NoError(t, db.Create(&p).Error)
	}

	// Test
	config, err := service.GetSettings()
	require.NoError(t, err)
	// OR aggregation: at least one provider has each flag true
	assert.True(t, config.NotifyWAFBlocks)
	assert.True(t, config.NotifyACLDenies)
	assert.True(t, config.NotifyRateLimitHits)
}

func TestGetProviderAggregatedConfig_ORSemantics(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Create providers where different providers have different flags
	providers := []models.NotificationProvider{
		{ID: "p1", Type: "discord", Enabled: true, NotifySecurityWAFBlocks: true, NotifySecurityACLDenies: false, NotifySecurityRateLimitHits: false},
		{ID: "p2", Type: "discord", Enabled: true, NotifySecurityWAFBlocks: false, NotifySecurityACLDenies: true, NotifySecurityRateLimitHits: false},
		{ID: "p3", Type: "discord", Enabled: true, NotifySecurityWAFBlocks: false, NotifySecurityACLDenies: false, NotifySecurityRateLimitHits: true},
	}

	for _, p := range providers {
		require.NoError(t, db.Create(&p).Error)
	}

	// Test
	config, err := service.getProviderAggregatedConfig()
	require.NoError(t, err)
	assert.True(t, config.NotifyWAFBlocks, "OR: p1 has WAF=true")
	assert.True(t, config.NotifyACLDenies, "OR: p2 has ACL=true")
	assert.True(t, config.NotifyRateLimitHits, "OR: p3 has RateLimit=true")
}

func TestGetProviderAggregatedConfig_FiltersSupportedTypes(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Create providers with both supported and unsupported types
	providers := []models.NotificationProvider{
		{ID: "discord", Type: "discord", Enabled: true, NotifySecurityWAFBlocks: true},
		{ID: "webhook", Type: "webhook", Enabled: true, NotifySecurityWAFBlocks: true},
		{ID: "slack", Type: "slack", Enabled: true, NotifySecurityACLDenies: true},
		{ID: "gotify", Type: "gotify", Enabled: true, NotifySecurityRateLimitHits: true},
		{ID: "unsupported", Type: "telegram", Enabled: true, NotifySecurityWAFBlocks: true}, // Should be filtered
	}

	for _, p := range providers {
		require.NoError(t, db.Create(&p).Error)
	}

	// Test
	config, err := service.getProviderAggregatedConfig()
	require.NoError(t, err)
	// Telegram is unsupported, so it shouldn't contribute to aggregation
	assert.True(t, config.NotifyWAFBlocks, "Discord and webhook have WAF=true")
	assert.True(t, config.NotifyACLDenies, "Slack has ACL=true")
	assert.True(t, config.NotifyRateLimitHits, "Gotify has RateLimit=true")
}

func TestGetProviderAggregatedConfig_DestinationReporting_SingleManaged(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	testCases := []struct {
		name          string
		providerType  string
		url           string
		expectedField string
	}{
		{
			name:          "webhook",
			providerType:  "webhook",
			url:           "https://example.com/webhook",
			expectedField: "WebhookURL",
		},
		{
			name:          "discord",
			providerType:  "discord",
			url:           "https://discord.com/webhook/123",
			expectedField: "DiscordWebhookURL",
		},
		{
			name:          "slack",
			providerType:  "slack",
			url:           "https://hooks.slack.com/services/T/B/X",
			expectedField: "SlackWebhookURL",
		},
		{
			name:          "gotify",
			providerType:  "gotify",
			url:           "https://gotify.example.com",
			expectedField: "GotifyURL",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// Clean up
			db.Exec("DELETE FROM notification_providers")

			// Create single managed provider
			provider := models.NotificationProvider{
				ID:                    "managed",
				Type:                  tc.providerType,
				URL:                   tc.url,
				Enabled:               true,
				ManagedLegacySecurity: true,
			}
			require.NoError(t, db.Create(&provider).Error)

			// Test
			config, err := service.getProviderAggregatedConfig()
			require.NoError(t, err)
			assert.False(t, config.DestinationAmbiguous, "Single managed provider = not ambiguous")

			// Verify correct field is populated
			switch tc.expectedField {
			case "WebhookURL":
				assert.Equal(t, tc.url, config.WebhookURL)
			case "DiscordWebhookURL":
				assert.Equal(t, tc.url, config.DiscordWebhookURL)
			case "SlackWebhookURL":
				assert.Equal(t, tc.url, config.SlackWebhookURL)
			case "GotifyURL":
				assert.Equal(t, tc.url, config.GotifyURL)
			}
		})
	}
}

func TestGetProviderAggregatedConfig_DestinationReporting_MultipleManaged_Ambiguous(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Create multiple managed providers
	providers := []models.NotificationProvider{
		{ID: "m1", Type: "discord", URL: "https://discord.com/webhook/1", Enabled: true, ManagedLegacySecurity: true},
		{ID: "m2", Type: "discord", URL: "https://discord.com/webhook/2", Enabled: true, ManagedLegacySecurity: true},
	}

	for _, p := range providers {
		require.NoError(t, db.Create(&p).Error)
	}

	// Test
	config, err := service.getProviderAggregatedConfig()
	require.NoError(t, err)
	assert.True(t, config.DestinationAmbiguous, "Multiple managed providers = ambiguous")
	assert.Empty(t, config.WebhookURL)
	assert.Empty(t, config.DiscordWebhookURL)
	assert.Empty(t, config.SlackWebhookURL)
	assert.Empty(t, config.GotifyURL)
}

func TestGetProviderAggregatedConfig_DestinationReporting_ZeroManaged_Ambiguous(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Create provider without managed flag
	provider := models.NotificationProvider{
		ID:                    "unmanaged",
		Type:                  "discord",
		URL:                   "https://discord.com/webhook/1",
		Enabled:               true,
		ManagedLegacySecurity: false,
	}
	require.NoError(t, db.Create(&provider).Error)

	// Test
	config, err := service.getProviderAggregatedConfig()
	require.NoError(t, err)
	assert.True(t, config.DestinationAmbiguous, "Zero managed providers = ambiguous")
}

func TestGetSettings_LegacyConfig_NotFound_ReturnsDefaults(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Set feature flag to false to use legacy path
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "false",
		Type:  "bool",
	}).Error)

	// Don't create legacy config

	// Test
	config, err := service.GetSettings()
	require.NoError(t, err)
	assert.True(t, config.NotifyWAFBlocks, "Default WAF=true")
	assert.True(t, config.NotifyACLDenies, "Default ACL=true")
	assert.True(t, config.NotifyRateLimitHits, "Default RateLimit=true")
}

func TestUpdateSettings_FeatureFlagDisabled_UpdatesLegacyConfig(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Set feature flag to false
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "false",
		Type:  "bool",
	}).Error)

	// Update
	req := &models.NotificationConfig{
		NotifyWAFBlocks:     false,
		NotifyACLDenies:     true,
		NotifyRateLimitHits: false,
		WebhookURL:          "https://updated.com/webhook",
	}
	err := service.UpdateSettings(req)
	require.NoError(t, err)

	// Verify
	var saved models.NotificationConfig
	require.NoError(t, db.First(&saved).Error)
	assert.False(t, saved.NotifyWAFBlocks)
	assert.True(t, saved.NotifyACLDenies)
	assert.False(t, saved.NotifyRateLimitHits)
	assert.Equal(t, "https://updated.com/webhook", saved.WebhookURL)
}

func TestUpdateSettings_FeatureFlagEnabled_UpdatesManagedProviders(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Set feature flag to true
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "true",
		Type:  "bool",
	}).Error)

	// Create managed provider
	provider := models.NotificationProvider{
		ID:                          "managed",
		Type:                        "discord",
		URL:                         "https://discord.com/webhook/old",
		Enabled:                     true,
		ManagedLegacySecurity:       true,
		NotifySecurityWAFBlocks:     true,
		NotifySecurityACLDenies:     true,
		NotifySecurityRateLimitHits: true,
	}
	require.NoError(t, db.Create(&provider).Error)

	// Update
	req := &models.NotificationConfig{
		NotifyWAFBlocks:     false,
		NotifyACLDenies:     true,
		NotifyRateLimitHits: false,
		DiscordWebhookURL:   "https://discord.com/webhook/new",
	}
	err := service.UpdateSettings(req)
	require.NoError(t, err)

	// Verify
	var updated models.NotificationProvider
	require.NoError(t, db.First(&updated, "id = ?", "managed").Error)
	assert.False(t, updated.NotifySecurityWAFBlocks)
	assert.True(t, updated.NotifySecurityACLDenies)
	assert.False(t, updated.NotifySecurityRateLimitHits)
	assert.Equal(t, "https://discord.com/webhook/new", updated.URL)
	assert.Equal(t, "discord", updated.Type)
}

func TestUpdateManagedProviders_CreatesProviderIfNoneExist(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// No existing providers

	// Update
	req := &models.NotificationConfig{
		NotifyWAFBlocks:   true,
		NotifyACLDenies:   false,
		DiscordWebhookURL: "https://discord.com/webhook/1",
	}
	err := service.updateManagedProviders(req)
	require.NoError(t, err)

	// Verify
	var providers []models.NotificationProvider
	require.NoError(t, db.Find(&providers).Error)
	require.Len(t, providers, 1)
	assert.Equal(t, "discord", providers[0].Type)
	assert.Equal(t, "https://discord.com/webhook/1", providers[0].URL)
	assert.True(t, providers[0].ManagedLegacySecurity)
	assert.True(t, providers[0].NotifySecurityWAFBlocks)
	assert.False(t, providers[0].NotifySecurityACLDenies)
}

func TestUpdateManagedProviders_RejectsMultipleDestinations(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Try to set multiple destinations
	req := &models.NotificationConfig{
		WebhookURL:        "https://example.com/webhook",
		DiscordWebhookURL: "https://discord.com/webhook/1",
	}

	err := service.updateManagedProviders(req)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "ambiguous destination")
}

func TestUpdateManagedProviders_GotifyValidation_RequiresBothURLAndToken(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	testCases := []struct {
		name        string
		gotifyURL   string
		gotifyToken string
		expectError bool
	}{
		{
			name:        "both_present",
			gotifyURL:   "https://gotify.example.com",
			gotifyToken: "token123",
			expectError: false,
		},
		{
			name:        "only_url",
			gotifyURL:   "https://gotify.example.com",
			gotifyToken: "",
			expectError: true,
		},
		{
			name:        "only_token",
			gotifyURL:   "",
			gotifyToken: "token123",
			expectError: true,
		},
		{
			name:        "both_empty",
			gotifyURL:   "",
			gotifyToken: "",
			expectError: false, // No gotify config = valid
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			req := &models.NotificationConfig{
				GotifyURL:   tc.gotifyURL,
				GotifyToken: tc.gotifyToken,
			}

			err := service.updateManagedProviders(req)
			if tc.expectError {
				assert.Error(t, err)
				assert.Contains(t, err.Error(), "incomplete gotify configuration")
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestUpdateManagedProviders_Idempotency_NoUpdateIfNoChange(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Create managed provider
	initialTime := time.Now().Add(-1 * time.Hour)
	provider := models.NotificationProvider{
		ID:                          "managed",
		Type:                        "discord",
		URL:                         "https://discord.com/webhook/1",
		Enabled:                     true,
		ManagedLegacySecurity:       true,
		NotifySecurityWAFBlocks:     true,
		NotifySecurityACLDenies:     false,
		NotifySecurityRateLimitHits: true,
	}
	provider.CreatedAt = initialTime
	provider.UpdatedAt = initialTime
	require.NoError(t, db.Create(&provider).Error)

	// Update with same values
	req := &models.NotificationConfig{
		NotifyWAFBlocks:     true,
		NotifyACLDenies:     false,
		NotifyRateLimitHits: true,
		DiscordWebhookURL:   "https://discord.com/webhook/1",
	}
	err := service.updateManagedProviders(req)
	require.NoError(t, err)

	// Verify UpdatedAt didn't change
	var updated models.NotificationProvider
	require.NoError(t, db.First(&updated, "id = ?", "managed").Error)
	assert.Equal(t, initialTime.Unix(), updated.UpdatedAt.Unix(), "UpdatedAt should not change if values unchanged")
}

func TestExtractDestinationURL(t *testing.T) {
	service := &EnhancedSecurityNotificationService{}

	testCases := []struct {
		name     string
		req      *models.NotificationConfig
		expected string
	}{
		{
			name:     "webhook",
			req:      &models.NotificationConfig{WebhookURL: "https://example.com/webhook"},
			expected: "https://example.com/webhook",
		},
		{
			name:     "discord",
			req:      &models.NotificationConfig{DiscordWebhookURL: "https://discord.com/webhook/1"},
			expected: "https://discord.com/webhook/1",
		},
		{
			name:     "slack",
			req:      &models.NotificationConfig{SlackWebhookURL: "https://hooks.slack.com/services/T/B/X"},
			expected: "https://hooks.slack.com/services/T/B/X",
		},
		{
			name:     "gotify",
			req:      &models.NotificationConfig{GotifyURL: "https://gotify.example.com"},
			expected: "https://gotify.example.com",
		},
		{
			name:     "empty",
			req:      &models.NotificationConfig{},
			expected: "",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result := service.extractDestinationURL(tc.req)
			assert.Equal(t, tc.expected, result)
		})
	}
}

func TestExtractDestinationToken(t *testing.T) {
	service := &EnhancedSecurityNotificationService{}

	testCases := []struct {
		name     string
		req      *models.NotificationConfig
		expected string
	}{
		{
			name:     "gotify_token",
			req:      &models.NotificationConfig{GotifyToken: "token123"},
			expected: "token123",
		},
		{
			name:     "empty",
			req:      &models.NotificationConfig{},
			expected: "",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result := service.extractDestinationToken(tc.req)
			assert.Equal(t, tc.expected, result)
		})
	}
}

func TestMigrateFromLegacyConfig_FeatureFlagDisabled_ReadOnlyMode(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Set feature flag to false
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "false",
		Type:  "bool",
	}).Error)

	// Create legacy config
	legacyConfig := models.NotificationConfig{
		NotifyWAFBlocks: true,
		WebhookURL:      "https://example.com/webhook",
	}
	require.NoError(t, db.Create(&legacyConfig).Error)

	// Migrate
	err := service.MigrateFromLegacyConfig()
	require.NoError(t, err)

	// Verify NO provider was created (read-only mode)
	var providers []models.NotificationProvider
	require.NoError(t, db.Find(&providers).Error)
	assert.Len(t, providers, 0, "Feature flag disabled = no provider mutation")
}

func TestMigrateFromLegacyConfig_NoLegacyConfig_NoOp(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Enable feature flag
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "true",
		Type:  "bool",
	}).Error)

	// No legacy config

	// Migrate
	err := service.MigrateFromLegacyConfig()
	require.NoError(t, err)

	// Verify NO provider was created
	var providers []models.NotificationProvider
	require.NoError(t, db.Find(&providers).Error)
	assert.Len(t, providers, 0)
}

func TestMigrateFromLegacyConfig_CreatesManagedProvider(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Enable feature flag
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "true",
		Type:  "bool",
	}).Error)

	// Create legacy config
	legacyConfig := models.NotificationConfig{
		NotifyWAFBlocks:     true,
		NotifyACLDenies:     false,
		NotifyRateLimitHits: true,
		WebhookURL:          "https://example.com/webhook",
	}
	require.NoError(t, db.Create(&legacyConfig).Error)

	// Migrate
	err := service.MigrateFromLegacyConfig()
	require.NoError(t, err)

	// Verify provider created
	var providers []models.NotificationProvider
	require.NoError(t, db.Find(&providers).Error)
	require.Len(t, providers, 1)
	assert.True(t, providers[0].ManagedLegacySecurity)
	assert.Equal(t, "webhook", providers[0].Type)
	assert.Equal(t, "https://example.com/webhook", providers[0].URL)
	assert.True(t, providers[0].NotifySecurityWAFBlocks)
	assert.False(t, providers[0].NotifySecurityACLDenies)
	assert.True(t, providers[0].NotifySecurityRateLimitHits)
}

func TestMigrateFromLegacyConfig_Idempotent_SameChecksum(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Enable feature flag
	require.NoError(t, db.Create(&models.Setting{
		Key:   "feature.notifications.security_provider_events.enabled",
		Value: "true",
		Type:  "bool",
	}).Error)

	// Create legacy config
	legacyConfig := models.NotificationConfig{
		NotifyWAFBlocks: true,
		WebhookURL:      "https://example.com/webhook",
	}
	require.NoError(t, db.Create(&legacyConfig).Error)

	// First migration
	err := service.MigrateFromLegacyConfig()
	require.NoError(t, err)

	// Get provider count after first migration
	var providersAfterFirst []models.NotificationProvider
	require.NoError(t, db.Find(&providersAfterFirst).Error)
	firstCount := len(providersAfterFirst)

	// Second migration (should be no-op due to checksum match)
	err = service.MigrateFromLegacyConfig()
	require.NoError(t, err)

	// Verify no duplicate provider created
	var providersAfterSecond []models.NotificationProvider
	require.NoError(t, db.Find(&providersAfterSecond).Error)
	assert.Equal(t, firstCount, len(providersAfterSecond), "Idempotent migration should not create duplicates")
}

func TestComputeConfigChecksum_Deterministic(t *testing.T) {
	config1 := models.NotificationConfig{
		NotifyWAFBlocks:     true,
		NotifyACLDenies:     false,
		NotifyRateLimitHits: true,
		WebhookURL:          "https://example.com/webhook",
	}

	config2 := models.NotificationConfig{
		NotifyWAFBlocks:     true,
		NotifyACLDenies:     false,
		NotifyRateLimitHits: true,
		WebhookURL:          "https://example.com/webhook",
	}

	checksum1 := computeConfigChecksum(config1)
	checksum2 := computeConfigChecksum(config2)

	assert.Equal(t, checksum1, checksum2, "Same config should produce same checksum")
	assert.NotEmpty(t, checksum1)
}

func TestComputeConfigChecksum_DifferentForDifferentConfigs(t *testing.T) {
	config1 := models.NotificationConfig{
		NotifyWAFBlocks: true,
	}

	config2 := models.NotificationConfig{
		NotifyWAFBlocks: false,
	}

	checksum1 := computeConfigChecksum(config1)
	checksum2 := computeConfigChecksum(config2)

	assert.NotEqual(t, checksum1, checksum2, "Different configs should produce different checksums")
}

func TestIsFeatureEnabled_NotFound_CreatesDefault(t *testing.T) {
	// Save and restore env vars
	origCharonEnv := os.Getenv("CHARON_ENV")
	origGinMode := os.Getenv("GIN_MODE")
	defer func() {
		_ = os.Setenv("CHARON_ENV", origCharonEnv)
		_ = os.Setenv("GIN_MODE", origGinMode)
	}()

	testCases := []struct {
		name        string
		charonEnv   string
		ginMode     string
		expected    bool
		description string
	}{
		{
			name:        "production_explicit",
			charonEnv:   "production",
			ginMode:     "",
			expected:    false,
			description: "CHARON_ENV=production should default to false",
		},
		{
			name:        "prod_explicit",
			charonEnv:   "prod",
			ginMode:     "",
			expected:    false,
			description: "CHARON_ENV=prod should default to false",
		},
		{
			name:        "gin_debug",
			charonEnv:   "",
			ginMode:     "debug",
			expected:    true,
			description: "GIN_MODE=debug should default to true",
		},
		{
			name:        "gin_test",
			charonEnv:   "",
			ginMode:     "test",
			expected:    true,
			description: "GIN_MODE=test should default to true",
		},
		{
			name:        "both_unset",
			charonEnv:   "",
			ginMode:     "",
			expected:    false,
			description: "Both unset should default to false (production)",
		},
		{
			name:        "development",
			charonEnv:   "development",
			ginMode:     "",
			expected:    true,
			description: "CHARON_ENV=development should default to true",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			db := setupEnhancedServiceDB(t)
			service := NewEnhancedSecurityNotificationService(db)

			// Set environment
			_ = os.Setenv("CHARON_ENV", tc.charonEnv)
			_ = os.Setenv("GIN_MODE", tc.ginMode)

			// Test
			enabled, err := service.isFeatureEnabled()
			require.NoError(t, err)
			assert.Equal(t, tc.expected, enabled, tc.description)

			// Verify setting was created
			var setting models.Setting
			require.NoError(t, db.Where("key = ?", "feature.notifications.security_provider_events.enabled").First(&setting).Error)
			expectedValue := "false"
			if tc.expected {
				expectedValue = "true"
			}
			assert.Equal(t, expectedValue, setting.Value)
		})
	}
}

func TestSendWebhook_SSRFValidation(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	testCases := []struct {
		name        string
		webhookURL  string
		shouldFail  bool
		description string
	}{
		{
			name:        "valid_https",
			webhookURL:  "https://example.com/webhook",
			shouldFail:  false,
			description: "HTTPS should be allowed",
		},
		{
			name:        "valid_http",
			webhookURL:  "http://example.com/webhook",
			shouldFail:  false,
			description: "HTTP should be allowed for backwards compatibility",
		},
		{
			name:        "empty_url",
			webhookURL:  "",
			shouldFail:  true,
			description: "Empty URL should fail validation",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			event := models.SecurityEvent{
				EventType: "waf_block",
				Severity:  "high",
				Message:   "Test event",
			}

			err := service.sendWebhook(context.Background(), tc.webhookURL, event)
			if tc.shouldFail {
				assert.Error(t, err, tc.description)
			} else {
				// May fail with network error but should pass SSRF validation
				// We're testing the validation step, not the actual HTTP call
				if err != nil {
					assert.NotContains(t, err.Error(), "ssrf validation failed", tc.description)
				}
			}
		})
	}
}

func TestSendWebhook_Success(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Create test server
	callCount := 0
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		callCount++
		assert.Equal(t, "POST", r.Method)
		assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
		assert.Equal(t, "Charon-Cerberus/1.0", r.Header.Get("User-Agent"))

		// Verify payload
		var event models.SecurityEvent
		err := json.NewDecoder(r.Body).Decode(&event)
		assert.NoError(t, err)
		assert.Equal(t, "waf_block", event.EventType)

		w.WriteHeader(http.StatusOK)
	}))
	defer server.Close()

	event := models.SecurityEvent{
		EventType: "waf_block",
		Severity:  "high",
		Message:   "Test event",
	}

	err := service.sendWebhook(context.Background(), server.URL, event)
	assert.NoError(t, err)
	assert.Equal(t, 1, callCount)
}

func TestNormalizeSecurityEventType(t *testing.T) {
	testCases := []struct {
		input    string
		expected string
	}{
		{"WAF Block", "waf_block"},
		{"waf_block", "waf_block"},
		{"ACL Deny", "acl_deny"},
		{"acl_deny", "acl_deny"},
		{"Rate Limit", "rate_limit"},
		{"rate_limit", "rate_limit"},
		{"CrowdSec Decision", "crowdsec_decision"},
		{"crowdsec_decision", "crowdsec_decision"},
		{"unknown_event", "unknown_event"},
		{" WAF ", "waf_block"},
		{" ACL ", "acl_deny"},
	}

	for _, tc := range testCases {
		t.Run(tc.input, func(t *testing.T) {
			result := normalizeSecurityEventType(tc.input)
			assert.Equal(t, tc.expected, result)
		})
	}
}

func TestGetDefaultFeatureFlagValue(t *testing.T) {
	// Save and restore env vars
	origCharonEnv := os.Getenv("CHARON_ENV")
	origGinMode := os.Getenv("GIN_MODE")
	defer func() {
		_ = os.Setenv("CHARON_ENV", origCharonEnv)
		_ = os.Setenv("GIN_MODE", origGinMode)
	}()

	testCases := []struct {
		name      string
		charonEnv string
		ginMode   string
		expected  string
	}{
		{"production", "production", "", "false"},
		{"prod", "prod", "", "false"},
		{"debug", "", "debug", "true"},
		{"test", "", "test", "true"},
		{"both_unset", "", "", "false"},
		{"development", "development", "", "true"},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			db := setupEnhancedServiceDB(t)
			service := NewEnhancedSecurityNotificationService(db)

			_ = os.Setenv("CHARON_ENV", tc.charonEnv)
			_ = os.Setenv("GIN_MODE", tc.ginMode)

			result := service.getDefaultFeatureFlagValue()
			assert.Equal(t, tc.expected, result)
		})
	}
}

func TestGetDefaultFeatureFlagValue_TestMode(t *testing.T) {
	db := setupEnhancedServiceDB(t)
	service := NewEnhancedSecurityNotificationService(db)

	// Create test mode marker
	require.NoError(t, db.Create(&models.Setting{
		Key:   "_test_mode_marker",
		Value: "true",
		Type:  "bool",
	}).Error)

	result := service.getDefaultFeatureFlagValue()
	assert.Equal(t, "true", result, "Test mode should return true")
}
