package handlers

import (
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"

	"github.com/Wikid82/charon/backend/internal/models"
)

// FeatureFlagsHandler exposes simple DB-backed feature flags with env fallback.
type FeatureFlagsHandler struct {
	DB *gorm.DB
}

func NewFeatureFlagsHandler(db *gorm.DB) *FeatureFlagsHandler {
	return &FeatureFlagsHandler{DB: db}
}

// defaultFlags lists the canonical feature flags we expose.
var defaultFlags = []string{
	"feature.cerberus.enabled",
	"feature.uptime.enabled",
	"feature.crowdsec.console_enrollment",
	"feature.notifications.engine.notify_v1.enabled",
	"feature.notifications.service.discord.enabled",
	"feature.notifications.service.gotify.enabled",
	"feature.notifications.service.webhook.enabled",
	"feature.notifications.legacy.fallback_enabled",
	"feature.notifications.security_provider_events.enabled", // Blocker 3: Add security_provider_events gate
}

var defaultFlagValues = map[string]bool{
	"feature.cerberus.enabled":                               false, // Cerberus OFF by default (per diagnostic fix)
	"feature.uptime.enabled":                                 true,  // Uptime enabled by default
	"feature.crowdsec.console_enrollment":                    false,
	"feature.notifications.engine.notify_v1.enabled":         false,
	"feature.notifications.service.discord.enabled":          false,
	"feature.notifications.service.gotify.enabled":           false,
	"feature.notifications.service.webhook.enabled":          false,
	"feature.notifications.legacy.fallback_enabled":          false,
	"feature.notifications.security_provider_events.enabled": false, // Blocker 3: Default disabled for this stage
}

var retiredLegacyFallbackEnvAliases = []string{
	"FEATURE_NOTIFICATIONS_LEGACY_FALLBACK_ENABLED",
	"NOTIFICATIONS_LEGACY_FALLBACK_ENABLED",
}

// GetFlags returns a map of feature flag -> bool. DB setting takes precedence
// and falls back to environment variables if present.
func (h *FeatureFlagsHandler) GetFlags(c *gin.Context) {
	// Phase 0: Performance instrumentation
	startTime := time.Now()
	defer func() {
		latency := time.Since(startTime).Milliseconds()
		log.Printf("[METRICS] GET /feature-flags: %dms", latency)
	}()

	result := make(map[string]bool)

	// Phase 1: Batch query optimization - fetch all flags in single query (eliminating N+1)
	var settings []models.Setting
	if err := h.DB.Where("key IN ?", defaultFlags).Find(&settings).Error; err != nil {
		log.Printf("[ERROR] Failed to fetch feature flags: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch feature flags"})
		return
	}

	// Build map for O(1) lookup
	settingsMap := make(map[string]models.Setting)
	for _, s := range settings {
		settingsMap[s.Key] = s
	}

	// Process all flags using the map
	for _, key := range defaultFlags {
		defaultVal := true
		if v, ok := defaultFlagValues[key]; ok {
			defaultVal = v
		}

		if key == "feature.notifications.legacy.fallback_enabled" {
			result[key] = h.resolveRetiredLegacyFallback(settingsMap)
			continue
		}

		// Check if flag exists in DB
		if s, exists := settingsMap[key]; exists {
			v := strings.ToLower(strings.TrimSpace(s.Value))
			b := v == "1" || v == "true" || v == "yes"
			result[key] = b
			continue
		}

		// Fallback to env vars. Try FEATURE_... and also stripped service name e.g. CERBERUS_ENABLED
		envKey := strings.ToUpper(strings.ReplaceAll(key, ".", "_"))
		if ev, ok := os.LookupEnv(envKey); ok {
			if bv, err := strconv.ParseBool(ev); err == nil {
				result[key] = bv
				continue
			}
			// accept 1/0
			result[key] = ev == "1"
			continue
		}

		// Try shorter variant after removing leading "feature."
		if strings.HasPrefix(key, "feature.") {
			short := strings.ToUpper(strings.ReplaceAll(strings.TrimPrefix(key, "feature."), ".", "_"))
			if ev, ok := os.LookupEnv(short); ok {
				if bv, err := strconv.ParseBool(ev); err == nil {
					result[key] = bv
					continue
				}
				result[key] = ev == "1"
				continue
			}
		}

		// Default based on declared flag value
		result[key] = defaultVal
	}

	c.JSON(http.StatusOK, result)
}

func parseFlagBool(raw string) (bool, bool) {
	v := strings.ToLower(strings.TrimSpace(raw))
	switch v {
	case "1", "true", "yes":
		return true, true
	case "0", "false", "no":
		return false, true
	default:
		return false, false
	}
}

func (h *FeatureFlagsHandler) resolveRetiredLegacyFallback(settingsMap map[string]models.Setting) bool {
	const retiredKey = "feature.notifications.legacy.fallback_enabled"

	if s, exists := settingsMap[retiredKey]; exists {
		if _, ok := parseFlagBool(s.Value); !ok {
			log.Printf("[WARN] Invalid persisted retired fallback flag value, forcing disabled: key=%s value=%q", retiredKey, s.Value)
		}
		return false
	}

	for _, alias := range retiredLegacyFallbackEnvAliases {
		if ev, ok := os.LookupEnv(alias); ok {
			if _, parsed := parseFlagBool(ev); !parsed {
				log.Printf("[WARN] Invalid environment retired fallback flag value, forcing disabled: key=%s value=%q", alias, ev)
			}
			return false
		}
	}

	return false
}

// UpdateFlags accepts a JSON object map[string]bool and upserts settings.
func (h *FeatureFlagsHandler) UpdateFlags(c *gin.Context) {
	// Phase 0: Performance instrumentation
	startTime := time.Now()
	defer func() {
		latency := time.Since(startTime).Milliseconds()
		log.Printf("[METRICS] PUT /feature-flags: %dms", latency)
	}()

	var payload map[string]bool
	if err := c.ShouldBindJSON(&payload); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if v, exists := payload["feature.notifications.legacy.fallback_enabled"]; exists && v {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "feature.notifications.legacy.fallback_enabled is retired and can only be false",
			"code":  "LEGACY_FALLBACK_REMOVED",
		})
		return
	}

	// Phase 1: Transaction wrapping - all updates in single atomic transaction
	if err := h.DB.Transaction(func(tx *gorm.DB) error {
		for k, v := range payload {
			// Only allow keys in the default list to avoid arbitrary settings
			allowed := false
			for _, ak := range defaultFlags {
				if ak == k {
					allowed = true
					break
				}
			}
			if !allowed {
				continue
			}

			if k == "feature.notifications.legacy.fallback_enabled" {
				v = false
			}

			s := models.Setting{Key: k, Value: strconv.FormatBool(v), Type: "bool", Category: "feature"}
			if err := tx.Where(models.Setting{Key: k}).Assign(s).FirstOrCreate(&s).Error; err != nil {
				return err // Rollback on error
			}
		}
		return nil
	}); err != nil {
		log.Printf("[ERROR] Failed to update feature flags: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update feature flags"})
		return
	}

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