package main

import (
	"archive/zip"
	"fmt"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"

	"github.com/spf13/cobra"
	"golang.org/x/sys/windows/registry"
)

// 全局配置
const (
	InstallRoot = "./gopkg_root" // 软件安装根目录
)

func main() {
	// 1. 定义根命令
	var rootCmd = &cobra.Command{
		Use:   "gopkg",
		Short: "一个用 Go 编写的极速包管理器",
		Long:  "Gopkg 支持自动下载、解压、配置环境变量，让开发环境搭建变得简单。",
	}

	// 2. 定义 'install' 命令
	var installCmd = &cobra.Command{
		Use:   "install [包名]",
		Short: "安装指定的软件包",
		Args:  cobra.ExactArgs(1),
		Run: func(cmd *cobra.Command, args []string) {
			pkgName := args[0]
			pkg := FindPackage(pkgName)
			if pkg == nil {
				fmt.Printf("❌ 找不到软件包: %s\n", pkgName)
				fmt.Println("💡 请使用 'gopkg list' 查看所有可用软件。")
				return
			}

			fmt.Printf("📦 正在准备安装: %s (v%s)\n", pkg.Name, pkg.Version)
			if err := executeInstall(pkg); err != nil {
				fmt.Printf("❌ 安装失败: %v\n", err)
				return
			}
			fmt.Printf("🎉 %s 安装成功！\n", pkg.Name)
		},
	}

	// 3. 定义 'uninstall' 命令 (新增功能)
	var uninstallCmd = &cobra.Command{
		Use:   "uninstall [包名]",
		Short: "卸载指定的软件包",
		Args:  cobra.ExactArgs(1),
		Run: func(cmd *cobra.Command, args []string) {
			pkgName := args[0]
			// 即使不在注册表中，只要文件夹存在也能卸载
			targetDir := filepath.Join(InstallRoot, pkgName)

			if _, err := os.Stat(targetDir); os.IsNotExist(err) {
				fmt.Printf("❌ 未安装该软件包: %s\n", pkgName)
				return
			}

			fmt.Printf("🗑️  正在卸载: %s...\n", pkgName)

			// 1. 删除文件夹
			if err := os.RemoveAll(targetDir); err != nil {
				fmt.Printf("❌ 文件删除失败: %v\n", err)
				return
			}

			// 2. 移除环境变量
			absPath, _ := filepath.Abs(targetDir)
			if err := removeFromPath(absPath); err != nil {
				fmt.Printf("⚠️  文件夹已删除，但环境变量清理失败: %v\n", err)
				fmt.Println("💡 请手动检查系统环境变量 PATH")
				return
			}

			fmt.Printf("🧹 %s 已完全卸载。\n", pkgName)
		},
	}

	// 4. 定义 'list' 命令
	var listCmd = &cobra.Command{
		Use:   "list",
		Short: "列出所有可用的软件包",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("📚 可用的软件包列表:")
			for _, pkg := range ListPackages() {
				fmt.Printf("   - %s (v%s): %s\n", pkg.Name, pkg.Version, pkg.Description)
			}
		},
	}

	// 5. 组装命令
	rootCmd.AddCommand(installCmd)
	rootCmd.AddCommand(uninstallCmd)
	rootCmd.AddCommand(listCmd)

	// 6. 执行
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

// executeInstall 执行具体的安装流程
func executeInstall(pkg *PackageDef) error {
	// 确保安装目录存在
	if err := os.MkdirAll(InstallRoot, os.ModePerm); err != nil {
		return err
	}

	// 1. 下载
	fileName := filepath.Base(pkg.URL)
	tempZipPath := filepath.Join(InstallRoot, fileName)
	
	if _, err := os.Stat(tempZipPath); os.IsNotExist(err) {
		fmt.Printf("⬇️  正在下载: %s\n", pkg.URL)
		if err := downloadFile(pkg.URL, tempZipPath); err != nil {
			return fmt.Errorf("下载失败: %v", err)
		}
	} else {
		fmt.Println("ℹ️  缓存命中，跳过下载")
	}

	// 2. 安装 (区分 zip 和 单文件)
	var targetBinDir string

	if strings.HasSuffix(pkg.URL, ".zip") {
		targetBinDir = filepath.Join(InstallRoot, pkg.Name)
		os.RemoveAll(targetBinDir)
		
		tempExtractDir := filepath.Join(InstallRoot, "temp_extract")
		os.RemoveAll(tempExtractDir)
		os.MkdirAll(tempExtractDir, os.ModePerm)

		if err := unzip(tempZipPath, tempExtractDir); err != nil {
			return fmt.Errorf("解压失败: %v", err)
		}

		entries, _ := os.ReadDir(tempExtractDir)
		if len(entries) == 0 {
			return fmt.Errorf("压缩包为空")
		}
		
		sourceBinPath := filepath.Join(tempExtractDir, entries[0].Name(), "bin")
		if _, err := os.Stat(sourceBinPath); os.IsNotExist(err) {
			 sourceBinPath = filepath.Join(tempExtractDir, entries[0].Name())
		}

		if err := os.Rename(sourceBinPath, targetBinDir); err != nil {
			return fmt.Errorf("移动文件失败: %v", err)
		}
		
		os.RemoveAll(tempExtractDir)
		os.Remove(tempZipPath)

	} else {
		targetBinDir = filepath.Join(InstallRoot, pkg.Name)
		targetExePath := filepath.Join(InstallRoot, pkg.Name+".exe")
		if err := os.Rename(tempZipPath, targetExePath); err != nil {
			return err
		}
	}

	// 3. 配置环境变量
	absPath, _ := filepath.Abs(targetBinDir)
	fmt.Printf("🔗 正在配置环境变量...\n")
	if err := addToPath(absPath); err != nil {
		return fmt.Errorf("环境变量配置失败: %v", err)
	}

	return nil
}

// downloadFile 下载文件
func downloadFile(url, filepath string) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("服务器返回状态码: %d", resp.StatusCode)
	}

	out, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, resp.Body)
	return err
}

// unzip 解压 zip 文件
func unzip(src, dest string) error {
	r, err := zip.OpenReader(src)
	if err != nil {
		return err
	}
	defer r.Close()

	for _, f := range r.File {
		fpath := filepath.Join(dest, f.Name)
		if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
			return fmt.Errorf("非法文件路径: %s", fpath)
		}

		if f.FileInfo().IsDir() {
			os.MkdirAll(fpath, f.Mode())
			continue
		}

		if err = os.MkdirAll(filepath.Dir(fpath), f.Mode()); err != nil {
			return err
		}

		outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return err
		}

		rc, err := f.Open()
		if err != nil {
			outFile.Close()
			return err
		}

		_, err = io.Copy(outFile, rc)
		outFile.Close()
		rc.Close()

		if err != nil {
			return err
		}
	}
	return nil
}

// addToPath 添加环境变量
func addToPath(newPath string) error {
	key, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.ALL_ACCESS)
	if err != nil {
		return err
	}
	defer key.Close()

	currentPath, _, err := key.GetStringValue("Path")
	if err != nil && err != registry.ErrNotExist {
		return err
	}

	// 检查是否已存在
	paths := strings.Split(currentPath, ";")
	for _, p := range paths {
		if p == newPath {
			return nil // 已存在，无需添加
		}
	}

	var updatedPath string
	if currentPath == "" {
		updatedPath = newPath
	} else {
		updatedPath = currentPath + ";" + newPath
	}

	if err = key.SetStringValue("Path", updatedPath); err != nil {
		return err
	}

	// 广播消息
	notifyEnvChange()
	return nil
}

// removeFromPath 从环境变量中移除指定路径 (新增)
func removeFromPath(targetPath string) error {
	key, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.ALL_ACCESS)
	if err != nil {
		return err
	}
	defer key.Close()

	currentPath, _, err := key.GetStringValue("Path")
	if err != nil {
		return err
	}

	// 分割路径
	paths := strings.Split(currentPath, ";")
	var newPaths []string

	// 过滤掉目标路径
	for _, p := range paths {
		// 去除可能存在的首尾空格和反斜杠，确保精确匹配
		cleanP := strings.Trim(p, " \\")
		cleanTarget := strings.Trim(targetPath, " \\")
		
		if cleanP != cleanTarget {
			newPaths = append(newPaths, p)
		}
	}

	// 如果长度没变，说明没找到
	if len(newPaths) == len(paths) {
		return nil
	}

	// 重新拼接并写入
	updatedPath := strings.Join(newPaths, ";")
	if err = key.SetStringValue("Path", updatedPath); err != nil {
		return err
	}

	// 广播消息
	notifyEnvChange()
	return nil
}

// notifyEnvChange 广播环境变量变更消息
func notifyEnvChange() {
	user32 := syscall.NewLazyDLL("user32.dll")
	proc := user32.NewProc("SendMessageTimeoutW")
	const HWND_BROADCAST = 0xffff
	const WM_SETTINGCHANGE = 0x001A
	const SMTO_ABORTIFHUNG = 0x0002
	var result uintptr
	proc.Call(
		uintptr(HWND_BROADCAST),
		uintptr(WM_SETTINGCHANGE),
		uintptr(0),
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Environment"))),
		uintptr(SMTO_ABORTIFHUNG),
		uintptr(5000),
		uintptr(unsafe.Pointer(&result)),
	)
}