package vless

import (
	"encoding/json"
	"fmt"
	"net"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

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

// XrayConfig представляет конфигурацию Xray
type XrayConfig struct {
	Log       LogConfig       `json:"log"`
	Inbounds  []InboundConfig `json:"inbounds"`
	Outbounds []OutboundConfig `json:"outbounds"`
	Stats     interface{}     `json:"stats"`
	Policy    PolicyConfig    `json:"policy"`
}

type LogConfig struct {
	Loglevel string `json:"loglevel"`
	Access   string `json:"access"`
	Error    string `json:"error"`
	DNSLog   bool   `json:"dnsLog"`
}

type InboundConfig struct {
	Port      int              `json:"port"`
	Protocol  string           `json:"protocol"`
	Settings  InboundSettings  `json:"settings"`
	Sniffing  SniffingConfig   `json:"sniffing"`
	Tag       string           `json:"tag"`
}

type InboundSettings struct {
	UDP  bool   `json:"udp"`
	Auth string `json:"auth"`
}

type SniffingConfig struct {
	Enabled      bool     `json:"enabled"`
	DestOverride []string `json:"destOverride"`
	MetadataOnly bool     `json:"metadataOnly"`
}

type OutboundConfig struct {
	Protocol       string          `json:"protocol"`
	Tag            string          `json:"tag"`
	Settings       OutboundSettings `json:"settings"`
	StreamSettings StreamSettings   `json:"streamSettings"`
}

type OutboundSettings struct {
	Vnext []VnextConfig `json:"vnext"`
}

type VnextConfig struct {
	Address string       `json:"address"`
	Port    int          `json:"port"`
	Users   []UserConfig `json:"users"`
}

type UserConfig struct {
	ID         string `json:"id"`
	Encryption string `json:"encryption"`
	Flow       string `json:"flow,omitempty"`
}

type StreamSettings struct {
	Network         string                 `json:"network"`
	Security        string                 `json:"security,omitempty"`
	TLSSettings     *TLSSettings           `json:"tlsSettings,omitempty"`
	RealitySettings *RealitySettings       `json:"realitySettings,omitempty"`
	WSSettings      *WSSettings            `json:"wsSettings,omitempty"`
	GRPCSettings    *GRPCSettings          `json:"grpcSettings,omitempty"`
	HTTPSettings    *HTTPSettings          `json:"httpSettings,omitempty"`
}

type TLSSettings struct {
	ServerName    string `json:"serverName"`
	AllowInsecure bool   `json:"allowInsecure"`
	Fingerprint   string `json:"fingerprint,omitempty"`
}

type RealitySettings struct {
	ServerName  string `json:"serverName"`
	Fingerprint string `json:"fingerprint"`
	Show        bool   `json:"show"`
	PublicKey   string `json:"publicKey,omitempty"`
	ShortID     string `json:"shortId,omitempty"`
	SpiderX     string `json:"spiderX,omitempty"`
}

type WSSettings struct {
	Path    string            `json:"path"`
	Headers map[string]string `json:"headers"`
}

type GRPCSettings struct {
	ServiceName string `json:"serviceName"`
	MultiMode   bool   `json:"multiMode"`
}

type HTTPSettings struct {
	Path []string `json:"path"`
	Host []string `json:"host"`
}

type PolicyConfig struct {
	Levels map[string]LevelPolicy `json:"levels"`
	System SystemPolicy           `json:"system"`
}

type LevelPolicy struct {
	StatsUserUplink   bool `json:"statsUserUplink"`
	StatsUserDownlink bool `json:"statsUserDownlink"`
}

type SystemPolicy struct {
	StatsInboundUplink    bool `json:"statsInboundUplink"`
	StatsInboundDownlink  bool `json:"statsInboundDownlink"`
	StatsOutboundUplink   bool `json:"statsOutboundUplink"`
	StatsOutboundDownlink bool `json:"statsOutboundDownlink"`
}

// ParseVLESSURL парсит VLESS URL и создает конфигурацию Xray
func ParseVLESSURL(vlessURL, logsDir string) (*XrayConfig, error) {
	// Убираем префикс vless://
	urlStr := strings.TrimPrefix(vlessURL, "vless://")

	// Разделяем на части
	var name string
	if idx := strings.Index(urlStr, "#"); idx != -1 {
		name = urlStr[idx+1:]
		urlStr = urlStr[:idx]
	}

	// Парсим параметры
	var paramsStr string
	var connection string
	if idx := strings.Index(urlStr, "?"); idx != -1 {
		connection = urlStr[:idx]
		paramsStr = urlStr[idx+1:]
	} else {
		connection = urlStr
	}

	// Парсим connection (uuid@server:port)
	parts := strings.Split(connection, "@")
	if len(parts) != 2 {
		return nil, fmt.Errorf("неверный формат VLESS URL")
	}
	uuid := parts[0]
	serverPort := parts[1]

	// Парсим server:port
	var server string
	var port int
	if strings.Contains(serverPort, "[") {
		// IPv6
		endIdx := strings.Index(serverPort, "]")
		server = serverPort[1:endIdx]
		portStr := strings.TrimPrefix(serverPort[endIdx+1:], ":")
		port, _ = strconv.Atoi(portStr)
	} else {
		lastColon := strings.LastIndex(serverPort, ":")
		server = serverPort[:lastColon]
		portStr := serverPort[lastColon+1:]
		port, _ = strconv.Atoi(portStr)
	}

	if port == 0 {
		port = 443
	}

	// Парсим параметры
	params := make(map[string]string)
	if paramsStr != "" {
		for _, param := range strings.Split(paramsStr, "&") {
			kv := strings.SplitN(param, "=", 2)
			if len(kv) == 2 {
				key, _ := url.QueryUnescape(kv[0])
				value, _ := url.QueryUnescape(kv[1])
				params[key] = value
			}
		}
	}

	// Создаем базовую конфигурацию
	cfg := &XrayConfig{
		Log: LogConfig{
			Loglevel: "debug",
			Access:   filepath.Join(logsDir, "vless_access.log"),
			Error:    filepath.Join(logsDir, "vless_error.log"),
			DNSLog:   true,
		},
		Inbounds: []InboundConfig{
			{
				Port:     10808,
				Protocol: "socks",
				Settings: InboundSettings{
					UDP:  true,
					Auth: "noauth",
				},
				Sniffing: SniffingConfig{
					Enabled:      true,
					DestOverride: []string{"http", "tls"},
					MetadataOnly: false,
				},
				Tag: "socks-in",
			},
		},
		Outbounds: []OutboundConfig{
			{
				Protocol: "vless",
				Tag:      "proxy",
				Settings: OutboundSettings{
					Vnext: []VnextConfig{
						{
							Address: server,
							Port:    port,
							Users: []UserConfig{
								{
									ID:         uuid,
									Encryption: getParam(params, "encryption", "none"),
									Flow:       params["flow"],
								},
							},
						},
					},
				},
				StreamSettings: StreamSettings{
					Network: getParam(params, "type", "tcp"),
				},
			},
		},
		Stats: struct{}{},
		Policy: PolicyConfig{
			Levels: map[string]LevelPolicy{
				"0": {
					StatsUserUplink:   true,
					StatsUserDownlink: true,
				},
			},
			System: SystemPolicy{
				StatsInboundUplink:    true,
				StatsInboundDownlink:  true,
				StatsOutboundUplink:   true,
				StatsOutboundDownlink: true,
			},
		},
	}

	// Удаляем пустой flow
	if cfg.Outbounds[0].Settings.Vnext[0].Users[0].Flow == "" {
		cfg.Outbounds[0].Settings.Vnext[0].Users[0].Flow = ""
	}

	// Настройки безопасности
	security := params["security"]
	if security == "tls" {
		cfg.Outbounds[0].StreamSettings.Security = "tls"
		cfg.Outbounds[0].StreamSettings.TLSSettings = &TLSSettings{
			ServerName:    getParam(params, "sni", server),
			AllowInsecure: params["allowInsecure"] == "1",
			Fingerprint:   params["fp"],
		}
	} else if security == "reality" {
		cfg.Outbounds[0].StreamSettings.Security = "reality"
		cfg.Outbounds[0].StreamSettings.RealitySettings = &RealitySettings{
			ServerName:  getParam(params, "sni", server),
			Fingerprint: getParam(params, "fp", "chrome"),
			Show:        false,
			PublicKey:   params["pbk"],
			ShortID:     params["sid"],
			SpiderX:     params["spx"],
		}
	}

	// Настройки транспорта
	network := getParam(params, "type", "tcp")
	if network == "ws" {
		headers := make(map[string]string)
		if host := params["host"]; host != "" {
			headers["Host"] = host
		}
		cfg.Outbounds[0].StreamSettings.WSSettings = &WSSettings{
			Path:    getParam(params, "path", "/"),
			Headers: headers,
		}
	} else if network == "grpc" {
		cfg.Outbounds[0].StreamSettings.GRPCSettings = &GRPCSettings{
			ServiceName: getParam(params, "serviceName", params["path"]),
			MultiMode:   params["mode"] == "multi",
		}
	} else if network == "h2" || network == "http" {
		cfg.Outbounds[0].StreamSettings.Network = "http"
		cfg.Outbounds[0].StreamSettings.HTTPSettings = &HTTPSettings{
			Path: []string{getParam(params, "path", "/")},
			Host: []string{getParam(params, "host", server)},
		}
	}

	// Логируем информацию
	logFile := logger.GetLogPath(logsDir, "vless")
	logger.LogMessage(logFile, fmt.Sprintf("Создание конфига для сервера: %s:%d", server, port))
	logger.LogMessage(logFile, fmt.Sprintf("UUID: %s", uuid))
	logger.LogMessage(logFile, fmt.Sprintf("Транспорт: %s", network))
	logger.LogMessage(logFile, fmt.Sprintf("Безопасность: %s", security))
	if name != "" {
		logger.LogMessage(logFile, fmt.Sprintf("Имя: %s", name))
	}

	return cfg, nil
}

func getParam(params map[string]string, key, defaultValue string) string {
	if val, ok := params[key]; ok && val != "" {
		return val
	}
	return defaultValue
}

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

	// Определяем директорию и имя исполняемого файла
	var coreDir, coreExe, coreName string
	if settings.CoreType == "v2ray" {
		coreDir = config.V2RayDir
		coreName = "V2Ray"
		if runtime.GOOS == "windows" {
			coreExe = "v2ray.exe"
		} else {
			coreExe = "v2ray"
		}
	} else {
		coreDir = config.XrayDir
		coreName = "Xray"
		if runtime.GOOS == "windows" {
			coreExe = "xray.exe"
		} else {
			coreExe = "xray"
		}
	}

	corePath := filepath.Join(coreDir, coreExe)

	// Загружаем конфигурации
	configs, err := config.LoadConfigs()
	if err != nil {
		return fmt.Errorf("ошибка загрузки конфигураций: %w", err)
	}

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

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

	// Создаем конфигурацию
	coreConfig, err := ParseVLESSURL(vlessConfig.URL, logsDir)
	if err != nil {
		return fmt.Errorf("ошибка парсинга VLESS URL: %w", err)
	}

	// Сохраняем конфигурацию
	configPath := filepath.Join(config.ConfigDir, "xray_config.json")
	data, err := json.MarshalIndent(coreConfig, "", "  ")
	if err != nil {
		return fmt.Errorf("ошибка сериализации конфига: %w", err)
	}

	if err := os.WriteFile(configPath, data, 0644); err != nil {
		return fmt.Errorf("ошибка сохранения конфига: %w", err)
	}

	// Проверяем наличие исполняемого файла
	if _, err := os.Stat(corePath); os.IsNotExist(err) {
		return fmt.Errorf("%s не найден в %s\n\nПожалуйста, перезапустите приложение для автоматической загрузки %s", coreName, coreDir, coreName)
	}

	// Создаем лог-файл трафика
	trafficLog := logger.GetTrafficLogPath(logsDir)
	logFile, err := os.Create(trafficLog)
	if err != nil {
		return fmt.Errorf("ошибка создания лог-файла: %w", err)
	}

	// Записываем заголовок
	fmt.Fprintf(logFile, "=== VPN Подключение начато: %s ===\n", time.Now().Format("2006-01-02 15:04:05"))
	fmt.Fprintf(logFile, "Конфиг: %s\n", configName)
	fmt.Fprintf(logFile, "Клиент: %s\n", coreName)
	fmt.Fprintf(logFile, "Прокси: 127.0.0.1:10808\n")
	fmt.Fprintf(logFile, "%s\n\n", strings.Repeat("=", 60))

	// Запускаем ядро
	cmd := exec.Command(corePath, "run", "-c", configPath)
	cmd.Stdout = logFile
	cmd.Stderr = logFile
	
	// На Windows создаем процесс в новой группе, чтобы Ctrl+C не убивал его
	if runtime.GOOS == "windows" {
		cmd.SysProcAttr = &syscall.SysProcAttr{
			CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP,
		}
	}

	if err := cmd.Start(); err != nil {
		logFile.Close()
		return fmt.Errorf("ошибка запуска %s: %w", coreName, err)
	}

	// Ждем немного для проверки запуска
	time.Sleep(2 * time.Second)

	// Проверяем, что процесс еще работает (для Windows используем другой метод)
	processRunning := true
	if runtime.GOOS == "windows" {
		// На Windows просто проверяем, что процесс существует
		process, err := os.FindProcess(cmd.Process.Pid)
		if err != nil {
			processRunning = false
		} else {
			// Пытаемся отправить сигнал 0 (проверка существования)
			err = process.Signal(syscall.Signal(0))
			// На Windows это всегда возвращает ошибку, поэтому просто считаем что процесс работает
			processRunning = true
		}
	} else {
		// На Unix используем стандартную проверку
		err := cmd.Process.Signal(syscall.Signal(0))
		processRunning = (err == nil)
	}

	if !processRunning {
		logFile.Close()
		
		// Читаем логи для диагностики
		errorLog := filepath.Join(logsDir, "vless_error.log")
		if errorData, readErr := os.ReadFile(errorLog); readErr == nil && len(errorData) > 0 {
			lines := strings.Split(string(errorData), "\n")
			errorLines := []string{}
			for i := len(lines) - 1; i >= 0 && len(errorLines) < 10; i-- {
				if strings.TrimSpace(lines[i]) != "" {
					errorLines = append([]string{lines[i]}, errorLines...)
				}
			}
			
			if len(errorLines) > 0 {
				return fmt.Errorf("процесс %s завершился с ошибкой.\n\nПоследние строки из лога ошибок:\n%s\n\nПолный лог: %s", 
					coreName, strings.Join(errorLines, "\n"), errorLog)
			}
		}
		
		return fmt.Errorf("процесс %s завершился с ошибкой.\nПроверьте логи в: %s", coreName, errorLog)
	}

	// Сохраняем состояние
	state := &config.ConnectionState{
		Connected:  true,
		ConfigName: configName,
		ConfigType: "vless",
		StartTime:  time.Now().Format(time.RFC3339),
		Interface:  strings.ToLower(coreName),
		ProcessPID: cmd.Process.Pid,
		LogFile:    trafficLog,
	}

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

	// Логируем успешное подключение
	logPath := logger.GetLogPath(logsDir, "vless")
	logger.LogMessage(logPath, fmt.Sprintf("Успешно подключено к '%s' через %s (PID: %d, Лог: %s)", configName, coreName, cmd.Process.Pid, trafficLog))

	fmt.Printf("✓ Подключено к '%s' через %s\n", configName, coreName)
	fmt.Printf("SOCKS5 прокси: 127.0.0.1:10808\n")
	
	// Предлагаем настроить системный прокси
	fmt.Println("\n" + strings.Repeat("─", 60))
	fmt.Println("Настроить системный прокси Windows?")
	fmt.Println("Это позволит всем приложениям использовать VPN.")
	fmt.Print("(y/n): ")
	
	var response string
	fmt.Scanln(&response)
	
	if strings.ToLower(response) == "y" || strings.ToLower(response) == "д" {
		if err := proxy.EnableSystemProxy("127.0.0.1:10808"); err != nil {
			fmt.Printf("⚠ Не удалось настроить системный прокси: %v\n", err)
			fmt.Println("Вы можете настроить его вручную в настройках Windows")
		} else {
			fmt.Println("✓ Системный прокси настроен")
		}
	} else {
		fmt.Println("\nДля использования VPN настройте прокси вручную:")
		fmt.Println("  1. Откройте Настройки Windows → Сеть и Интернет → Прокси")
		fmt.Println("  2. Включите 'Использовать прокси-сервер'")
		fmt.Println("  3. Адрес: 127.0.0.1, Порт: 10808")
		fmt.Println("\nИли настройте SOCKS5 прокси в браузере:")
		fmt.Println("  Firefox: Настройки → Основные → Параметры сети")
		fmt.Println("  Chrome: Настройки → Система → Открыть настройки прокси")
	}
	
	fmt.Printf("\nЛоги трафика:\n")
	fmt.Printf("  - Основной: %s\n", trafficLog)
	fmt.Printf("  - Доступ (IP): %s\n", filepath.Join(logsDir, "vless_access.log"))
	fmt.Printf("  - Ошибки: %s\n", filepath.Join(logsDir, "vless_error.log"))

	return nil
}

// PingServer проверяет доступность VLESS сервера
func PingServer(vlessURL string, timeout time.Duration) (bool, float64, error) {
	// Парсим URL для получения адреса сервера
	urlStr := strings.TrimPrefix(vlessURL, "vless://")
	
	if idx := strings.Index(urlStr, "#"); idx != -1 {
		urlStr = urlStr[:idx]
	}
	
	if idx := strings.Index(urlStr, "?"); idx != -1 {
		urlStr = urlStr[:idx]
	}
	
	parts := strings.Split(urlStr, "@")
	if len(parts) != 2 {
		return false, 0, fmt.Errorf("неверный формат URL")
	}
	
	serverPort := parts[1]
	var server string
	var port string
	
	if strings.Contains(serverPort, "[") {
		endIdx := strings.Index(serverPort, "]")
		server = serverPort[1:endIdx]
		port = strings.TrimPrefix(serverPort[endIdx+1:], ":")
	} else {
		lastColon := strings.LastIndex(serverPort, ":")
		server = serverPort[:lastColon]
		port = serverPort[lastColon+1:]
	}
	
	if port == "" {
		port = "443"
	}
	
	// Измеряем время подключения
	start := time.Now()
	conn, err := net.DialTimeout("tcp", net.JoinHostPort(server, port), timeout)
	elapsed := time.Since(start)
	
	if err != nil {
		return false, 0, err
	}
	conn.Close()
	
	return true, float64(elapsed.Milliseconds()), nil
}
