package main

import (
	_ "embed"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"sync"
	"time"

	"github.com/jchv/go-webview2"
)

//go:embed web/update.html
var updateHTML string

//go:embed updater.exe
var updaterExe []byte

type PackageInfo struct {
	URL    string `json:"url"`
	Size   int64  `json:"size"`
	SHA256 string `json:"sha256"`
}

type UpdateInfo struct {
	Version      string      `json:"version"`
	Force        bool        `json:"force"`
	ReleaseNotes string      `json:"release_notes"`
	Package      PackageInfo `json:"package"`
}

var (
	updateMu     sync.Mutex
	updateOpen   bool
	updateInfo   *UpdateInfo
)

const defaultUpdateURL = "https://c.1216.top/u-desktop/version.json"

func getUpdateURL() string {
	cfg := loadConfig()
	if cfg.UpdateURL != "" {
		return cfg.UpdateURL
	}
	return defaultUpdateURL
}

func checkUpdate() (*UpdateInfo, error) {
	resp, err := httpClient.Get(getUpdateURL())
	if err != nil {
		return nil, fmt.Errorf("请求 version.json 失败: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("version.json 返回状态 %d", resp.StatusCode)
	}

	var info UpdateInfo
	if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
		return nil, fmt.Errorf("解析 version.json 失败: %w", err)
	}

	if info.Version == "" || info.Version == version || info.Version == "dev" {
		return nil, nil
	}

	// 用户跳过过此版本且非强制
	cfg := loadConfig()
	if !info.Force && cfg.SkipVersion == info.Version {
		return nil, nil
	}

	return &info, nil
}

func runAutoUpdateCheck() {
	cfg := loadConfig()
	if !cfg.AutoCheckUpdate {
		return
	}
	time.Sleep(3 * time.Second)

	info, err := checkUpdate()
	if err != nil {
		log.Println("自动更新检查失败:", err)
		return
	}
	if info != nil {
		showUpdateWindow(info)
	}
}

func showUpdateWindow(info *UpdateInfo) {
	updateMu.Lock()
	if updateOpen {
		updateMu.Unlock()
		return
	}
	updateOpen = true
	updateInfo = info
	updateMu.Unlock()

	go func() {
		runtime.LockOSThread()
		defer func() {
			updateMu.Lock()
			updateOpen = false
			updateInfo = nil
			updateMu.Unlock()
			runtime.UnlockOSThread()
		}()

		winW := 340
		winH := 220
		if info.Force {
			winW = 420
			winH = 260
		}

		dataDir := filepath.Join(configDir(), "update-data")
		os.MkdirAll(dataDir, 0755)

		w := webview2.NewWithOptions(webview2.WebViewOptions{
			AutoFocus: true,
			DataPath:  dataDir,
			WindowOptions: webview2.WindowOptions{
				Title:  "更新提示",
				Width:  uint(winW),
				Height: uint(winH),
			},
		})
		if w == nil {
			log.Println("更新窗口: 创建失败")
			return
		}

		hwnd := uintptr(w.Window())

		// 无边框
		style, _, _ := procGetWindowLongPtrW.Call(hwnd, gwlStyle)
		procSetWindowLongPtrW.Call(hwnd, gwlStyle, style & ^uintptr(wsSizebox|wsMaxbox|0x00C00000))
		// 置顶 + 工具窗口
		procSetWindowLongPtrW.Call(hwnd, gwlExStyle, wsExToolwindow|0x00000008)

		// 定位窗口
		screenW, screenH := getScreenSize()
		if info.Force {
			x := (int(screenW) - winW) / 2
			y := (int(screenH) - winH) / 2
			procMoveWindow.Call(hwnd, uintptr(x), uintptr(y), uintptr(winW), uintptr(winH), 1)
		} else {
			x := int(screenW) - winW - 20
			y := int(screenH) - winH - 60
			procMoveWindow.Call(hwnd, uintptr(x), uintptr(y), uintptr(winW), uintptr(winH), 1)
		}

		w.Bind("getUpdateInfo", func() string {
			if updateInfo == nil {
				return "{}"
			}
			data, _ := json.Marshal(map[string]interface{}{
				"version":       updateInfo.Version,
				"force":         updateInfo.Force,
				"releaseNotes":  updateInfo.ReleaseNotes,
				"size":          updateInfo.Package.Size,
				"currentVersion": version,
			})
			return string(data)
		})

		w.Bind("skipUpdate", func() string {
			if updateInfo != nil && !updateInfo.Force {
				cfg := loadConfig()
				cfg.SkipVersion = updateInfo.Version
				saveConfig(cfg)
			}
			return ""
		})

		w.Bind("startUpdate", func() string {
			if updateInfo == nil {
				return ""
			}
			go doStartUpdate(updateInfo)
			return ""
		})

		w.SetHtml(updateHTML)
		log.Println("更新窗口已打开")
		w.Run()
		log.Println("更新窗口已关闭")
	}()
}

func doStartUpdate(info *UpdateInfo) {
	exePath, err := os.Executable()
	if err != nil {
		log.Println("获取自身路径失败:", err)
		return
	}
	exeDir := filepath.Dir(exePath)

	// 释放嵌入的 updater.exe 到临时目录
	updaterPath := filepath.Join(os.TempDir(), "u-desktop-updater.exe")
	if err := os.WriteFile(updaterPath, updaterExe, 0755); err != nil {
		log.Println("释放 updater.exe 失败:", err)
		showError("释放升级器失败: " + err.Error())
		return
	}

	backupDir := filepath.Join(exeDir, "backup")
	os.MkdirAll(backupDir, 0755)

	args := []string{
		"--package-url", info.Package.URL,
		"--sha256", info.Package.SHA256,
		"--target", exePath,
		"--version", info.Version,
		"--backup-dir", backupDir,
	}
	if info.Force {
		args = append(args, "--force")
	}

	cmd := exec.Command(updaterPath, args...)
	cmd.Stdout = nil
	cmd.Stderr = nil
	if err := cmd.Start(); err != nil {
		log.Println("启动 updater 失败:", err)
		return
	}

	log.Println("updater 已启动, 主程序退出")
	os.Exit(0)
}

func manualCheckUpdate() {
	info, err := checkUpdate()
	if err != nil {
		log.Println("手动更新检查失败:", err)
		showError("检查更新失败: " + err.Error())
		return
	}
	if info == nil {
		showError("当前已是最新版本 (" + version + ")")
		return
	}
	showUpdateWindow(info)
}

func formatSize(size int64) string {
	const (
		KB = 1024
		MB = KB * 1024
	)
	switch {
	case size >= MB:
		return strconv.FormatFloat(float64(size)/float64(MB), 'f', 1, 64) + " MB"
	case size >= KB:
		return strconv.FormatFloat(float64(size)/float64(KB), 'f', 1, 64) + " KB"
	default:
		return strconv.FormatInt(size, 10) + " B"
	}
}
