package main

import (
	"errors"
	"fmt"
	"image/color"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"syscall"

	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/theme"
	"fyne.io/fyne/v2/widget"
	"fyne.io/fyne/v2"
)

// Config represents a VPN configuration
type Config struct {
	Title string
	URL   string
}

var (
	currentProcess *exec.Cmd
	configs        []Config // Store all configurations
	activeConfig   int      // Index of the active config (-1 if none)
)

// checkSingBox checks if sing-box.exe exists in the bin folder
func checkSingBox() bool {
	singBoxPath := filepath.Join("bin", "sing-box.exe")
	if _, err := os.Stat(singBoxPath); os.IsNotExist(err) {
		return false
	}
	return true
}

// isValidVLESS checks if the given URL is a valid VLESS URL
func isValidVLESS(vlessURL string) bool {
	// Basic validation for VLESS URL format
	if !strings.HasPrefix(vlessURL, "vless://") {
		return false
	}

	u, err := url.Parse(vlessURL)
	if err != nil {
		return false
	}

	// Check if it's a valid VLESS link
	matched, _ := regexp.MatchString(`^vless://[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}@.*`, vlessURL)
	if !matched {
		return false
	}

	// Additional validation checks can be added here
	return u.Scheme == "vless" && u.Host != ""
}

// addConfig adds a new configuration to the list
func addConfig(title, url string) {
	configs = append(configs, Config{Title: title, URL: url})
}

// updateConfig updates an existing configuration
func updateConfig(index int, title, url string) {
	if index >= 0 && index < len(configs) {
		configs[index].Title = title
		configs[index].URL = url
	}
}

// removeConfig removes a configuration by index
func removeConfig(index int) {
	if index >= 0 && index < len(configs) {
		configs = append(configs[:index], configs[index+1:]...)
		if activeConfig >= index {
			activeConfig--
		}
		if activeConfig >= len(configs) {
			activeConfig = -1
		}
	}
}

// runSingBox starts the sing-box process with the given config path
func runSingBox(configPath string) error {
	singBoxPath := filepath.Join("bin", "sing-box.exe")

	if !checkSingBox() {
		return nil
	}

	cmd := exec.Command(singBoxPath, "run", "-c", configPath)

	// Make the process run as hidden
	cmd.SysProcAttr = &syscall.SysProcAttr{
		HideWindow: true,
	}

	err := cmd.Start()
	if err != nil {
		return err
	}

	currentProcess = cmd
	return nil
}

// killCurrentProcess terminates the current sing-box process if running
func killCurrentProcess() {
	if currentProcess != nil && currentProcess.Process != nil {
		currentProcess.Process.Kill()
		currentProcess = nil
	}
}

func main() {
	myApp := app.New()
	myWindow := myApp.NewWindow("Hamy VPN Client")

	// Set the window to not resizable and fixed size
	myWindow.SetFixedSize(true)

	// Set the window size to 200x300 as requested
	myWindow.Resize(fyne.NewSize(200, 300))

	// Initialize active config to -1 (no config selected)
	activeConfig = -1

	// Connection state - true for connected, false for disconnected
	var isConnected bool = false

	// Update connection button text based on connection state
	updateConnectionButtonText := func(button *widget.Button) {
		if isConnected {
			button.SetText("Отключить")
		} else {
			button.SetText("Подключить")
		}
	}

	// Create program title with proper padding
	titleText := canvas.NewText("Hamy VPN", nil)
	titleText.TextStyle.Bold = true
	titleText.TextSize = 16
	titleText.Alignment = fyne.TextAlignLeading

	// Create settings button (gear icon)
	settingsButton := widget.NewButtonWithIcon("", theme.SettingsIcon(), func() {
		// Show settings dialog in a separate fixed-size window
		settingsWindow := myApp.NewWindow("Настройки")
		settingsWindow.Resize(fyne.NewSize(400, 300))
		settingsWindow.SetFixedSize(true)

		// Add content to settings window (for now just a placeholder)
		settingsLabel := widget.NewLabel("Настройки приложения")
		settingsContent := container.NewVBox(
			settingsLabel,
			widget.NewLabel("Добавьте элементы настроек здесь"),
		)

		settingsWindow.SetContent(settingsContent)
		settingsWindow.Show()
	})

	// Create HBox for title and settings button with proper padding
	titleContainer := container.NewBorder(nil, nil, titleText, settingsButton, layout.NewSpacer())

	// Status indicator (bold, smaller padding)
	statusLabel := canvas.NewText("Отключено", color.RGBA{R: 128, G: 128, B: 128, A: 255}) // Gray color
	statusLabel.Alignment = fyne.TextAlignCenter
	statusLabel.TextSize = 14  // Small font
	statusLabel.TextStyle.Bold = true  // Make it bold

	// Connect/Disconnect button (with dynamic text)
	var connectButton *widget.Button
	connectButton = widget.NewButton("Подключить", func() {
		if activeConfig == -1 || len(configs) == 0 {
			dialog.ShowError(errors.New("Выберите конфигурацию"), myWindow)
			return
		}

		// Toggle connection state
		isConnected = !isConnected

		// Update button text
		updateConnectionButtonText(connectButton)

		// Update status text
		if isConnected {
			statusLabel.Text = "Подключено"
			statusLabel.Color = color.RGBA{R: 0, G: 128, B: 0, A: 255} // Green color for connected
		} else {
			statusLabel.Text = "Отключено"
			statusLabel.Color = color.RGBA{R: 128, G: 128, B: 128, A: 255} // Gray color for disconnected
		}
		statusLabel.Refresh()

		// This functionality will be implemented later
		dialog.ShowInformation("Info", fmt.Sprintf("Connect/Disconnect functionality will be implemented later using: %s", configs[activeConfig].Title), myWindow)
	})
	connectButton.Importance = widget.HighImportance
	if len(configs) == 0 {
		connectButton.Disable() // Disable initially until a config is selected
	} else {
		connectButton.Enable() // Enable if we have configs
	}

	// Configurations button - opens a separate window
	configsButton := widget.NewButton("Конфигурации", func() {
		// Create a separate fixed-size window for configurations
		configWindow := myApp.NewWindow("Конфигурации")
		configWindow.Resize(fyne.NewSize(400, 300))
		configWindow.SetFixedSize(true)

		// Declare variables before use
		var listContainer *widget.List
		var scrollContainer *container.Scroll
		var refreshList func()

		// Entry for importing new configuration - fixed to expand properly
		importEntry := widget.NewEntry()
		importEntry.PlaceHolder = "vless://..."

		// Container for import field and button - use HBox to allow proper expansion
		importButton := widget.NewButtonWithIcon("", theme.ContentAddIcon(), func() {
			url := importEntry.Text
			if url != "" && isValidVLESS(url) {
				title := fmt.Sprintf("Конфиг %d", len(configs)+1)
				addConfig(title, url)
				dialog.ShowInformation("Успешно", "Конфиг добавлен", configWindow)

				// Refresh the list
				refreshList()
				importEntry.SetText("")
			} else if url != "" {
				dialog.ShowError(errors.New("Невалидная ссылка"), configWindow)
			}
		})

		// Fixed the order of operations to avoid nil reference
		refreshList = func() {
			// Create the list first
			newListContainer := widget.NewList(
				func() int {
					return len(configs)
				},
				func() fyne.CanvasObject {
					return container.NewBorder(nil, nil,
						widget.NewLabel("Template"),
						container.NewHBox(
							widget.NewButtonWithIcon("", theme.DocumentCreateIcon(), func() {}),
							widget.NewButtonWithIcon("", theme.DeleteIcon(), func() {}),
						),
					)
				},
				func(id widget.ListItemID, obj fyne.CanvasObject) {
					containerObj := obj.(*fyne.Container)

					// For a container.NewBorder(nil, nil, left, right, nil), the structure is:
					// Objects[0] is the left element (label)
					// Objects[1] is the right element (buttons container)
					titleLabel := containerObj.Objects[0].(*widget.Label)
					buttonsContainer := containerObj.Objects[1].(*fyne.Container)

					editButton := buttonsContainer.Objects[0].(*widget.Button)
					deleteButton := buttonsContainer.Objects[1].(*widget.Button)

					// Update visual indication for active config
					if id == activeConfig {
						titleLabel.SetText(fmt.Sprintf("● %s", configs[id].Title))
					} else {
						titleLabel.SetText(fmt.Sprintf("○ %s", configs[id].Title))
					}

					// Set up edit button
					editButton.OnTapped = func() {
						showEditDialog(configWindow, id)
					}

					// Set up delete button
					deleteButton.OnTapped = func() {
						dialog.ShowConfirm("Удалить конфигурацию",
							fmt.Sprintf("Вы уверены, что хотите удалить конфигурацию '%s'?", configs[id].Title),
							func(confirmed bool) {
								if confirmed {
									removeConfig(id)
									dialog.ShowInformation("Удалено", "Конфигурация удалена", configWindow)

									// Refresh the list
									refreshList()
								}
							},
							configWindow)
					}
				},
			)

			newListContainer.OnSelected = func(id widget.ListItemID) {
				activeConfig = id
				dialog.ShowInformation("Config Selected", fmt.Sprintf("Active config: %s", configs[id].Title), configWindow)
				configWindow.Close()
			}

			// Assign to the variable before using it in the scroll container
			listContainer = newListContainer

			// Create scroll container only once
			if scrollContainer == nil {
				scrollContainer = container.NewScroll(listContainer)
			} else {
				scrollContainer.Content = listContainer
				scrollContainer.Refresh()
			}
		}

		// Call refreshList to initialize the list first
		refreshList()

		// Container for import field and button - use HBox to allow proper expansion
		// Using container.NewBorder to make the entry expand properly
		importContainer := container.NewBorder(nil, nil, nil, importButton, importEntry)

		// Main content for the config window using a border container to properly size the scroll area
		configContent := container.NewBorder(importContainer, nil, nil, nil, scrollContainer)

		configWindow.SetContent(configContent)
		configWindow.Show()
	})

	// Main content container with vertical layout
	contentContainer := container.NewVBox(
		titleContainer,
		layout.NewSpacer(),
		container.NewCenter(statusLabel),
		layout.NewSpacer(),
		container.NewCenter(connectButton),
		layout.NewSpacer(),
		container.NewCenter(configsButton),
		layout.NewSpacer(),
	)

	// Create a top-level container
	mainContainer := container.NewStack(contentContainer)

	// Set the content and show the window
	myWindow.SetContent(mainContainer)

	myWindow.ShowAndRun()
}

// Function to show the edit dialog
func showEditDialog(parentWindow fyne.Window, configIndex int) {
	// Create entry fields for title and URL with existing values
	titleEntry := widget.NewEntry()
	titleEntry.SetText(configs[configIndex].Title)
	titleEntry.PlaceHolder = "Название конфигурации"
	urlEntry := widget.NewEntry()
	urlEntry.SetText(configs[configIndex].URL)
	urlEntry.PlaceHolder = "vless://..."

	// Create a custom dialog
	editDialog := dialog.NewForm("Редактировать конфигурацию", "Сохранить", "Отмена",
		[]*widget.FormItem{
			widget.NewFormItem("Заголовок", titleEntry),
			widget.NewFormItem("Ссылка", urlEntry),
		},
		func(ok bool) {
			if ok {
				title := titleEntry.Text
				url := urlEntry.Text

				if title == "" {
					title = fmt.Sprintf("Конфиг %d", configIndex+1) // Default title
				}

				if isValidVLESS(url) {
					updateConfig(configIndex, title, url)
					dialog.ShowInformation("Успешно", "Конфиг обновлен", parentWindow)
				} else {
					dialog.ShowError(errors.New("Невалидная ссылка"), parentWindow)
				}
			}
		},
		parentWindow,
	)
	editDialog.Show()
}