package main

import (
	"archive/tar"
	"compress/gzip"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"
)

// selfUpdate 自动更新
func (a *Agent) selfUpdate() {
	// 获取当前可执行文件路径
	exePath, err := os.Executable()
	if err != nil {
		log.Errorf("获取可执行文件路径失败: %v", err)
		return
	}
	exePath, _ = filepath.Abs(exePath)

	// 下载新版本 tar.gz
	downloadURL := a.config.ServerURL + "/api/agent/download?os=" + runtime.GOOS + "&arch=" + runtime.GOARCH
	req, err := http.NewRequest("GET", downloadURL, nil)
	if err != nil {
		log.Errorf("创建下载请求失败: %v", err)
		return
	}
	req.Header.Set("Authorization", "Bearer "+a.config.Token)

	client := &http.Client{Timeout: 5 * time.Minute}
	resp, err := client.Do(req)
	if err != nil {
		log.Errorf("下载新版本失败: %v", err)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		log.Errorf("下载新版本失败: HTTP %d", resp.StatusCode)
		return
	}

	// 读取 tar.gz 内容
	gzReader, err := gzip.NewReader(resp.Body)
	if err != nil {
		log.Errorf("解压 gzip 失败: %v", err)
		return
	}
	defer gzReader.Close()

	tarReader := tar.NewReader(gzReader)

	// 解压并找到二进制文件
	var newBinary []byte
	binaryName := "baihu-agent"
	if runtime.GOOS == "windows" {
		binaryName = "baihu-agent.exe"
	}

	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Errorf("读取 tar 失败: %v", err)
			return
		}

		if header.Typeflag == tar.TypeReg && header.Name == binaryName {
			newBinary, err = io.ReadAll(tarReader)
			if err != nil {
				log.Errorf("读取二进制文件失败: %v", err)
				return
			}
			break
		}
	}

	if newBinary == nil {
		log.Errorf("tar.gz 中未找到 %s", binaryName)
		return
	}

	// 保存到临时文件（放到 data 目录）
	os.MkdirAll(dataDir, 0755)
	tmpFile := filepath.Join(dataDir, binaryName+".new")
	if err := os.WriteFile(tmpFile, newBinary, 0755); err != nil {
		log.Errorf("保存新版本失败: %v", err)
		return
	}

	// 计算基础路径（去掉所有 .bak 后缀）
	basePath := exePath
	for strings.HasSuffix(basePath, ".bak") {
		basePath = strings.TrimSuffix(basePath, ".bak")
	}
	backupFile := basePath + ".bak"

	// 如果当前运行的就是 .bak 文件，直接删除它（更新后会用新版本）
	// 否则需要备份当前文件
	if exePath != backupFile {
		os.Remove(backupFile)
		if err := os.Rename(exePath, backupFile); err != nil {
			log.Errorf("备份旧版本失败: %v", err)
			os.Remove(tmpFile)
			return
		}
	}

	// 替换为新版本（放到 basePath，即不带 .bak 的路径）
	if err := os.Rename(tmpFile, basePath); err != nil {
		log.Errorf("替换新版本失败: %v", err)
		if exePath != backupFile {
			os.Rename(backupFile, exePath) // 恢复旧版本
		}
		return
	}

	// 如果之前运行的是 .bak 文件，现在可以删除它了
	if exePath == backupFile {
		os.Remove(exePath)
	}

	log.Info("更新完成，正在重启...")

	// 重启服务
	a.restart()
}

// restart 重启服务
func (a *Agent) restart() {
	exePath, _ := os.Executable()

	// 计算基础路径（去掉所有 .bak 后缀），确保启动的是正确的可执行文件
	basePath := exePath
	for strings.HasSuffix(basePath, ".bak") {
		basePath = strings.TrimSuffix(basePath, ".bak")
	}

	// 删除 PID 文件，避免新进程检测到旧 PID 而拒绝启动
	removePidFile()

	if runtime.GOOS == "windows" {
		// Windows: 启动新进程后退出
		cmd := exec.Command(basePath, "start")
		cmd.Start()
		os.Exit(0)
	} else {
		// Linux/macOS: 使用 exec 替换当前进程，直接运行（不需要 daemon）
		// 因为 syscall.Exec 会替换当前进程，当前进程本身就是 daemon
		// --restart 标记告诉新进程这是重启，只输出到文件
		syscall.Exec(basePath, []string{basePath, "run", "--restart"}, os.Environ())
	}
}
