package main

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"syscall"
)

type PlecConfig struct {
	UploadUrl       string `json:"uploadUrl"`
	OriginUrl       string `json:"originUrl"`
	PreviewBase     string `json:"previewBase"`
	SkipExistsCheck bool   `json:"skipExistsCheck"`
}

type ApplovinConfig struct {
	Cookie             string `json:"cookie"`
	DelayMs            int    `json:"delayMs"`
	RandomizeUserAgent *bool  `json:"randomizeUserAgent,omitempty"`
}

type SendToMobileConfig struct {
	Port int `json:"port"`
}

type AppConfig struct {
	Plec             PlecConfig         `json:"plec"`
	Applovin         ApplovinConfig     `json:"applovin"`
	SendToMobile     SendToMobileConfig `json:"sendToMobile"`
	BetaToolsEnabled bool               `json:"betaToolsEnabled"`
	LastPickDir      string             `json:"lastPickDir,omitempty"`
}

var configMu sync.Mutex

// userDataDir returns %LOCALAPPDATA%\HPLToolbox, creating it if needed.
// All mutable app files (config, lock, logs, WebView2 cache) live here so
// they are never placed next to the executable, which may be on a synced or
// antivirus-watched drive and would cause message-loop stalls.
func userDataDir() string {
	local := os.Getenv("LOCALAPPDATA")
	if local == "" {
		local = os.Getenv("APPDATA")
	}
	if local == "" {
		return appDir()
	}
	dir := filepath.Join(local, "HPLToolbox")
	_ = os.MkdirAll(dir, 0755)
	return dir
}

func defaultConfig() AppConfig {
	return AppConfig{
		Plec: PlecConfig{
			UploadUrl:       "http://167.99.227.249/src/upload_HTML_Experimental.php",
			OriginUrl:       "http://167.99.227.249",
			PreviewBase:     "https://playable.applovindemo.com/phaser/",
			SkipExistsCheck: false,
		},
		Applovin:     ApplovinConfig{Cookie: "", DelayMs: 5000},
		SendToMobile: SendToMobileConfig{Port: 0},
	}
}

func appDir() string {
	exe, err := os.Executable()
	if err != nil {
		wd, _ := os.Getwd()
		return wd
	}
	// `go run` puts the exe in a temp dir; fall back to cwd in that case.
	resolved, err := filepath.EvalSymlinks(exe)
	if err == nil {
		exe = resolved
	}
	dir := filepath.Dir(exe)
	if strings.Contains(strings.ToLower(dir), "go-build") {
		wd, _ := os.Getwd()
		return wd
	}
	return dir
}

func configPath() string {
	return filepath.Join(userDataDir(), "config.json")
}

func LoadConfig() AppConfig {
	configMu.Lock()
	defer configMu.Unlock()
	cfg := defaultConfig()
	data, err := os.ReadFile(configPath())
	if err != nil {
		return cfg
	}
	_ = json.Unmarshal(data, &cfg)
	return cfg
}

func SaveConfig(cfg AppConfig) error {
	configMu.Lock()
	defer configMu.Unlock()
	data, err := json.MarshalIndent(cfg, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(configPath(), data, 0644)
}

func OpenInBrowser(url string) {
	cmd := hiddenCommand("rundll32.exe", "url.dll,FileProtocolHandler", url)
	_ = cmd.Start()
}

func CopyToClipboard(text string) error {
	cmd := hiddenCommand("clip")
	cmd.Stdin = strings.NewReader(text)
	return cmd.Run()
}

type FileFilter struct {
	Name       string
	Extensions []string
}

func psEscape(s string) string { return strings.ReplaceAll(s, "'", "''") }

func hiddenCommand(name string, args ...string) *exec.Cmd {
	cmd := exec.Command(name, args...)
	cmd.SysProcAttr = &syscall.SysProcAttr{
		HideWindow:    true,
		CreationFlags: 0x08000000,
	}
	return cmd
}

func PickFiles(title string, filters []FileFilter, multi bool, initialDir string) []string {
	filterStr := "All files|*.*"
	if len(filters) > 0 {
		parts := make([]string, 0, len(filters))
		for _, f := range filters {
			exts := make([]string, 0, len(f.Extensions))
			for _, e := range f.Extensions {
				exts = append(exts, "*."+e)
			}
			parts = append(parts, fmt.Sprintf("%s|%s", f.Name, strings.Join(exts, ";")))
		}
		filterStr = strings.Join(parts, "|")
	}
	multiStr := "false"
	if multi {
		multiStr = "true"
	}
	script := fmt.Sprintf(`
Add-Type -AssemblyName System.Windows.Forms
$dlg = New-Object System.Windows.Forms.OpenFileDialog
$dlg.Title = '%s'
$dlg.Filter = '%s'
$dlg.Multiselect = $%s
if ('%s'.Length -gt 0) { $dlg.InitialDirectory = '%s' }
if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
  $dlg.FileNames | ForEach-Object { Write-Output $_ }
}
`, psEscape(title), psEscape(filterStr), multiStr, psEscape(initialDir), psEscape(initialDir))
	out, err := hiddenCommand("powershell", "-NoProfile", "-NonInteractive", "-STA", "-Command", script).Output()
	if err != nil {
		return nil
	}
	lines := strings.Split(string(out), "\n")
	result := make([]string, 0, len(lines))
	for _, l := range lines {
		t := strings.TrimSpace(l)
		if t != "" {
			result = append(result, t)
		}
	}
	return result
}

func PickSaveFile(title, defaultName string, filters []FileFilter, initialDir string) string {
	filterStr := "All files|*.*"
	if len(filters) > 0 {
		parts := make([]string, 0, len(filters))
		for _, f := range filters {
			exts := make([]string, 0, len(f.Extensions))
			for _, e := range f.Extensions {
				exts = append(exts, "*."+e)
			}
			parts = append(parts, fmt.Sprintf("%s|%s", f.Name, strings.Join(exts, ";")))
		}
		filterStr = strings.Join(parts, "|")
	}
	script := fmt.Sprintf(`
Add-Type -AssemblyName System.Windows.Forms
$dlg = New-Object System.Windows.Forms.SaveFileDialog
$dlg.Title = '%s'
$dlg.Filter = '%s'
$dlg.FileName = '%s'
if ('%s'.Length -gt 0) { $dlg.InitialDirectory = '%s' }
if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
  Write-Output $dlg.FileName
}
`, psEscape(title), psEscape(filterStr), psEscape(defaultName), psEscape(initialDir), psEscape(initialDir))
	out, err := hiddenCommand("powershell", "-NoProfile", "-NonInteractive", "-STA", "-Command", script).Output()
	if err != nil {
		return ""
	}
	return strings.TrimSpace(string(out))
}

func PickFolder(title, initialDir string) string {
	script := fmt.Sprintf(`
Add-Type -AssemblyName System.Windows.Forms
$dlg = New-Object System.Windows.Forms.FolderBrowserDialog
$dlg.Description = '%s'
if ('%s'.Length -gt 0) { $dlg.SelectedPath = '%s' }
if ($dlg.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
  Write-Output $dlg.SelectedPath
}
`, psEscape(title), psEscape(initialDir), psEscape(initialDir))
	out, err := hiddenCommand("powershell", "-NoProfile", "-NonInteractive", "-STA", "-Command", script).Output()
	if err != nil {
		return ""
	}
	return strings.TrimSpace(string(out))
}
