package wireguard

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"vpn-client/internal/config"
	"vpn-client/internal/logger"
)

// Connect подключается к WireGuard серверу
func Connect(configName string, logsDir string) error {
	// Загружаем конфигурации
	configs, err := config.LoadConfigs()
	if err != nil {
		return fmt.Errorf("ошибка загрузки конфигураций: %w", err)
	}

	// Ищем конфиг
	var wgConfig *config.WireGuardConfig
	for i := range configs.WireGuard {
		if configs.WireGuard[i].Name == configName {
			wgConfig = &configs.WireGuard[i]
			break
		}
	}

	if wgConfig == nil {
		return fmt.Errorf("конфиг '%s' не найден", configName)
	}

	// Создаем файл конфига
	interfaceName := strings.ReplaceAll(configName, " ", "_")
	configPath := filepath.Join(config.ConfigDir, interfaceName+".conf")

	if err := os.WriteFile(configPath, []byte(wgConfig.Config), 0600); err != nil {
		return fmt.Errorf("ошибка создания файла конфига: %w", err)
	}

	fmt.Printf("Подключение к WireGuard '%s'...\n", configName)

	logFile := logger.GetLogPath(logsDir, "wireguard")
	logger.LogMessage(logFile, fmt.Sprintf("Начало подключения к '%s'", configName))

	var cmd *exec.Cmd

	if runtime.GOOS == "windows" {
		// Для Windows используем wireguard.exe
		wgPaths := []string{
			`C:\Program Files\WireGuard\wireguard.exe`,
			`C:\Program Files (x86)\WireGuard\wireguard.exe`,
		}

		var wgExe string
		for _, path := range wgPaths {
			if _, err := os.Stat(path); err == nil {
				wgExe = path
				break
			}
		}

		if wgExe == "" {
			return fmt.Errorf("WireGuard не найден. Установите WireGuard для Windows\nСкачать: https://www.wireguard.com/install/")
		}

		// Импортируем туннель
		cmd = exec.Command(wgExe, "/installtunnelservice", configPath)
		output, err := cmd.CombinedOutput()

		if err != nil {
			logger.LogMessage(logFile, fmt.Sprintf("Ошибка подключения к '%s': %s", configName, string(output)))
			return fmt.Errorf("ошибка подключения: %s", string(output))
		}

		// Сохраняем состояние
		state := &config.ConnectionState{
			Connected:  true,
			ConfigName: configName,
			ConfigType: "wireguard",
			StartTime:  time.Now().Format(time.RFC3339),
			Interface:  interfaceName,
			ProcessPID: 0, // WireGuard на Windows работает как служба
			LogFile:    "",
		}

		if err := config.SaveState(state); err != nil {
			return fmt.Errorf("ошибка сохранения состояния: %w", err)
		}

		logger.LogMessage(logFile, fmt.Sprintf("Успешно подключено к '%s'", configName))
		fmt.Printf("✓ Подключено к '%s'\n", configName)

	} else {
		// Для Linux/Mac используем wg-quick
		cmd = exec.Command("sudo", "wg-quick", "up", configPath)
		output, err := cmd.CombinedOutput()

		if err != nil {
			logger.LogMessage(logFile, fmt.Sprintf("Ошибка подключения к '%s': %s", configName, string(output)))
			return fmt.Errorf("ошибка подключения: %s", string(output))
		}

		// Сохраняем состояние
		state := &config.ConnectionState{
			Connected:  true,
			ConfigName: configName,
			ConfigType: "wireguard",
			StartTime:  time.Now().Format(time.RFC3339),
			Interface:  interfaceName,
			ProcessPID: 0,
			LogFile:    "",
		}

		if err := config.SaveState(state); err != nil {
			return fmt.Errorf("ошибка сохранения состояния: %w", err)
		}

		logger.LogMessage(logFile, fmt.Sprintf("Успешно подключено к '%s'", configName))
		fmt.Printf("✓ Подключено к '%s'\n", configName)
	}

	return nil
}

// Disconnect отключается от WireGuard
func Disconnect(interfaceName, logsDir string) error {
	logFile := logger.GetLogPath(logsDir, "wireguard")
	logger.LogMessage(logFile, fmt.Sprintf("Начало отключения от '%s'", interfaceName))

	var cmd *exec.Cmd

	if runtime.GOOS == "windows" {
		wgPaths := []string{
			`C:\Program Files\WireGuard\wireguard.exe`,
			`C:\Program Files (x86)\WireGuard\wireguard.exe`,
		}

		var wgExe string
		for _, path := range wgPaths {
			if _, err := os.Stat(path); err == nil {
				wgExe = path
				break
			}
		}

		if wgExe != "" {
			cmd = exec.Command(wgExe, "/uninstalltunnelservice", interfaceName)
			cmd.Run()
		}
	} else {
		configPath := filepath.Join(config.ConfigDir, interfaceName+".conf")
		cmd = exec.Command("sudo", "wg-quick", "down", configPath)
		cmd.Run()
	}

	logger.LogMessage(logFile, fmt.Sprintf("Отключено от '%s'", interfaceName))
	return nil
}

// GetStats получает статистику WireGuard
func GetStats(interfaceName string) (map[string]string, error) {
	stats := map[string]string{
		"rx": "N/A",
		"tx": "N/A",
	}

	if runtime.GOOS == "windows" {
		// Для Windows пытаемся получить статистику через wg.exe
		wgPaths := []string{
			`C:\Program Files\WireGuard\wg.exe`,
			`C:\Program Files (x86)\WireGuard\wg.exe`,
		}

		var wgExe string
		for _, path := range wgPaths {
			if _, err := os.Stat(path); err == nil {
				wgExe = path
				break
			}
		}

		if wgExe != "" {
			cmd := exec.Command(wgExe, "show", interfaceName, "transfer")
			output, err := cmd.Output()
			if err == nil && len(output) > 0 {
				lines := strings.Split(strings.TrimSpace(string(output)), "\n")
				for _, line := range lines {
					parts := strings.Fields(line)
					if len(parts) >= 3 {
						rx := parseBytes(parts[1])
						tx := parseBytes(parts[2])
						stats["rx"] = formatBytes(rx)
						stats["tx"] = formatBytes(tx)
						break
					}
				}
			}
		}
	} else {
		// Для Linux/Mac
		cmd := exec.Command("wg", "show", interfaceName, "transfer")
		output, err := cmd.Output()
		if err == nil && len(output) > 0 {
			lines := strings.Split(strings.TrimSpace(string(output)), "\n")
			if len(lines) > 0 {
				parts := strings.Fields(lines[0])
				if len(parts) >= 2 {
					rx := parseBytes(parts[0])
					tx := parseBytes(parts[1])
					stats["rx"] = formatBytes(rx)
					stats["tx"] = formatBytes(tx)
				}
			}
		}
	}

	return stats, nil
}

// AddConfig добавляет WireGuard конфигурацию
func AddConfig(name, configText string) error {
	configs, err := config.LoadConfigs()
	if err != nil {
		return fmt.Errorf("ошибка загрузки конфигураций: %w", err)
	}

	configs.WireGuard = append(configs.WireGuard, config.WireGuardConfig{
		Name:   name,
		Config: configText,
	})

	if err := config.SaveConfigs(configs); err != nil {
		return fmt.Errorf("ошибка сохранения конфигураций: %w", err)
	}

	return nil
}

// AddConfigFromFile добавляет WireGuard конфигурацию из файла
func AddConfigFromFile(name, filePath string) error {
	data, err := os.ReadFile(filePath)
	if err != nil {
		return fmt.Errorf("ошибка чтения файла: %w", err)
	}

	return AddConfig(name, string(data))
}

// DeleteConfig удаляет WireGuard конфигурацию
func DeleteConfig(name string) error {
	configs, err := config.LoadConfigs()
	if err != nil {
		return fmt.Errorf("ошибка загрузки конфигураций: %w", err)
	}

	var filtered []config.WireGuardConfig
	found := false
	for _, cfg := range configs.WireGuard {
		if cfg.Name != name {
			filtered = append(filtered, cfg)
		} else {
			found = true
		}
	}

	if !found {
		return fmt.Errorf("конфиг '%s' не найден", name)
	}

	configs.WireGuard = filtered

	if err := config.SaveConfigs(configs); err != nil {
		return fmt.Errorf("ошибка сохранения конфигураций: %w", err)
	}

	return nil
}

func parseBytes(s string) int64 {
	var val int64
	fmt.Sscanf(s, "%d", &val)
	return val
}

func formatBytes(bytes int64) string {
	const unit = 1024
	if bytes < unit {
		return fmt.Sprintf("%d B", bytes)
	}
	div, exp := int64(unit), 0
	for n := bytes / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.2f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
}
