package services

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"time"

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

// ConfigService manages agent configuration generation and validation
type ConfigService struct {
	db               *sqlx.DB
	config           *config.Config
	logger           *log.Logger
	subsystemQueries *queries.SubsystemQueries
}

// NewConfigService creates a new configuration service
func NewConfigService(db *sqlx.DB, cfg *config.Config, logger *log.Logger) *ConfigService {
	return &ConfigService{
		db:               db,
		config:           cfg,
		logger:           logger,
		subsystemQueries: queries.NewSubsystemQueries(db),
	}
}

// getDB returns the database connection (for access to refresh token queries)
func (s *ConfigService) getDB() *sqlx.DB {
	return s.db
}

// AgentConfigData represents agent configuration structure
type AgentConfigData struct {
	AgentID        string                 `json:"agent_id"`
	Version        string                 `json:"version"`
	Platform       string                 `json:"platform"`
	ServerURL      string                 `json:"server_url"`
	LogLevel       string                 `json:"log_level"`
	Intervals      map[string]int         `json:"intervals"`
	Subsystems     map[string]interface{} `json:"subsystems"`
	MaxRetries     int                    `json:"max_retries"`
	TimeoutSeconds int                    `json:"timeout_seconds"`
	MachineID      string                 `json:"machine_id"`
	AgentType      string                 `json:"agent_type"`
	ConfigPath     string                 `json:"config_path"`
	StatePath      string                 `json:"state_path"`
	LogPath        string                 `json:"log_path"`
	ServiceName    string                 `json:"service_name"`
	CreatedAt      time.Time              `json:"created_at"`
	UpdatedAt      time.Time              `json:"updated_at"`
}

// GenerateNewConfig creates configuration for a new agent
func (s *ConfigService) GenerateNewConfig(agentCfg *AgentConfig) ([]byte, error) {
	// Base configuration
	serverURL := fmt.Sprintf("http://%s:%d", s.config.Server.Host, s.config.Server.Port)
	if s.config.Server.PublicURL != "" {
		serverURL = s.config.Server.PublicURL
	}

	// Get subsystems from database (not hardcoded!)
	agentID := uuid.MustParse(agentCfg.AgentID)
	subsystems, err := s.subsystemQueries.GetSubsystems(agentID)
	if err != nil || len(subsystems) == 0 {
		// If not found, create defaults
		if err := s.subsystemQueries.CreateDefaultSubsystems(agentID); err != nil {
			return nil, fmt.Errorf("failed to create default subsystems: %w", err)
		}
		subsystems, _ = s.subsystemQueries.GetSubsystems(agentID)
	}

	// Convert to map format for JSON
	subsystemMap := make(map[string]interface{})
	for _, sub := range subsystems {
		subsystemMap[sub.Subsystem] = map[string]interface{}{
			"enabled":  sub.Enabled,
			"auto_run": sub.AutoRun,
			"interval": sub.IntervalMinutes,
		}
	}

	cfg := &AgentConfigData{
		AgentID:   agentCfg.AgentID,
		Version:   agentCfg.Version,
		Platform:  agentCfg.Platform,
		ServerURL: serverURL,
		LogLevel:  "info",
		Intervals: map[string]int{
			"metrics":  300,  // 5 minutes
			"updates":  3600, // 1 hour
			"commands": 30,   // 30 seconds
		},
		Subsystems:     subsystemMap, // ← USE DATABASE VALUES!
		MaxRetries:     3,
		TimeoutSeconds: 30,
		MachineID:      agentCfg.MachineID,
		AgentType:      agentCfg.AgentType,
		CreatedAt:      time.Now(),
		UpdatedAt:      time.Now(),
	}

	// Platform-specific customizations
	s.applyPlatformDefaults(cfg)

	// Validate configuration
	if err := s.validateConfig(cfg); err != nil {
		return nil, fmt.Errorf("validation failed: %w", err)
	}

	// Marshal to JSON
	configJSON, err := json.MarshalIndent(cfg, "", "  ")
	if err != nil {
		return nil, fmt.Errorf("marshal failed: %w", err)
	}

	return configJSON, nil
}

// LoadExistingConfig retrieves and updates existing agent configuration
func (s *ConfigService) LoadExistingConfig(agentID string) ([]byte, error) {
	// Get existing agent from database
	var agent models.Agent
	query := `SELECT * FROM agents WHERE id = $1`
	err := s.db.Get(&agent, query, agentID)
	if err != nil {
		return nil, fmt.Errorf("agent not found: %w", err)
	}

	// For existing registered agents, generate proper config with auth tokens
	s.logger.Printf("[DEBUG] Generating config for existing agent %s", agentID)
	machineID := ""
	if agent.MachineID != nil {
		machineID = *agent.MachineID
	}

	agentCfg := &AgentConfig{
		AgentID:      agentID,
		Version:      agent.CurrentVersion,
		Platform:     agent.OSType,
		Architecture: agent.OSArchitecture,
		MachineID:    machineID,
		AgentType:    "", // Could be stored in metadata
		Hostname:     agent.Hostname,
	}

	return s.GenerateNewConfig(agentCfg)
}

// applyPlatformDefaults applies platform-specific configuration
func (s *ConfigService) applyPlatformDefaults(cfg *AgentConfigData) {
	switch cfg.Platform {
	case "windows-amd64", "windows-arm64", "windows-386":
		// Windows-specific paths
		cfg.ConfigPath = "C:\\ProgramData\\RedFlag\\config.json"
		cfg.StatePath = "C:\\ProgramData\\RedFlag\\state\\"
		cfg.LogPath = "C:\\ProgramData\\RedFlag\\logs\\"
		cfg.ServiceName = "RedFlagAgent"

		// Windows-specific subsystems
		cfg.Subsystems["windows"] = map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 300}
		cfg.Subsystems["winget"] = map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 180}

	default:
		// Linux defaults
		cfg.ConfigPath = "/etc/redflag/config.json"
		cfg.StatePath = "/var/lib/redflag/"
		cfg.LogPath = "/var/log/redflag/"
		cfg.ServiceName = "redflag-agent"
	}
}

// validateConfig validates configuration
func (s *ConfigService) validateConfig(cfg *AgentConfigData) error {
	if cfg.AgentID == "" {
		return fmt.Errorf("agent_id is required")
	}
	if cfg.Version == "" {
		return fmt.Errorf("version is required")
	}
	if cfg.Platform == "" {
		return fmt.Errorf("platform is required")
	}
	if cfg.ServerURL == "" {
		return fmt.Errorf("server_url is required")
	}

	return nil
}

// SaveConfig saves agent configuration to database
func (s *ConfigService) SaveConfig(ctx context.Context, agentID uuid.UUID, configJSON []byte) error {
	query := `
		UPDATE agents SET config = $1, updated_at = $2
		WHERE id = $3
	`
	_, err := s.db.ExecContext(ctx, query, configJSON, time.Now(), agentID)
	return err
}
