package services

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries"
	"github.com/Fimeg/RedFlag/aggregator-server/internal/models"
	"github.com/google/uuid"
)

type SecuritySettingsService struct {
	settingsQueries *queries.SecuritySettingsQueries
	signingService  *SigningService
	encryptionKey   []byte
}

// NewSecuritySettingsService creates a new security settings service
func NewSecuritySettingsService(settingsQueries *queries.SecuritySettingsQueries, signingService *SigningService) (*SecuritySettingsService, error) {
	// Get encryption key from environment or generate one
	keyStr := os.Getenv("REDFLAG_SETTINGS_ENCRYPTION_KEY")
	var key []byte
	var err error

	if keyStr != "" {
		key, err = base64.StdEncoding.DecodeString(keyStr)
		if err != nil {
			return nil, fmt.Errorf("invalid encryption key format: %w", err)
		}
	} else {
		// Generate a new key (in production, this should be persisted)
		key = make([]byte, 32) // AES-256
		if _, err := rand.Read(key); err != nil {
			return nil, fmt.Errorf("failed to generate encryption key: %w", err)
		}
	}

	return &SecuritySettingsService{
		settingsQueries: settingsQueries,
		signingService:  signingService,
		encryptionKey:   key,
	}, nil
}

// GetSetting retrieves a security setting with proper priority resolution
func (s *SecuritySettingsService) GetSetting(category, key string) (interface{}, error) {
	// Priority 1: Environment variables
	if envValue := s.getEnvironmentValue(category, key); envValue != nil {
		return envValue, nil
	}

	// Priority 2: Config file values (this would be implemented based on your config structure)
	if configValue := s.getConfigValue(category, key); configValue != nil {
		return configValue, nil
	}

	// Priority 3: Database settings
	if dbSetting, err := s.settingsQueries.GetSetting(category, key); err == nil && dbSetting != nil {
		var value interface{}
		if dbSetting.IsEncrypted {
			decrypted, err := s.decrypt(dbSetting.Value)
			if err != nil {
				return nil, fmt.Errorf("failed to decrypt setting: %w", err)
			}
			if err := json.Unmarshal([]byte(decrypted), &value); err != nil {
				return nil, fmt.Errorf("failed to unmarshal decrypted setting: %w", err)
			}
		} else {
			if err := json.Unmarshal([]byte(dbSetting.Value), &value); err != nil {
				return nil, fmt.Errorf("failed to unmarshal setting: %w", err)
			}
		}
		return value, nil
	}

	// Priority 4: Hardcoded defaults
	if defaultValue := s.getDefaultValue(category, key); defaultValue != nil {
		return defaultValue, nil
	}

	return nil, fmt.Errorf("setting not found: %s.%s", category, key)
}

// SetSetting updates a security setting with validation and audit logging
func (s *SecuritySettingsService) SetSetting(category, key string, value interface{}, userID uuid.UUID, reason string) error {
	// Validate the setting
	if err := s.ValidateSetting(category, key, value); err != nil {
		return fmt.Errorf("validation failed: %w", err)
	}

	// Check if setting is sensitive and should be encrypted
	isEncrypted := s.isSensitiveSetting(category, key)

	// Check if setting exists
	existing, err := s.settingsQueries.GetSetting(category, key)
	if err != nil {
		return fmt.Errorf("failed to check existing setting: %w", err)
	}

	var oldValue *string
	var settingID uuid.UUID

	if existing != nil {
		// Update existing setting
		updated, oldVal, err := s.settingsQueries.UpdateSetting(category, key, value, &userID)
		if err != nil {
			return fmt.Errorf("failed to update setting: %w", err)
		}
		oldValue = oldVal
		settingID = updated.ID
	} else {
		// Create new setting
		created, err := s.settingsQueries.CreateSetting(category, key, value, isEncrypted, &userID)
		if err != nil {
			return fmt.Errorf("failed to create setting: %w", err)
		}
		settingID = created.ID
	}

	// Create audit log
	valueJSON, _ := json.Marshal(value)
	if err := s.settingsQueries.CreateAuditLog(
		settingID,
		userID,
		"update",
		stringOrNil(oldValue),
		string(valueJSON),
		reason,
	); err != nil {
		// Log error but don't fail the operation
		log.Printf("[WARNING] [server] [security] audit_log_create_failed error=%v", err)
	}

	return nil
}

// GetAllSettings retrieves all security settings organized by category
func (s *SecuritySettingsService) GetAllSettings() (map[string]map[string]interface{}, error) {
	// Get all default values first
	result := s.getDefaultSettings()

	// Override with database settings
	dbSettings, err := s.settingsQueries.GetAllSettings()
	if err != nil {
		return nil, fmt.Errorf("failed to get database settings: %w", err)
	}

	for _, setting := range dbSettings {
		var value interface{}
		if setting.IsEncrypted {
			decrypted, err := s.decrypt(setting.Value)
			if err != nil {
				return nil, fmt.Errorf("failed to decrypt setting %s.%s: %w", setting.Category, setting.Key, err)
			}
			if err := json.Unmarshal([]byte(decrypted), &value); err != nil {
				return nil, fmt.Errorf("failed to unmarshal decrypted setting %s.%s: %w", setting.Category, setting.Key, err)
			}
		} else {
			if err := json.Unmarshal([]byte(setting.Value), &value); err != nil {
				return nil, fmt.Errorf("failed to unmarshal setting %s.%s: %w", setting.Category, setting.Key, err)
			}
		}

		if result[setting.Category] == nil {
			result[setting.Category] = make(map[string]interface{})
		}
		result[setting.Category][setting.Key] = value
	}

	// Override with config file settings
	for category, settings := range result {
		for key := range settings {
			if configValue := s.getConfigValue(category, key); configValue != nil {
				result[category][key] = configValue
			}
		}
	}

	// Override with environment variables
	for category, settings := range result {
		for key := range settings {
			if envValue := s.getEnvironmentValue(category, key); envValue != nil {
				result[category][key] = envValue
			}
		}
	}

	return result, nil
}

// GetSettingsByCategory retrieves all settings for a specific category
func (s *SecuritySettingsService) GetSettingsByCategory(category string) (map[string]interface{}, error) {
	allSettings, err := s.GetAllSettings()
	if err != nil {
		return nil, err
	}

	if categorySettings, exists := allSettings[category]; exists {
		return categorySettings, nil
	}

	return nil, fmt.Errorf("category not found: %s", category)
}

// ValidateSetting validates a security setting value
func (s *SecuritySettingsService) ValidateSetting(category, key string, value interface{}) error {
	switch fmt.Sprintf("%s.%s", category, key) {
	case "nonce_validation.timeout_seconds":
		if timeout, ok := value.(float64); ok {
			if timeout < 60 || timeout > 3600 {
				return fmt.Errorf("nonce timeout must be between 60 and 3600 seconds")
			}
		} else {
			return fmt.Errorf("nonce timeout must be a number")
		}

	case "command_signing.enforcement_mode", "update_signing.enforcement_mode", "machine_binding.enforcement_mode":
		if mode, ok := value.(string); ok {
			validModes := []string{"strict", "warning", "disabled"}
			valid := false
			for _, m := range validModes {
				if mode == m {
					valid = true
					break
				}
			}
			if !valid {
				return fmt.Errorf("enforcement mode must be one of: strict, warning, disabled")
			}
		} else {
			return fmt.Errorf("enforcement mode must be a string")
		}

	case "signature_verification.log_retention_days":
		if days, ok := value.(float64); ok {
			if days < 1 || days > 365 {
				return fmt.Errorf("log retention must be between 1 and 365 days")
			}
		} else {
			return fmt.Errorf("log retention must be a number")
		}

	case "command_signing.algorithm", "update_signing.algorithm":
		if algo, ok := value.(string); ok {
			if algo != "ed25519" {
				return fmt.Errorf("only ed25519 algorithm is currently supported")
			}
		} else {
			return fmt.Errorf("algorithm must be a string")
		}
	}

	return nil
}

// InitializeDefaultSettings creates default settings in the database if they don't exist
func (s *SecuritySettingsService) InitializeDefaultSettings() error {
	defaults := s.getDefaultSettings()

	for category, settings := range defaults {
		for key, value := range settings {
			existing, err := s.settingsQueries.GetSetting(category, key)
			if err != nil {
				return fmt.Errorf("failed to check existing setting %s.%s: %w", category, key, err)
			}

			if existing == nil {
				isEncrypted := s.isSensitiveSetting(category, key)
				_, err := s.settingsQueries.CreateSetting(category, key, value, isEncrypted, nil)
				if err != nil {
					return fmt.Errorf("failed to create default setting %s.%s: %w", category, key, err)
				}
			}
		}
	}

	return nil
}

// Helper methods

func (s *SecuritySettingsService) getDefaultSettings() map[string]map[string]interface{} {
	return map[string]map[string]interface{}{
		"command_signing": {
			"enabled":          true,
			"enforcement_mode": "strict",
			"algorithm":        "ed25519",
		},
		"update_signing": {
			"enabled":          true,
			"enforcement_mode": "strict",
			"allow_unsigned":   false,
		},
		"nonce_validation": {
			"timeout_seconds":     600,
			"reject_expired":      true,
			"log_expired_attempts": true,
		},
		"machine_binding": {
			"enabled":         true,
			"enforcement_mode": "strict",
			"strict_action":   "reject",
		},
		"signature_verification": {
			"log_level":          "warn",
			"log_retention_days": 30,
			"log_failures":       true,
			"alert_on_failure":   true,
		},
	}
}

func (s *SecuritySettingsService) getDefaultValue(category, key string) interface{} {
	defaults := s.getDefaultSettings()
	if cat, exists := defaults[category]; exists {
		if value, exists := cat[key]; exists {
			return value
		}
	}
	return nil
}

func (s *SecuritySettingsService) getEnvironmentValue(category, key string) interface{} {
	envKey := fmt.Sprintf("REDFLAG_%s_%s", strings.ToUpper(category), strings.ToUpper(key))
	envValue := os.Getenv(envKey)
	if envValue == "" {
		return nil
	}

	// Try to parse as boolean
	if strings.ToLower(envValue) == "true" {
		return true
	}
	if strings.ToLower(envValue) == "false" {
		return false
	}

	// Try to parse as number
	if num, err := strconv.ParseFloat(envValue, 64); err == nil {
		return num
	}

	// Return as string
	return envValue
}

func (s *SecuritySettingsService) getConfigValue(category, key string) interface{} {
	// This would be implemented based on your config structure
	// For now, returning nil to prioritize env vars and database
	return nil
}

func (s *SecuritySettingsService) isSensitiveSetting(category, key string) bool {
	// Define which settings are sensitive and should be encrypted
	sensitive := map[string]bool{
		"command_signing.private_key":  true,
		"update_signing.private_key":   true,
		"machine_binding.server_key":   true,
		"encryption.master_key":        true,
	}

	settingKey := fmt.Sprintf("%s.%s", category, key)
	return sensitive[settingKey]
}

func (s *SecuritySettingsService) encrypt(value string) (string, error) {
	block, err := aes.NewCipher(s.encryptionKey)
	if err != nil {
		return "", err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}

	nonce := make([]byte, gcm.NonceSize())
	if _, err := rand.Read(nonce); err != nil {
		return "", err
	}

	ciphertext := gcm.Seal(nonce, nonce, []byte(value), nil)
	return base64.StdEncoding.EncodeToString(ciphertext), nil
}

func (s *SecuritySettingsService) decrypt(encryptedValue string) (string, error) {
	data, err := base64.StdEncoding.DecodeString(encryptedValue)
	if err != nil {
		return "", err
	}

	block, err := aes.NewCipher(s.encryptionKey)
	if err != nil {
		return "", err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}

	nonceSize := gcm.NonceSize()
	if len(data) < nonceSize {
		return "", fmt.Errorf("ciphertext too short")
	}

	nonce, ciphertext := data[:nonceSize], data[nonceSize:]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return "", err
	}

	return string(plaintext), nil
}

func stringOrNil(s *string) string {
	if s == nil {
		return ""
	}
	return *s
}

// GetNonceTimeout returns the current nonce validation timeout in seconds
func (s *SecuritySettingsService) GetNonceTimeout() (int, error) {
	value, err := s.GetSetting("nonce_validation", "timeout_seconds")
	if err != nil {
		return 600, err // Return default on error
	}

	if timeout, ok := value.(float64); ok {
		return int(timeout), nil
	}

	return 600, nil // Return default if type is wrong
}

// GetEnforcementMode returns the enforcement mode for a given category
func (s *SecuritySettingsService) GetEnforcementMode(category string) (string, error) {
	value, err := s.GetSetting(category, "enforcement_mode")
	if err != nil {
		return "strict", err // Return default on error
	}

	if mode, ok := value.(string); ok {
		return mode, nil
	}

	return "strict", nil // Return default if type is wrong
}

// IsSignatureVerificationEnabled returns whether signature verification is enabled for a category
func (s *SecuritySettingsService) IsSignatureVerificationEnabled(category string) (bool, error) {
	value, err := s.GetSetting(category, "enabled")
	if err != nil {
		return true, err // Return default on error
	}

	if enabled, ok := value.(bool); ok {
		return enabled, nil
	}

	return true, nil // Return default if type is wrong
}

// GetAuditTrail returns recent security settings audit entries (F-E1-7 fix)
func (s *SecuritySettingsService) GetAuditTrail(limit int) ([]models.SecuritySettingAudit, error) {
	return s.settingsQueries.GetAllAuditLogs(limit)
}