package handlers

import (
	"encoding/json"
	"errors"
	"net"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	log "github.com/sirupsen/logrus"
	"gorm.io/gorm"

	"github.com/Wikid82/charon/backend/internal/caddy"
	"github.com/Wikid82/charon/backend/internal/config"
	"github.com/Wikid82/charon/backend/internal/models"
	"github.com/Wikid82/charon/backend/internal/services"
)

// WAFExclusionRequest represents a rule exclusion for false positives
type WAFExclusionRequest struct {
	RuleID      int    `json:"rule_id" binding:"required"`
	Target      string `json:"target,omitempty"`      // e.g., "ARGS:password"
	Description string `json:"description,omitempty"` // Human-readable reason
}

// WAFExclusion represents a stored rule exclusion
type WAFExclusion struct {
	RuleID      int    `json:"rule_id"`
	Target      string `json:"target,omitempty"`
	Description string `json:"description,omitempty"`
}

// SecurityHandler handles security-related API requests.
type SecurityHandler struct {
	cfg          config.SecurityConfig
	db           *gorm.DB
	svc          *services.SecurityService
	caddyManager *caddy.Manager
	geoipSvc     *services.GeoIPService
}

// NewSecurityHandler creates a new SecurityHandler.
func NewSecurityHandler(cfg config.SecurityConfig, db *gorm.DB, caddyManager *caddy.Manager) *SecurityHandler {
	svc := services.NewSecurityService(db)
	return &SecurityHandler{cfg: cfg, db: db, svc: svc, caddyManager: caddyManager}
}

// SetGeoIPService sets the GeoIP service for the handler.
func (h *SecurityHandler) SetGeoIPService(geoipSvc *services.GeoIPService) {
	h.geoipSvc = geoipSvc
}

// GetStatus returns the current status of all security services.
// Priority chain:
// 1. Settings table (highest - runtime overrides)
// 2. SecurityConfig DB record (middle - user configuration)
// 3. Static config (lowest - defaults)
func (h *SecurityHandler) GetStatus(c *gin.Context) {
	// Start with static config defaults
	enabled := h.cfg.CerberusEnabled
	wafMode := h.cfg.WAFMode
	rateLimitMode := h.cfg.RateLimitMode
	crowdSecMode := h.cfg.CrowdSecMode
	crowdSecAPIURL := h.cfg.CrowdSecAPIURL
	aclMode := h.cfg.ACLMode

	// Override with database SecurityConfig if present (priority 2)
	if h.db != nil {
		var sc models.SecurityConfig
		if err := h.db.Where("name = ?", "default").First(&sc).Error; err == nil {
			// SecurityConfig in DB takes precedence over static config
			enabled = sc.Enabled
			if sc.WAFMode != "" {
				wafMode = sc.WAFMode
			}
			if sc.RateLimitMode != "" {
				rateLimitMode = sc.RateLimitMode
			} else if sc.RateLimitEnable {
				rateLimitMode = "enabled"
			}
			if sc.CrowdSecMode != "" {
				crowdSecMode = sc.CrowdSecMode
			}
			if sc.CrowdSecAPIURL != "" {
				crowdSecAPIURL = sc.CrowdSecAPIURL
			}
		}

		// Check runtime setting overrides from settings table (priority 1 - highest)
		var setting struct{ Value string }

		// Cerberus enabled override
		if err := h.db.Raw("SELECT value FROM settings WHERE key = ? LIMIT 1", "feature.cerberus.enabled").Scan(&setting).Error; err == nil && setting.Value != "" {
			enabled = strings.EqualFold(setting.Value, "true")
		}

		// WAF enabled override
		setting = struct{ Value string }{}
		if err := h.db.Raw("SELECT value FROM settings WHERE key = ? LIMIT 1", "security.waf.enabled").Scan(&setting).Error; err == nil && setting.Value != "" {
			if strings.EqualFold(setting.Value, "true") {
				wafMode = "enabled"
			} else {
				wafMode = "disabled"
			}
		}

		// Rate Limit enabled override
		setting = struct{ Value string }{}
		if err := h.db.Raw("SELECT value FROM settings WHERE key = ? LIMIT 1", "security.rate_limit.enabled").Scan(&setting).Error; err == nil && setting.Value != "" {
			if strings.EqualFold(setting.Value, "true") {
				rateLimitMode = "enabled"
			} else {
				rateLimitMode = "disabled"
			}
		}

		// CrowdSec enabled override
		setting = struct{ Value string }{}
		if err := h.db.Raw("SELECT value FROM settings WHERE key = ? LIMIT 1", "security.crowdsec.enabled").Scan(&setting).Error; err == nil && setting.Value != "" {
			if strings.EqualFold(setting.Value, "true") {
				crowdSecMode = "local"
			} else {
				crowdSecMode = "disabled"
			}
		}

		// CrowdSec mode override
		setting = struct{ Value string }{}
		if err := h.db.Raw("SELECT value FROM settings WHERE key = ? LIMIT 1", "security.crowdsec.mode").Scan(&setting).Error; err == nil && setting.Value != "" {
			crowdSecMode = setting.Value
		}

		// ACL enabled override
		setting = struct{ Value string }{}
		if err := h.db.Raw("SELECT value FROM settings WHERE key = ? LIMIT 1", "security.acl.enabled").Scan(&setting).Error; err == nil && setting.Value != "" {
			if strings.EqualFold(setting.Value, "true") {
				aclMode = "enabled"
			} else {
				aclMode = "disabled"
			}
		}
	}

	// Map unknown/external mode to disabled
	if crowdSecMode != "local" && crowdSecMode != "disabled" {
		crowdSecMode = "disabled"
	}

	// Compute effective enabled state for each feature
	wafEnabled := wafMode != "" && wafMode != "disabled"
	rateLimitEnabled := rateLimitMode == "enabled"
	crowdsecEnabled := crowdSecMode == "local"
	aclEnabled := aclMode == "enabled"

	// All features require Cerberus to be enabled
	if !enabled {
		wafEnabled = false
		rateLimitEnabled = false
		crowdsecEnabled = false
		aclEnabled = false
		wafMode = "disabled"
		rateLimitMode = "disabled"
		crowdSecMode = "disabled"
		aclMode = "disabled"
	}

	c.JSON(http.StatusOK, gin.H{
		"cerberus": gin.H{"enabled": enabled},
		"crowdsec": gin.H{
			"mode":    crowdSecMode,
			"api_url": crowdSecAPIURL,
			"enabled": crowdsecEnabled,
		},
		"waf": gin.H{
			"mode":    wafMode,
			"enabled": wafEnabled,
		},
		"rate_limit": gin.H{
			"mode":    rateLimitMode,
			"enabled": rateLimitEnabled,
		},
		"acl": gin.H{
			"mode":    aclMode,
			"enabled": aclEnabled,
		},
	})
}

// GetConfig returns the site security configuration from DB or default
func (h *SecurityHandler) GetConfig(c *gin.Context) {
	cfg, err := h.svc.Get()
	if err != nil {
		if err == services.ErrSecurityConfigNotFound {
			c.JSON(http.StatusOK, gin.H{"config": nil})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to read security config"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"config": cfg})
}

// UpdateConfig creates or updates the SecurityConfig in DB
func (h *SecurityHandler) UpdateConfig(c *gin.Context) {
	var payload models.SecurityConfig
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
		return
	}
	if payload.Name == "" {
		payload.Name = "default"
	}
	// Sync RateLimitMode with RateLimitEnable for backward compatibility
	if payload.RateLimitEnable {
		payload.RateLimitMode = "enabled"
	} else if payload.RateLimitMode == "" {
		payload.RateLimitMode = "disabled"
	}
	if err := h.svc.Upsert(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	// Apply updated config to Caddy so WAF mode changes take effect
	if h.caddyManager != nil {
		if err := h.caddyManager.ApplyConfig(c.Request.Context()); err != nil {
			log.WithError(err).Warn("failed to apply security config changes to Caddy")
		}
	}
	c.JSON(http.StatusOK, gin.H{"config": payload})
}

// GenerateBreakGlass generates a break-glass token and returns the plaintext token once
func (h *SecurityHandler) GenerateBreakGlass(c *gin.Context) {
	token, err := h.svc.GenerateBreakGlassToken("default")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate break-glass token"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"token": token})
}

// ListDecisions returns recent security decisions
func (h *SecurityHandler) ListDecisions(c *gin.Context) {
	limit := 50
	if q := c.Query("limit"); q != "" {
		if v, err := strconv.Atoi(q); err == nil {
			limit = v
		}
	}
	list, err := h.svc.ListDecisions(limit)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list decisions"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"decisions": list})
}

// CreateDecision creates a manual decision (override) - for now no checks besides payload
func (h *SecurityHandler) CreateDecision(c *gin.Context) {
	var payload models.SecurityDecision
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
		return
	}
	if payload.IP == "" || payload.Action == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "ip and action are required"})
		return
	}
	// Populate source
	payload.Source = "manual"
	if err := h.svc.LogDecision(&payload); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to log decision"})
		return
	}
	// Record an audit entry
	actor := c.GetString("user_id")
	if actor == "" {
		actor = c.ClientIP()
	}
	_ = h.svc.LogAudit(&models.SecurityAudit{Actor: actor, Action: "create_decision", Details: payload.Details})
	c.JSON(http.StatusOK, gin.H{"decision": payload})
}

// ListRuleSets returns the list of known rulesets
func (h *SecurityHandler) ListRuleSets(c *gin.Context) {
	list, err := h.svc.ListRuleSets()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list rule sets"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"rulesets": list})
}

// UpsertRuleSet uploads or updates a ruleset
func (h *SecurityHandler) UpsertRuleSet(c *gin.Context) {
	var payload models.SecurityRuleSet
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
		return
	}
	if payload.Name == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "name required"})
		return
	}
	if err := h.svc.UpsertRuleSet(&payload); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to upsert ruleset"})
		return
	}
	if h.caddyManager != nil {
		if err := h.caddyManager.ApplyConfig(c.Request.Context()); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to apply configuration: " + err.Error()})
			return
		}
	}
	// Create an audit event
	actor := c.GetString("user_id")
	if actor == "" {
		actor = c.ClientIP()
	}
	_ = h.svc.LogAudit(&models.SecurityAudit{Actor: actor, Action: "upsert_ruleset", Details: payload.Name})
	c.JSON(http.StatusOK, gin.H{"ruleset": payload})
}

// DeleteRuleSet removes a ruleset by id
func (h *SecurityHandler) DeleteRuleSet(c *gin.Context) {
	idParam := c.Param("id")
	if idParam == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "id is required"})
		return
	}
	id, err := strconv.ParseUint(idParam, 10, 32)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
		return
	}
	if err := h.svc.DeleteRuleSet(uint(id)); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			c.JSON(http.StatusNotFound, gin.H{"error": "ruleset not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete ruleset"})
		return
	}
	if h.caddyManager != nil {
		if err := h.caddyManager.ApplyConfig(c.Request.Context()); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to apply configuration: " + err.Error()})
			return
		}
	}
	actor := c.GetString("user_id")
	if actor == "" {
		actor = c.ClientIP()
	}
	_ = h.svc.LogAudit(&models.SecurityAudit{Actor: actor, Action: "delete_ruleset", Details: idParam})
	c.JSON(http.StatusOK, gin.H{"deleted": true})
}

// Enable toggles Cerberus on, validating admin whitelist or break-glass token
func (h *SecurityHandler) Enable(c *gin.Context) {
	// Look for requester's IP and optional breakglass token
	adminIP := c.ClientIP()
	var body struct {
		Token string `json:"break_glass_token"`
	}
	_ = c.ShouldBindJSON(&body)

	// If config exists, require that adminIP is in whitelist or token matches
	cfg, err := h.svc.Get()
	if err != nil && err != services.ErrSecurityConfigNotFound {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to retrieve security config"})
		return
	}
	if cfg != nil {
		// Check admin whitelist
		if cfg.AdminWhitelist == "" && body.Token == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "admin whitelist missing; provide break_glass_token or add admin_whitelist CIDR before enabling"})
			return
		}
		if body.Token != "" {
			ok, err := h.svc.VerifyBreakGlassToken(cfg.Name, body.Token)
			if err == nil && ok {
				// proceed
			} else {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "break glass token invalid"})
				return
			}
		} else {
			// verify client IP in admin whitelist
			found := false
			for _, entry := range strings.Split(cfg.AdminWhitelist, ",") {
				entry = strings.TrimSpace(entry)
				if entry == "" {
					continue
				}
				if entry == adminIP {
					found = true
					break
				}
				// If CIDR, check contains
				if _, cidr, err := net.ParseCIDR(entry); err == nil {
					if cidr.Contains(net.ParseIP(adminIP)) {
						found = true
						break
					}
				}
			}
			if !found {
				c.JSON(http.StatusForbidden, gin.H{"error": "admin IP not present in admin_whitelist"})
				return
			}
		}
	}
	// Set enabled true
	newCfg := &models.SecurityConfig{Name: "default", Enabled: true}
	if cfg != nil {
		newCfg = cfg
		newCfg.Enabled = true
	}
	if err := h.svc.Upsert(newCfg); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to enable Cerberus"})
		return
	}
	if h.caddyManager != nil {
		if err := h.caddyManager.ApplyConfig(c.Request.Context()); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to apply configuration: " + err.Error()})
			return
		}
	}
	c.JSON(http.StatusOK, gin.H{"enabled": true})
}

// Disable toggles Cerberus off; requires break-glass token or localhost request
func (h *SecurityHandler) Disable(c *gin.Context) {
	var body struct {
		Token string `json:"break_glass_token"`
	}
	_ = c.ShouldBindJSON(&body)
	// Allow requests from localhost to disable without token
	clientIP := c.ClientIP()
	if clientIP == "127.0.0.1" || clientIP == "::1" {
		cfg, _ := h.svc.Get()
		if cfg == nil {
			cfg = &models.SecurityConfig{Name: "default", Enabled: false}
		} else {
			cfg.Enabled = false
		}
		_ = h.svc.Upsert(cfg)
		if h.caddyManager != nil {
			_ = h.caddyManager.ApplyConfig(c.Request.Context())
		}
		c.JSON(http.StatusOK, gin.H{"enabled": false})
		return
	}
	cfg, err := h.svc.Get()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to read config"})
		return
	}
	if body.Token == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "break glass token required to disable Cerberus from non-localhost"})
		return
	}
	ok, err := h.svc.VerifyBreakGlassToken(cfg.Name, body.Token)
	if err != nil || !ok {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "break glass token invalid"})
		return
	}
	cfg.Enabled = false
	_ = h.svc.Upsert(cfg)
	if h.caddyManager != nil {
		_ = h.caddyManager.ApplyConfig(c.Request.Context())
	}
	c.JSON(http.StatusOK, gin.H{"enabled": false})
}

// GetRateLimitPresets returns predefined rate limit configurations
func (h *SecurityHandler) GetRateLimitPresets(c *gin.Context) {
	presets := []map[string]interface{}{
		{
			"id":          "standard",
			"name":        "Standard Web",
			"description": "Balanced protection for general web applications",
			"requests":    100,
			"window_sec":  60,
			"burst":       20,
		},
		{
			"id":          "api",
			"name":        "API Protection",
			"description": "Stricter limits for API endpoints",
			"requests":    30,
			"window_sec":  60,
			"burst":       10,
		},
		{
			"id":          "login",
			"name":        "Login Protection",
			"description": "Aggressive protection against brute-force",
			"requests":    5,
			"window_sec":  300,
			"burst":       2,
		},
		{
			"id":          "relaxed",
			"name":        "High Traffic",
			"description": "Higher limits for trusted, high-traffic apps",
			"requests":    500,
			"window_sec":  60,
			"burst":       100,
		},
	}
	c.JSON(http.StatusOK, gin.H{"presets": presets})
}

// GetGeoIPStatus returns the current status of the GeoIP service.
func (h *SecurityHandler) GetGeoIPStatus(c *gin.Context) {
	if h.geoipSvc == nil {
		c.JSON(http.StatusOK, gin.H{
			"loaded":   false,
			"message":  "GeoIP service not initialized",
			"db_path":  "",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"loaded":   h.geoipSvc.IsLoaded(),
		"db_path":  h.geoipSvc.GetDatabasePath(),
		"message":  "GeoIP service available",
	})
}

// ReloadGeoIP reloads the GeoIP database from disk.
func (h *SecurityHandler) ReloadGeoIP(c *gin.Context) {
	if h.geoipSvc == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"error": "GeoIP service not initialized",
		})
		return
	}

	if err := h.geoipSvc.Load(); err != nil {
		log.WithError(err).Error("Failed to reload GeoIP database")
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Failed to reload GeoIP database: " + err.Error(),
		})
		return
	}

	// Log audit event
	actor := c.GetString("user_id")
	if actor == "" {
		actor = c.ClientIP()
	}
	_ = h.svc.LogAudit(&models.SecurityAudit{Actor: actor, Action: "reload_geoip", Details: "GeoIP database reloaded successfully"})

	c.JSON(http.StatusOK, gin.H{
		"message": "GeoIP database reloaded successfully",
		"loaded":  h.geoipSvc.IsLoaded(),
		"db_path": h.geoipSvc.GetDatabasePath(),
	})
}

// LookupGeoIP performs a GeoIP lookup for a given IP address.
func (h *SecurityHandler) LookupGeoIP(c *gin.Context) {
	var req struct {
		IPAddress string `json:"ip_address" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "ip_address is required"})
		return
	}

	if h.geoipSvc == nil || !h.geoipSvc.IsLoaded() {
		c.JSON(http.StatusServiceUnavailable, gin.H{
			"error": "GeoIP service not available",
		})
		return
	}

	country, err := h.geoipSvc.LookupCountry(req.IPAddress)
	if err != nil {
		if errors.Is(err, services.ErrInvalidGeoIP) {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid IP address"})
			return
		}
		if errors.Is(err, services.ErrCountryNotFound) {
			c.JSON(http.StatusOK, gin.H{
				"ip_address":   req.IPAddress,
				"country_code": "",
				"found":        false,
				"message":      "No country found for this IP address",
			})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "GeoIP lookup failed: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"ip_address":   req.IPAddress,
		"country_code": country,
		"found":        true,
	})
}

// GetWAFExclusions returns current WAF rule exclusions from SecurityConfig
func (h *SecurityHandler) GetWAFExclusions(c *gin.Context) {
	cfg, err := h.svc.Get()
	if err != nil {
		if err == services.ErrSecurityConfigNotFound {
			c.JSON(http.StatusOK, gin.H{"exclusions": []WAFExclusion{}})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to read security config"})
		return
	}

	var exclusions []WAFExclusion
	if cfg.WAFExclusions != "" {
		if err := json.Unmarshal([]byte(cfg.WAFExclusions), &exclusions); err != nil {
			log.WithError(err).Warn("Failed to parse WAF exclusions")
			exclusions = []WAFExclusion{}
		}
	}

	c.JSON(http.StatusOK, gin.H{"exclusions": exclusions})
}

// AddWAFExclusion adds a rule exclusion to the WAF configuration
func (h *SecurityHandler) AddWAFExclusion(c *gin.Context) {
	var req WAFExclusionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "rule_id is required"})
		return
	}

	if req.RuleID <= 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "rule_id must be a positive integer"})
		return
	}

	cfg, err := h.svc.Get()
	if err != nil {
		if err == services.ErrSecurityConfigNotFound {
			// Create default config with the exclusion
			cfg = &models.SecurityConfig{Name: "default"}
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to read security config"})
			return
		}
	}

	// Parse existing exclusions
	var exclusions []WAFExclusion
	if cfg.WAFExclusions != "" {
		if err := json.Unmarshal([]byte(cfg.WAFExclusions), &exclusions); err != nil {
			log.WithError(err).Warn("Failed to parse existing WAF exclusions")
			exclusions = []WAFExclusion{}
		}
	}

	// Check for duplicate rule_id with same target
	for _, e := range exclusions {
		if e.RuleID == req.RuleID && e.Target == req.Target {
			c.JSON(http.StatusConflict, gin.H{"error": "exclusion for this rule_id and target already exists"})
			return
		}
	}

	// Add the new exclusion - convert request to WAFExclusion type
	newExclusion := WAFExclusion(req)
	exclusions = append(exclusions, newExclusion)

	// Marshal back to JSON
	exclusionsJSON, err := json.Marshal(exclusions)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to serialize exclusions"})
		return
	}

	cfg.WAFExclusions = string(exclusionsJSON)
	if err := h.svc.Upsert(cfg); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save exclusion"})
		return
	}

	// Apply updated config to Caddy
	if h.caddyManager != nil {
		if err := h.caddyManager.ApplyConfig(c.Request.Context()); err != nil {
			log.WithError(err).Warn("failed to apply WAF exclusion changes to Caddy")
		}
	}

	// Log audit event
	actor := c.GetString("user_id")
	if actor == "" {
		actor = c.ClientIP()
	}
	_ = h.svc.LogAudit(&models.SecurityAudit{
		Actor:   actor,
		Action:  "add_waf_exclusion",
		Details: strconv.Itoa(req.RuleID),
	})

	c.JSON(http.StatusOK, gin.H{"exclusion": newExclusion})
}

// DeleteWAFExclusion removes a rule exclusion by rule_id
func (h *SecurityHandler) DeleteWAFExclusion(c *gin.Context) {
	ruleIDParam := c.Param("rule_id")
	if ruleIDParam == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "rule_id is required"})
		return
	}

	ruleID, err := strconv.Atoi(ruleIDParam)
	if err != nil || ruleID <= 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid rule_id"})
		return
	}

	// Get optional target query parameter (for exclusions with specific targets)
	target := c.Query("target")

	cfg, err := h.svc.Get()
	if err != nil {
		if err == services.ErrSecurityConfigNotFound {
			c.JSON(http.StatusNotFound, gin.H{"error": "exclusion not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to read security config"})
		return
	}

	// Parse existing exclusions
	var exclusions []WAFExclusion
	if cfg.WAFExclusions != "" {
		if err := json.Unmarshal([]byte(cfg.WAFExclusions), &exclusions); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to parse exclusions"})
			return
		}
	}

	// Find and remove the exclusion
	found := false
	newExclusions := make([]WAFExclusion, 0, len(exclusions))
	for _, e := range exclusions {
		// Match by rule_id and target (empty target matches exclusions without target)
		if e.RuleID == ruleID && e.Target == target {
			found = true
			continue // Skip this one (delete it)
		}
		newExclusions = append(newExclusions, e)
	}

	if !found {
		c.JSON(http.StatusNotFound, gin.H{"error": "exclusion not found"})
		return
	}

	// Marshal back to JSON
	exclusionsJSON, err := json.Marshal(newExclusions)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to serialize exclusions"})
		return
	}

	cfg.WAFExclusions = string(exclusionsJSON)
	if err := h.svc.Upsert(cfg); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save exclusions"})
		return
	}

	// Apply updated config to Caddy
	if h.caddyManager != nil {
		if err := h.caddyManager.ApplyConfig(c.Request.Context()); err != nil {
			log.WithError(err).Warn("failed to apply WAF exclusion changes to Caddy")
		}
	}

	// Log audit event
	actor := c.GetString("user_id")
	if actor == "" {
		actor = c.ClientIP()
	}
	_ = h.svc.LogAudit(&models.SecurityAudit{
		Actor:   actor,
		Action:  "delete_waf_exclusion",
		Details: ruleIDParam,
	})

	c.JSON(http.StatusOK, gin.H{"deleted": true})
}
