package handlers

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/Wikid82/charon/backend/internal/services"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

// TestSecurityEventIntakeCompileSuccess tests that the handler is properly instantiated and route registration doesn't fail.
// Blocker 1: Fix compile error - undefined handler reference.
func TestSecurityEventIntakeCompileSuccess(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	// This test validates that the handler can be instantiated with all required dependencies
	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	securityService := services.NewSecurityService(db)
	managementCIDRs := []string{"127.0.0.0/8"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		securityService,
		"/data",
		notificationService,
		managementCIDRs,
	)

	require.NotNil(t, handler, "Handler should be instantiated successfully")
	require.NotNil(t, handler.notificationService, "Notification service should be set")
	require.NotNil(t, handler.managementCIDRs, "Management CIDRs should be set")
	assert.Equal(t, 1, len(handler.managementCIDRs), "Management CIDRs should have one entry")
}

// TestSecurityEventIntakeAuthLocalhost tests that localhost requests are accepted.
// Blocker 2: Implement real source validation - localhost should be allowed.
func TestSecurityEventIntakeAuthLocalhost(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	managementCIDRs := []string{"10.0.0.0/8"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		nil,
		"",
		notificationService,
		managementCIDRs,
	)

	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	event := models.SecurityEvent{
		EventType: "waf_block",
		Severity:  "error",
		Message:   "WAF blocked request",
		ClientIP:  "192.168.1.100",
		Path:      "/admin",
		Timestamp: time.Now(),
	}
	body, _ := json.Marshal(event)

	// Localhost IPv4 request
	c.Request = httptest.NewRequest("POST", "/api/v1/security/events", bytes.NewReader(body))
	c.Request.RemoteAddr = "127.0.0.1:12345"
	c.Request.Header.Set("Content-Type", "application/json")

	handler.HandleSecurityEvent(c)

	assert.Equal(t, http.StatusAccepted, w.Code, "Localhost should be accepted")
}

// TestSecurityEventIntakeAuthManagementCIDR tests that management network requests are accepted.
// Blocker 2: Implement real source validation - management CIDRs should be allowed.
func TestSecurityEventIntakeAuthManagementCIDR(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	managementCIDRs := []string{"192.168.1.0/24", "10.0.0.0/8"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		nil,
		"",
		notificationService,
		managementCIDRs,
	)

	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	event := models.SecurityEvent{
		EventType: "waf_block",
		Severity:  "error",
		Message:   "WAF blocked request",
		ClientIP:  "8.8.8.8",
		Path:      "/admin",
		Timestamp: time.Now(),
	}
	body, _ := json.Marshal(event)

	// Request from management network
	c.Request = httptest.NewRequest("POST", "/api/v1/security/events", bytes.NewReader(body))
	c.Request.RemoteAddr = "192.168.1.50:12345"
	c.Request.Header.Set("Content-Type", "application/json")

	handler.HandleSecurityEvent(c)

	assert.Equal(t, http.StatusAccepted, w.Code, "Management CIDR should be accepted")
}

// TestSecurityEventIntakeAuthUnauthorizedIP tests that external IPs are rejected.
// Blocker 2: Implement real source validation - external IPs should be rejected.
func TestSecurityEventIntakeAuthUnauthorizedIP(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	managementCIDRs := []string{"192.168.1.0/24"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		nil,
		"",
		notificationService,
		managementCIDRs,
	)

	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	event := models.SecurityEvent{
		EventType: "waf_block",
		Severity:  "error",
		Message:   "WAF blocked request",
		ClientIP:  "8.8.8.8",
		Path:      "/admin",
		Timestamp: time.Now(),
	}
	body, _ := json.Marshal(event)

	// External IP not in management network
	c.Request = httptest.NewRequest("POST", "/api/v1/security/events", bytes.NewReader(body))
	c.Request.RemoteAddr = "8.8.8.8:12345"
	c.Request.Header.Set("Content-Type", "application/json")

	handler.HandleSecurityEvent(c)

	assert.Equal(t, http.StatusForbidden, w.Code, "External IP should be rejected")

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.Equal(t, "unauthorized_source", response["error"])
}

// TestSecurityEventIntakeAuthInvalidIP tests that malformed IPs are rejected.
// Blocker 2: Implement real source validation - invalid IPs should be rejected.
func TestSecurityEventIntakeAuthInvalidIP(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	managementCIDRs := []string{"192.168.1.0/24"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		nil,
		"",
		notificationService,
		managementCIDRs,
	)

	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	event := models.SecurityEvent{
		EventType: "waf_block",
		Severity:  "error",
		Message:   "WAF blocked request",
		ClientIP:  "8.8.8.8",
		Path:      "/admin",
		Timestamp: time.Now(),
	}
	body, _ := json.Marshal(event)

	// Malformed IP
	c.Request = httptest.NewRequest("POST", "/api/v1/security/events", bytes.NewReader(body))
	c.Request.RemoteAddr = "invalid-ip:12345"
	c.Request.Header.Set("Content-Type", "application/json")

	handler.HandleSecurityEvent(c)

	assert.Equal(t, http.StatusForbidden, w.Code, "Invalid IP should be rejected")

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.Equal(t, "invalid_source", response["error"])
}

// TestSecurityEventIntakeDispatchInvoked tests that notification dispatch is invoked.
// Blocker 3: Implement actual dispatch - remove TODO/no-op behavior.
func TestSecurityEventIntakeDispatchInvoked(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	// Create a Discord provider with security notifications enabled
	provider := &models.NotificationProvider{
		Name:                            "Discord Security Alerts",
		Type:                            "discord",
		URL:                             "https://discord.com/api/webhooks/123/token",
		Enabled:                         true,
		NotifySecurityWAFBlocks:         true,
		NotifySecurityACLDenies:         true,
		NotifySecurityRateLimitHits:     true,
		NotifySecurityCrowdSecDecisions: true,
	}
	require.NoError(t, db.Create(provider).Error)

	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	managementCIDRs := []string{"127.0.0.0/8"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		nil,
		"",
		notificationService,
		managementCIDRs,
	)

	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	event := models.SecurityEvent{
		EventType: "waf_block",
		Severity:  "error",
		Message:   "WAF blocked suspicious request",
		ClientIP:  "192.168.1.100",
		Path:      "/admin/login",
		Timestamp: time.Now(),
		Metadata: map[string]any{
			"rule_id":      "920100",
			"matched_data": "suspicious pattern",
		},
	}
	body, _ := json.Marshal(event)

	c.Request = httptest.NewRequest("POST", "/api/v1/security/events", bytes.NewReader(body))
	c.Request.RemoteAddr = "127.0.0.1:12345"
	c.Request.Header.Set("Content-Type", "application/json")

	handler.HandleSecurityEvent(c)

	assert.Equal(t, http.StatusAccepted, w.Code, "Event should be accepted")

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.Equal(t, "Security event recorded", response["message"])
	assert.Equal(t, "waf_block", response["event_type"])
	assert.NotEmpty(t, response["timestamp"])
}

// TestSecurityEventIntakeR6Intact tests that legacy PUT endpoint returns 410 Gone.
// Constraint: Keep R6 410/no-mutation behavior intact.
func TestSecurityEventIntakeR6Intact(t *testing.T) {
	// Create in-memory database with User table for this test
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(
		&models.User{},
		&models.NotificationProvider{},
		&models.NotificationConfig{},
		&models.Setting{},
	))

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

	// Create an admin user for authentication
	adminUser := &models.User{
		Email:        "admin@example.com",
		Name:         "Admin User",
		PasswordHash: "$2a$10$abcdefghijklmnopqrstuvwxyz", // Dummy bcrypt hash
		Role:         "admin",
		Enabled:      true,
	}
	require.NoError(t, db.Create(adminUser).Error)

	service := services.NewEnhancedSecurityNotificationService(db)
	handler := NewSecurityNotificationHandler(service)

	gin.SetMode(gin.TestMode)
	router := gin.New()

	// Add auth middleware that sets user context
	router.Use(func(c *gin.Context) {
		c.Set("user_id", adminUser.ID)
		c.Set("user", adminUser)
		c.Set("role", "admin")
		c.Next()
	})

	router.PUT("/api/v1/notifications/settings/security", handler.UpdateSettings)

	reqBody := map[string]interface{}{
		"enabled":              true,
		"security_waf_enabled": true,
	}
	body, _ := json.Marshal(reqBody)

	w := httptest.NewRecorder()
	req := httptest.NewRequest("PUT", "/api/v1/notifications/settings/security", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusGone, w.Code, "PUT should return 410 Gone")

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.Equal(t, "legacy_security_settings_deprecated", response["error"])
	assert.Equal(t, "LEGACY_SECURITY_SETTINGS_DEPRECATED", response["code"])
}

// TestSecurityEventIntakeDiscordOnly tests Discord-only dispatch enforcement.
// Constraint: Keep Discord-only dispatch enforcement for this rollout stage.
func TestSecurityEventIntakeDiscordOnly(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	// Create various provider types
	discordProvider := &models.NotificationProvider{
		Name:                    "Discord",
		Type:                    "discord",
		URL:                     "https://discord.com/api/webhooks/123/token",
		Enabled:                 true,
		NotifySecurityWAFBlocks: true,
	}
	require.NoError(t, db.Create(discordProvider).Error)

	// Non-Discord providers should also work with supportsJSONTemplates
	webhookProvider := &models.NotificationProvider{
		Name:                    "Webhook",
		Type:                    "webhook",
		URL:                     "https://example.com/webhook",
		Enabled:                 true,
		NotifySecurityWAFBlocks: true,
	}
	require.NoError(t, db.Create(webhookProvider).Error)

	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	managementCIDRs := []string{"127.0.0.0/8"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		nil,
		"",
		notificationService,
		managementCIDRs,
	)

	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	event := models.SecurityEvent{
		EventType: "waf_block",
		Severity:  "error",
		Message:   "WAF blocked request",
		ClientIP:  "192.168.1.100",
		Path:      "/admin",
		Timestamp: time.Now(),
	}
	body, _ := json.Marshal(event)

	c.Request = httptest.NewRequest("POST", "/api/v1/security/events", bytes.NewReader(body))
	c.Request.RemoteAddr = "127.0.0.1:12345"
	c.Request.Header.Set("Content-Type", "application/json")

	handler.HandleSecurityEvent(c)

	assert.Equal(t, http.StatusAccepted, w.Code)

	// Validate both Discord and webhook providers exist (JSON-capable)
	var providers []models.NotificationProvider
	err := db.Where("enabled = ?", true).Find(&providers).Error
	require.NoError(t, err)
	assert.Equal(t, 2, len(providers), "Both Discord and webhook providers should be enabled")
}

// TestSecurityEventIntakeMalformedPayload tests rejection of malformed event payloads.
func TestSecurityEventIntakeMalformedPayload(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	managementCIDRs := []string{"127.0.0.0/8"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		nil,
		"",
		notificationService,
		managementCIDRs,
	)

	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	// Malformed JSON
	c.Request = httptest.NewRequest("POST", "/api/v1/security/events", bytes.NewReader([]byte("{invalid json")))
	c.Request.RemoteAddr = "127.0.0.1:12345"
	c.Request.Header.Set("Content-Type", "application/json")

	handler.HandleSecurityEvent(c)

	assert.Equal(t, http.StatusBadRequest, w.Code, "Malformed JSON should be rejected")

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	assert.Equal(t, "Invalid security event payload", response["error"])
}

// TestSecurityEventIntakeIPv6Localhost tests that IPv6 localhost is accepted.
func TestSecurityEventIntakeIPv6Localhost(t *testing.T) {
	db := SetupCompatibilityTestDB(t)

	notificationService := services.NewNotificationService(db)
	service := services.NewEnhancedSecurityNotificationService(db)
	managementCIDRs := []string{"10.0.0.0/8"}

	handler := NewSecurityNotificationHandlerWithDeps(
		service,
		nil,
		"",
		notificationService,
		managementCIDRs,
	)

	gin.SetMode(gin.TestMode)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	event := models.SecurityEvent{
		EventType: "acl_deny",
		Severity:  "warn",
		Message:   "ACL denied request",
		ClientIP:  "::1",
		Path:      "/api/admin",
		Timestamp: time.Now(),
	}
	body, _ := json.Marshal(event)

	// IPv6 localhost
	c.Request = httptest.NewRequest("POST", "/api/v1/security/events", bytes.NewReader(body))
	c.Request.RemoteAddr = "[::1]:12345"
	c.Request.Header.Set("Content-Type", "application/json")

	handler.HandleSecurityEvent(c)

	assert.Equal(t, http.StatusAccepted, w.Code, "IPv6 localhost should be accepted")
}
