package tlp

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"git.gostacks.org/iwasforcedtobehere/WiseTLP/autotlp/internal/system"
	"git.gostacks.org/iwasforcedtobehere/WiseTLP/autotlp/pkg/types"
	"git.gostacks.org/iwasforcedtobehere/WiseTLP/autotlp/pkg/utils"
)

type Manager struct {
	logger *utils.Logger
}

func NewManager(logger *utils.Logger) *Manager {
	return &Manager{
		logger: logger.WithComponent("tlp"),
	}
}

func (m *Manager) GetStatus(ctx context.Context) (*types.TLPStatus, error) {
	status := &types.TLPStatus{
		CurrentConfig: make(map[string]string),
	}

	if utils.CommandExists("tlp") {
		status.Installed = true

		if version, err := utils.RunCommand("tlp", "--version"); err == nil {
			parts := strings.Fields(version)
			if len(parts) >= 2 {
				status.Version = parts[1]
			}
		}

		if output, err := utils.RunCommand("systemctl", "is-active", "tlp"); err == nil {
			status.Active = strings.TrimSpace(output) == "active"
		}
	}

	configPaths := []string{
		"/etc/tlp.conf",
		"/etc/default/tlp",
	}

	for _, path := range configPaths {
		if utils.FileExists(path) {
			status.ConfigPath = path
			status.ConfigExists = true

			if info, err := os.Stat(path); err == nil {
				modTime := info.ModTime()
				status.LastModified = &modTime
			}

			if config, err := m.readConfig(path); err == nil {
				status.CurrentConfig = config
			}
			break
		}
	}

	return status, nil
}

func (m *Manager) Install(ctx context.Context, sysInfo *system.Info) error {
	m.logger.Info("Installing TLP", "distro", sysInfo.Distribution, "package_manager", sysInfo.PackageManager)

	if utils.CommandExists("tlp") {
		return fmt.Errorf("TLP is already installed")
	}

	if !utils.IsRoot() {
		return fmt.Errorf("root privileges required for TLP installation")
	}

	var installCmd []string
	var updateCmd []string

	switch sysInfo.PackageManager {
	case "apt":
		updateCmd = []string{"apt", "update"}
		installCmd = []string{"apt", "install", "-y", "tlp", "tlp-rdw"}
	case "dnf":
		installCmd = []string{"dnf", "install", "-y", "tlp", "tlp-rdw"}
	case "yum":
		installCmd = []string{"yum", "install", "-y", "tlp", "tlp-rdw"}
	case "zypper":
		installCmd = []string{"zypper", "install", "-y", "tlp", "tlp-rdw"}
	case "pacman":
		updateCmd = []string{"pacman", "-Sy"}
		installCmd = []string{"pacman", "-S", "--noconfirm", "tlp", "tlp-rdw"}
	case "apk":
		installCmd = []string{"apk", "add", "tlp"}
	default:
		return fmt.Errorf("unsupported package manager: %s", sysInfo.PackageManager)
	}

	if len(updateCmd) > 0 {
		m.logger.Info("Updating package lists")
		if _, err := utils.RunCommand(updateCmd[0], updateCmd[1:]...); err != nil {
			m.logger.Warn("Failed to update package lists", "error", err)
		}
	}

	m.logger.Info("Installing TLP packages", "command", strings.Join(installCmd, " "))
	if _, err := utils.RunCommand(installCmd[0], installCmd[1:]...); err != nil {
		return fmt.Errorf("failed to install TLP: %w", err)
	}

	m.logger.Info("Enabling TLP service")
	if _, err := utils.RunCommand("systemctl", "enable", "tlp"); err != nil {
		m.logger.Warn("Failed to enable TLP service", "error", err)
	}

	if _, err := utils.RunCommand("systemctl", "start", "tlp"); err != nil {
		m.logger.Warn("Failed to start TLP service", "error", err)
	}

	if utils.CommandExists("systemctl") {
		if _, err := utils.RunCommand("systemctl", "mask", "power-profiles-daemon"); err != nil {
			m.logger.Debug("power-profiles-daemon not found or already masked")
		}
	}

	m.logger.Info("TLP installation completed successfully")
	return nil
}

func (m *Manager) ApplyConfig(ctx context.Context, config *types.TLPConfiguration) error {
	m.logger.Info("Applying TLP configuration")

	if err := m.ValidateConfig(config); err != nil {
		return fmt.Errorf("configuration validation failed: %w", err)
	}

	configPath := "/etc/tlp.conf"
	if !utils.FileExists(configPath) {
		configPath = "/etc/default/tlp"
	}

	if utils.FileExists(configPath) {
		backupPath := fmt.Sprintf("%s.backup.%d", configPath, time.Now().Unix())
		if err := m.backupConfig(configPath, backupPath); err != nil {
			m.logger.Warn("Failed to backup existing configuration", "error", err)
		} else {
			m.logger.Info("Backed up existing configuration", "backup", backupPath)
		}
	}

	if err := m.writeConfig(configPath, config); err != nil {
		return fmt.Errorf("failed to write configuration: %w", err)
	}

	m.logger.Info("Reloading TLP configuration")
	if _, err := utils.RunCommand("tlp", "start"); err != nil {
		m.logger.Warn("Failed to reload TLP configuration", "error", err)
	}

	m.logger.Info("TLP configuration applied successfully")
	return nil
}

func (m *Manager) ValidateConfig(config *types.TLPConfiguration) error {
	if config == nil {
		return fmt.Errorf("configuration is nil")
	}

	if len(config.Settings) == 0 {
		return fmt.Errorf("configuration has no settings")
	}

	for key, value := range config.Settings {
		if key == "" {
			return fmt.Errorf("empty setting key found")
		}

		switch key {
		case "TLP_ENABLE":
			if value != "1" && value != "0" {
				return fmt.Errorf("TLP_ENABLE must be 0 or 1, got: %s", value)
			}
		case "CPU_SCALING_GOVERNOR_ON_AC", "CPU_SCALING_GOVERNOR_ON_BAT":
			validGovernors := []string{"performance", "powersave", "ondemand", "conservative", "schedutil"}
			if !utils.Contains(validGovernors, value) {
				return fmt.Errorf("invalid CPU governor: %s", value)
			}
		}
	}

	return nil
}

func (m *Manager) readConfig(configPath string) (map[string]string, error) {
	config := make(map[string]string)

	lines, err := utils.ReadFileLines(configPath)
	if err != nil {
		return nil, err
	}

	for _, line := range lines {
		line = strings.TrimSpace(line)

		if line == "" || strings.HasPrefix(line, "#") {
			continue
		}

		if key, value, ok := utils.ParseKeyValue(line); ok {
			config[key] = value
		}
	}

	return config, nil
}

func (m *Manager) writeConfig(configPath string, config *types.TLPConfiguration) error {
	if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil {
		return fmt.Errorf("failed to create config directory: %w", err)
	}

	file, err := os.OpenFile(configPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return fmt.Errorf("failed to open config file: %w", err)
	}
	defer file.Close()

	fmt.Fprintf(file, "# TLP Configuration File\n")
	fmt.Fprintf(file, "# Generated by WiseTLP on %s\n", time.Now().Format("2006-01-02 15:04:05"))
	fmt.Fprintf(file, "# %s\n\n", config.Description)

	for key, value := range config.Settings {
		if rationale, exists := config.Rationale[key]; exists {
			fmt.Fprintf(file, "# %s\n", rationale)
		}
		fmt.Fprintf(file, "%s=%s\n\n", key, value)
	}

	if len(config.Warnings) > 0 {
		fmt.Fprintf(file, "# WARNINGS:\n")
		for _, warning := range config.Warnings {
			fmt.Fprintf(file, "# - %s\n", warning)
		}
	}

	return nil
}

func (m *Manager) backupConfig(configPath, backupPath string) error {
	input, err := os.ReadFile(configPath)
	if err != nil {
		return err
	}

	return os.WriteFile(backupPath, input, 0644)
}

type Configuration struct {
	*types.TLPConfiguration
}

func (c *Configuration) Present() error {
	fmt.Println("\n" + strings.Repeat("=", 60))
	fmt.Println("GENERATED TLP CONFIGURATION")
	fmt.Println(strings.Repeat("=", 60))

	fmt.Printf("\nDescription: %s\n", c.Description)

	if len(c.Warnings) > 0 {
		fmt.Println("\n⚠️  WARNINGS:")
		for _, warning := range c.Warnings {
			fmt.Printf("   - %s\n", warning)
		}
	}

	fmt.Println("\nConfiguration Settings:")
	fmt.Println(strings.Repeat("-", 40))

	// Group settings by category for better presentation
	categories := map[string][]string{
		"General": {"TLP_ENABLE", "TLP_WARN_LEVEL", "TLP_DEBUG"},
		"CPU": {"CPU_SCALING_GOVERNOR_ON_AC", "CPU_SCALING_GOVERNOR_ON_BAT",
			"CPU_SCALING_MIN_FREQ_ON_AC", "CPU_SCALING_MAX_FREQ_ON_AC",
			"CPU_SCALING_MIN_FREQ_ON_BAT", "CPU_SCALING_MAX_FREQ_ON_BAT"},
		"Platform": {"PLATFORM_PROFILE_ON_AC", "PLATFORM_PROFILE_ON_BAT"},
		"Disk": {"DISK_APM_LEVEL_ON_AC", "DISK_APM_LEVEL_ON_BAT",
			"DISK_SPINDOWN_TIMEOUT_ON_AC", "DISK_SPINDOWN_TIMEOUT_ON_BAT"},
		"Graphics": {"RADEON_DPM_STATE_ON_AC", "RADEON_DPM_STATE_ON_BAT"},
		"Network":  {"WIFI_PWR_ON_AC", "WIFI_PWR_ON_BAT"},
		"USB":      {"USB_AUTOSUSPEND", "USB_BLACKLIST"},
	}

	for category, keys := range categories {
		hasSettings := false
		var categorySettings []string

		for _, key := range keys {
			if value, exists := c.Settings[key]; exists {
				if !hasSettings {
					categorySettings = append(categorySettings, fmt.Sprintf("\n%s:", category))
					hasSettings = true
				}

				rationale := ""
				if r, exists := c.Rationale[key]; exists {
					rationale = fmt.Sprintf(" (%s)", r)
				}

				categorySettings = append(categorySettings, fmt.Sprintf("  %s = %s%s", key, value, rationale))
			}
		}

		if hasSettings {
			for _, setting := range categorySettings {
				fmt.Println(setting)
			}
		}
	}

	// Show any remaining settings not in categories
	fmt.Println("\nOther Settings:")
	for key, value := range c.Settings {
		found := false
		for _, keys := range categories {
			if utils.Contains(keys, key) {
				found = true
				break
			}
		}

		if !found {
			rationale := ""
			if r, exists := c.Rationale[key]; exists {
				rationale = fmt.Sprintf(" (%s)", r)
			}
			fmt.Printf("  %s = %s%s\n", key, value, rationale)
		}
	}

	fmt.Println(strings.Repeat("=", 60))

	// Get user approval
	fmt.Print("\nDo you want to apply this configuration? (y/N): ")
	var response string
	fmt.Scanln(&response)

	response = strings.ToLower(strings.TrimSpace(response))
	if response != "y" && response != "yes" {
		return fmt.Errorf("configuration rejected by user")
	}

	return nil
}
