package sys

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"syscall"
	"time"
)

type ProgressCallback func(percent float64)

var (
	cachedNpmPrefix string
	cachedNodePath  string
	cachedGitPath   string
	prefixOnce      sync.Once
	nodePathOnce    sync.Once
	gitPathOnce     sync.Once
)

const (
	downloadConcurrentThreshold int64 = 20 * 1024 * 1024
	downloadConcurrentParts           = 4
	DefaultGatewayPort                = 18789
)

const gitProxyEnv = "GIT_PROXY"
const gitProxyDefault = "https://g.blfrp.cn/"

func gitProxy() string {
	proxy := strings.TrimSpace(os.Getenv(gitProxyEnv))
	if proxy == "" {
		proxy = gitProxyDefault
	}
	if !strings.HasSuffix(proxy, "/") {
		proxy += "/"
	}
	return proxy
}

// SHA256 来源 https://nodejs.org/dist/v24.13.0/SHASUMS256.txt.asc
const nodeMsiSHA256 = "1a5f0cd914386f3be2fbaf03ad9fff808a588ce50d2e155f338fad5530575f18"

// SHA256 来源 https://github.com/git-for-windows/git/releases/tag/v2.52.0.windows.1
const gitExeSHA256 = "d8de7a3152266c8bb13577eab850ea1df6dccf8c2aa48be5b4a1c58b7190d62c"

// OpenclawConfig 配置结构
type OpenclawConfig struct {
	Gateway  GatewayConfig     `json:"gateway"`
	Env      map[string]string `json:"env,omitempty"`
	Agents   AgentsConfig      `json:"agents"`
	Models   *ModelsConfig     `json:"models,omitempty"`
	Tools    ToolsConfig       `json:"tools"`
	Channels ChannelsConfig    `json:"channels"`
}

type GatewayConfig struct {
	Mode string      `json:"mode"`
	Bind string      `json:"bind"`
	Port int         `json:"port"`
	Auth *AuthConfig `json:"auth,omitempty"`
}

type AuthConfig struct {
	Mode  string `json:"mode,omitempty"`
	Token string `json:"token"`
}

type AgentsConfig struct {
	Defaults AgentDefaults `json:"defaults"`
}

type AgentDefaults struct {
	Model           ModelRef          `json:"model"`
	ElevatedDefault string            `json:"elevatedDefault,omitempty"`
	Compaction      *CompactionConfig `json:"compaction,omitempty"`
	MaxConcurrent   int               `json:"maxConcurrent,omitempty"`
}

type ModelRef struct {
	Primary string `json:"primary"`
}

type CompactionConfig struct {
	Mode string `json:"mode"`
}

type ModelsConfig struct {
	Mode      string                    `json:"mode"`
	Providers map[string]ProviderConfig `json:"providers"`
}

type ProviderConfig struct {
	BaseURL string       `json:"baseUrl"`
	APIKey  string       `json:"apiKey"`
	API     string       `json:"api"`
	Models  []ModelEntry `json:"models"`
}

type ModelEntry struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type ToolsConfig struct {
	Exec     *ExecConfig    `json:"exec,omitempty"`
	Elevated ElevatedConfig `json:"elevated"`
	Allow    []string       `json:"allow"`
}

type ExecConfig struct {
	BackgroundMs int  `json:"backgroundMs"`
	TimeoutSec   int  `json:"timeoutSec"`
	CleanupMs    int  `json:"cleanupMs"`
	NotifyOnExit bool `json:"notifyOnExit"`
}

type ElevatedConfig struct {
	Enabled   bool                `json:"enabled"`
	AllowFrom map[string][]string `json:"allowFrom"`
}

type ChannelsConfig struct {
	Telegram TelegramConfig `json:"telegram"`
}

type TelegramConfig struct {
	Enabled   bool     `json:"enabled"`
	BotToken  string   `json:"botToken"`
	DMPolicy  string   `json:"dmPolicy"`
	AllowFrom []string `json:"allowFrom"`
}

// GetOpenclawPath 获取执行路径
func GetOpenclawPath() (string, error) {
	if path, err := exec.LookPath("openclaw"); err == nil {
		return path, nil
	}
	// Legacy fallback
	if path, err := exec.LookPath("clawdbot"); err == nil {
		return path, nil
	}
	if path, err := exec.LookPath("moltbot"); err == nil {
		return path, nil
	}

	npmPrefix, err := getNpmPrefix()
	if err != nil {
		return "", err
	}

	possibleClawd := filepath.Join(npmPrefix, "openclaw.cmd")
	if _, err := os.Stat(possibleClawd); err == nil {
		return possibleClawd, nil
	}

	// Legacy checks
	possibleMolt := filepath.Join(npmPrefix, "moltbot.cmd")
	if _, err := os.Stat(possibleMolt); err == nil {
		return possibleMolt, nil
	}

	return "", fmt.Errorf("未找到 openclaw 可执行文件")
}

// GetNodePath 获取 Node 路径
func GetNodePath() (string, error) {
	var err error
	nodePathOnce.Do(func() {
		if path, e := exec.LookPath("node"); e == nil {
			cachedNodePath = path
			return
		}
		defaultPath := `C:\Program Files\nodejs\node.exe`
		if _, e := os.Stat(defaultPath); e == nil {
			cachedNodePath = defaultPath
			return
		}
		err = fmt.Errorf("未找到 Node.js")
	})
	if err != nil {
		return "", err
	}
	if cachedNodePath != "" {
		return cachedNodePath, nil
	}
	return "", fmt.Errorf("未找到 Node.js")
}

// GetGitPath 获取 Git 路径
func GetGitPath() (string, error) {
	var err error
	gitPathOnce.Do(func() {
		if path, e := exec.LookPath("git"); e == nil {
			cachedGitPath = path
			return
		}
		defaultPaths := []string{
			`C:\Program Files\Git\cmd\git.exe`,
			`C:\Program Files\Git\bin\git.exe`,
		}
		for _, p := range defaultPaths {
			if _, e := os.Stat(p); e == nil {
				cachedGitPath = p
				return
			}
		}
		err = fmt.Errorf("未找到 Git")
	})
	if err != nil {
		return "", err
	}
	if cachedGitPath != "" {
		return cachedGitPath, nil
	}
	return "", fmt.Errorf("未找到 Git")
}

// SetupNodeEnv 配置 Node 环境变量
func SetupNodeEnv() error {
	nodeExe, err := GetNodePath()
	if err != nil {
		return err
	}
	nodeDir := filepath.Dir(nodeExe)

	pathEnv := os.Getenv("PATH")
	if strings.Contains(strings.ToLower(pathEnv), strings.ToLower(nodeDir)) {
		return nil
	}

	newPath := nodeDir + string(os.PathListSeparator) + pathEnv

	if npmPrefix, err := getNpmPrefix(); err == nil {
		if !strings.Contains(strings.ToLower(newPath), strings.ToLower(npmPrefix)) {
			newPath = npmPrefix + string(os.PathListSeparator) + newPath
		}
	}

	return os.Setenv("PATH", newPath)
}

// SetupGitEnv 配置 Git 环境变量
func SetupGitEnv() error {
	gitExe, err := GetGitPath()
	if err != nil {
		return err
	}
	gitDir := filepath.Dir(gitExe)

	pathEnv := os.Getenv("PATH")
	if strings.Contains(strings.ToLower(pathEnv), strings.ToLower(gitDir)) {
		return nil
	}

	newPath := gitDir + string(os.PathListSeparator) + pathEnv
	return os.Setenv("PATH", newPath)
}

// CheckOpenclaw 检查安装状态
func CheckOpenclaw() (string, bool) {
	SetupNodeEnv()

	cmdName, err := GetOpenclawPath()
	if err != nil {
		return "", false
	}

	cmd := exec.Command("cmd", "/c", cmdName, "--version")
	out, err := cmd.Output()
	if err != nil {
		return "", false
	}
	return strings.TrimSpace(string(out)), true
}

// CheckNode 检查 Node 版本
func CheckNode() (string, bool) {
	nodePath, err := GetNodePath()
	if err != nil {
		return "", false
	}

	cmd := exec.Command(nodePath, "-v")
	out, err := cmd.Output()
	if err != nil {
		return "", false
	}

	versionStr := strings.TrimSpace(string(out))
	re := regexp.MustCompile(`v(\d+)\.`)
	matches := re.FindStringSubmatch(versionStr)
	if len(matches) < 2 {
		return versionStr, false
	}

	majorVer, err := strconv.Atoi(matches[1])
	if err != nil {
		return versionStr, false
	}

	if majorVer >= 22 {
		return versionStr, true
	}
	return versionStr, false
}

// CheckGit 检查 Git 状态
func CheckGit() (string, bool) {
	gitPath, err := GetGitPath()
	if err != nil {
		return "", false
	}

	cmd := exec.Command(gitPath, "--version")
	out, err := cmd.Output()
	if err != nil {
		return "", false
	}

	return strings.TrimSpace(string(out)), true
}

// getNpmPath 获取 npm
func getNpmPath() (string, error) {
	path, err := exec.LookPath("npm")
	if err == nil {
		return path, nil
	}
	defaultPath := `C:\Program Files\nodejs\npm.cmd`
	if _, err := os.Stat(defaultPath); err == nil {
		return defaultPath, nil
	}
	return "", fmt.Errorf("未找到 npm，请确认 Node.js 安装成功")
}

func getNpmPrefix() (string, error) {
	var err error
	prefixOnce.Do(func() {
		npmPath, e := getNpmPath()
		if e != nil {
			err = fmt.Errorf("无法定位 npm: %v", e)
			return
		}
		cmd := exec.Command(npmPath, "config", "get", "prefix")
		out, e := cmd.Output()
		if e != nil {
			err = fmt.Errorf("无法获取 npm prefix: %v", e)
			return
		}
		cachedNpmPrefix = strings.TrimSpace(string(out))
	})
	if err != nil {
		return "", err
	}
	return cachedNpmPrefix, nil
}

// checkIsCNLocation Checks if the current IP location is in China
func checkIsCNLocation() bool {
	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Get("https://www.cloudflare.com/cdn-cgi/trace")
	if err != nil {
		// If the request fails, assume CN to be safe and ensure accessibility
		return true
	}
	defer resp.Body.Close()

	scanner := bufio.NewScanner(resp.Body)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if line == "loc=CN" {
			return true
		}
	}
	return false
}

func ResetPathCache() {
	cachedNpmPrefix = ""
	cachedNodePath = ""
	cachedGitPath = ""
	prefixOnce = sync.Once{}
	nodePathOnce = sync.Once{}
	gitPathOnce = sync.Once{}
}

// ConfigureNpmMirror 配置镜像
func ConfigureNpmMirror() error {
	npmPath, err := getNpmPath()
	if err != nil {
		return err
	}
	cmd := exec.Command(npmPath, "config", "set", "registry", "https://registry.npmmirror.com/")
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("设置 npm 镜像失败: %v", err)
	}
	return nil
}

func ConfigureGitProxy() error {
	if !checkIsCNLocation() {
		return nil
	}

	var lastErr error
	for i := 0; i < 3; i++ {
		ResetPathCache()
		gitPath, err := GetGitPath()
		if err != nil {
			lastErr = err
		} else {
			proxy := gitProxy()
			key := fmt.Sprintf("url.%shttps://github.com/.insteadOf", proxy)
			cmd := exec.Command(gitPath, "config", "--global", key, "https://github.com/")
			if err := cmd.Run(); err == nil {
				return nil
			} else {
				lastErr = fmt.Errorf("设置 git 代理失败: %v", err)
			}
		}
		time.Sleep(300 * time.Millisecond)
	}
	return lastErr
}

// RemoveGitProxy 取消 Git 代理
func RemoveGitProxy() error {
	gitPath, err := GetGitPath()
	if err != nil {
		return nil
	}

	proxy := gitProxy()
	key := fmt.Sprintf("url.%shttps://github.com/.insteadOf", proxy)
	
	// Unset the proxy configuration
	cmd := exec.Command(gitPath, "config", "--global", "--unset", key)
	return cmd.Run()
}

// downloadFile 下载文件
func downloadFile(url, dest, expectedSHA256 string, onProgress ProgressCallback) error {
	if ok, err := verifyFileSHA256(dest, expectedSHA256); err == nil && ok {
		return nil
	}

	partPath := dest + ".part"
	if ok, err := verifyFileSHA256(partPath, expectedSHA256); err == nil && ok {
		_ = os.Remove(dest)
		return os.Rename(partPath, dest)
	}

	_ = os.Remove(dest)

	size, acceptRanges, err := probeRemoteFile(url)
	if err != nil {
		return err
	}

	if err := downloadWithResume(url, partPath, size, acceptRanges, onProgress); err != nil {
		return err
	}

	if ok, err := verifyFileSHA256(partPath, expectedSHA256); err != nil || !ok {
		_ = os.Remove(partPath)
		if err != nil {
			return err
		}
		return fmt.Errorf("下载文件校验失败")
	}

	_ = os.Remove(dest)
	return os.Rename(partPath, dest)
}

func downloadWithResume(url, dest string, size int64, acceptRanges bool, onProgress ProgressCallback) error {
	if size > 0 && acceptRanges {
		if info, err := os.Stat(dest); err == nil && info.Size() > 0 && info.Size() < size {
			return downloadRange(url, dest, info.Size(), size-1, size, onProgress)
		}
		if size >= downloadConcurrentThreshold {
			return downloadConcurrent(url, dest, size, downloadConcurrentParts, onProgress)
		}
	}
	return downloadRange(url, dest, 0, -1, size, onProgress)
}

func downloadRange(url, dest string, start, end, total int64, onProgress ProgressCallback) error {
	out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return fmt.Errorf("创建文件失败: %v", err)
	}
	defer out.Close()

	if start > 0 {
		if _, err := out.Seek(start, 0); err != nil {
			return fmt.Errorf("定位文件失败: %v", err)
		}
	}

	client := &http.Client{Timeout: 30 * time.Minute}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return fmt.Errorf("创建请求失败: %v", err)
	}
	if start > 0 || end >= 0 {
		if end >= start && end >= 0 {
			req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end))
		} else {
			req.Header.Set("Range", fmt.Sprintf("bytes=%d-", start))
		}
	}

	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("下载失败: %v", err)
	}
	defer resp.Body.Close()

	if start > 0 && resp.StatusCode != http.StatusPartialContent {
		return fmt.Errorf("不支持断点续传，状态码: %d", resp.StatusCode)
	}
	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
		return fmt.Errorf("下载失败，状态码: %d", resp.StatusCode)
	}

	if total <= 0 && resp.ContentLength > 0 {
		total = start + resp.ContentLength
	}

	reader := &countingReader{
		r: resp.Body,
		update: func(n int64) {
			if total > 0 && onProgress != nil {
				current := start + n
				percent := float64(current) * 100 / float64(total)
				onProgress(percent)
			}
		},
	}

	if _, err = io.Copy(out, reader); err != nil {
		return fmt.Errorf("写入文件失败: %v", err)
	}
	return nil
}

func downloadConcurrent(url, dest string, size int64, parts int, onProgress ProgressCallback) error {
	if parts < 2 {
		return downloadRange(url, dest, 0, -1, size, onProgress)
	}

	out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return fmt.Errorf("创建文件失败: %v", err)
	}
	if err := out.Truncate(size); err != nil {
		out.Close()
		return fmt.Errorf("预分配文件失败: %v", err)
	}

	var wg sync.WaitGroup
	errCh := make(chan error, parts)
	
	// Shared progress tracking
	var written int64
	
	partSize := size / int64(parts)
	for i := 0; i < parts; i++ {
		start := int64(i) * partSize
		end := start + partSize - 1
		if i == parts-1 {
			end = size - 1
		}

		wg.Add(1)
		go func(s, e int64) {
			defer wg.Done()
			client := &http.Client{Timeout: 30 * time.Minute}
			req, err := http.NewRequest("GET", url, nil)
			if err != nil {
				errCh <- fmt.Errorf("创建请求失败: %v", err)
				return
			}
			req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", s, e))
			resp, err := client.Do(req)
			if err != nil {
				errCh <- fmt.Errorf("下载失败: %v", err)
				return
			}
			defer resp.Body.Close()
			if resp.StatusCode != http.StatusPartialContent {
				errCh <- fmt.Errorf("分段下载失败，状态码: %d", resp.StatusCode)
				return
			}
			
			writer := &writeAtWriter{
				file: out,
				offset: s,
				update: func(n int) {
					newVal := atomic.AddInt64(&written, int64(n))
					if onProgress != nil {
						percent := float64(newVal) * 100 / float64(size)
						onProgress(percent)
					}
				},
			}
			
			if _, err := io.Copy(writer, resp.Body); err != nil {
				errCh <- fmt.Errorf("写入文件失败: %v", err)
				return
			}
		}(start, end)
	}

	wg.Wait()
	close(errCh)
	out.Close()

	for err := range errCh {
		if err != nil {
			return err
		}
	}
	return nil
}

type writeAtWriter struct {
	file   *os.File
	offset int64
	update func(int)
}

func (w *writeAtWriter) Write(p []byte) (int, error) {
	n, err := w.file.WriteAt(p, w.offset)
	w.offset += int64(n)
	if w.update != nil && n > 0 {
		w.update(n)
	}
	return n, err
}

type countingReader struct {
	r       io.Reader
	totalRead int64
	update  func(int64)
}

func (c *countingReader) Read(p []byte) (int, error) {
	n, err := c.r.Read(p)
	if n > 0 {
		c.totalRead += int64(n)
		if c.update != nil {
			c.update(c.totalRead)
		}
	}
	return n, err
}

func probeRemoteFile(url string) (int64, bool, error) {
	client := &http.Client{Timeout: 30 * time.Second}
	req, err := http.NewRequest("HEAD", url, nil)
	if err == nil {
		resp, err := client.Do(req)
		if err == nil {
			resp.Body.Close()
			size := resp.ContentLength
			acceptRanges := strings.Contains(strings.ToLower(resp.Header.Get("Accept-Ranges")), "bytes")
			if size > 0 && acceptRanges {
				return size, acceptRanges, nil
			}
		}
	}

	req, err = http.NewRequest("GET", url, nil)
	if err != nil {
		return 0, false, fmt.Errorf("创建请求失败: %v", err)
	}
	req.Header.Set("Range", "bytes=0-0")
	resp, err := client.Do(req)
	if err != nil {
		return 0, false, fmt.Errorf("探测下载失败: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusPartialContent {
		return -1, false, nil
	}

	total := parseContentRangeTotal(resp.Header.Get("Content-Range"))
	return total, true, nil
}

func parseContentRangeTotal(value string) int64 {
	parts := strings.Split(value, "/")
	if len(parts) != 2 {
		return -1
	}
	totalStr := strings.TrimSpace(parts[1])
	if totalStr == "*" {
		return -1
	}
	total, err := strconv.ParseInt(totalStr, 10, 64)
	if err != nil {
		return -1
	}
	return total
}

func verifyFileSHA256(path, expected string) (bool, error) {
	if expected == "" {
		return true, nil
	}
	info, err := os.Stat(path)
	if err != nil || info.Size() == 0 {
		return false, err
	}
	sum, err := fileSHA256(path)
	if err != nil {
		return false, err
	}
	return strings.EqualFold(sum, expected), nil
}

func fileSHA256(path string) (string, error) {
	f, err := os.Open(path)
	if err != nil {
		return "", fmt.Errorf("打开文件失败: %v", err)
	}
	defer f.Close()

	hasher := sha256.New()
	if _, err := io.Copy(hasher, f); err != nil {
		return "", fmt.Errorf("读取文件失败: %v", err)
	}
	return hex.EncodeToString(hasher.Sum(nil)), nil
}

// InstallNode 安装 Node.js
func InstallNode(onProgress ProgressCallback) error {
	if _, ok := CheckNode(); ok {
		return nil
	}

	msiUrl := "https://nodejs.org/dist/v24.13.0/node-v24.13.0-x64.msi"
	tempDir := os.TempDir()
	msiPath := filepath.Join(tempDir, "node-v24.13.0-x64.msi")

	if err := downloadFile(msiUrl, msiPath, nodeMsiSHA256, onProgress); err != nil {
		return err
	}
	
	for i := 0; i < 3; i++ {
		installCmd := exec.Command("msiexec", "/i", msiPath, "/qn")
		output, err := installCmd.CombinedOutput()
		if err == nil {
			break
		}
		
		outStr := string(output)
		if strings.Contains(outStr, "1618") {
			time.Sleep(5 * time.Second)
			continue
		}
		
		if strings.Contains(outStr, "1619") {
			return fmt.Errorf("安装包损坏 (Error 1619). 请尝试手动下载: %s", msiUrl)
		}
		
		if i == 2 {
			return fmt.Errorf("安装失败: %v, Output: %s", err, outStr)
		}
		time.Sleep(2 * time.Second)
	}

	SetupNodeEnv()
	return nil
}

// InstallGit 安装 Git
func InstallGit(onProgress ProgressCallback) error {
	if _, ok := CheckGit(); ok {
		return nil
	}

	gitUrl := fmt.Sprintf("%sgithub.com/git-for-windows/git/releases/download/v2.52.0.windows.1/Git-2.52.0-64-bit.exe", gitProxy())
	tempDir := os.TempDir()
	exePath := filepath.Join(tempDir, "Git-2.52.0-64-bit.exe")

	if err := downloadFile(gitUrl, exePath, gitExeSHA256, onProgress); err != nil {
		return fmt.Errorf("git 下载失败: %v", err)
	}

	installCmd := exec.Command(exePath,
		"/VERYSILENT",
		"/NORESTART",
		"/NOCANCEL",
		"/SP-",
		"/CLOSEAPPLICATIONS",
		"/RESTARTAPPLICATIONS",
		"/o:PathOption=Cmd",
	)
	
	if out, err := installCmd.CombinedOutput(); err != nil {
		return fmt.Errorf("git 安装失败: %v, Output: %s", err, string(out))
	}

	ResetPathCache()
	SetupGitEnv()
	return nil
}

// InstallOpenclawNpm 安装包
func InstallOpenclawNpm(tag string, onProgress ProgressCallback) error {
	SetupNodeEnv()

	pkgName := "openclaw"
	if tag == "" || tag == "beta" {
		tag = "latest"
	}

	npmPath, err := getNpmPath()
	if err != nil {
		return err
	}

	os.Setenv("NPM_CONFIG_LOGLEVEL", "error")
	os.Setenv("NPM_CONFIG_UPDATE_NOTIFIER", "false")
	os.Setenv("NPM_CONFIG_FUND", "false")
	os.Setenv("NPM_CONFIG_AUDIT", "false")

	cmd := exec.Command(npmPath, "install", "-g", fmt.Sprintf("%s@%s", pkgName, tag))
	cmd.Stdout = nil
	cmd.Stderr = nil
	if err := cmd.Run(); err != nil {
		return err
	}

	return nil
}

// EnsureOnPath 检查并配置 PATH
func EnsureOnPath() (bool, error) {
	if _, err := exec.LookPath("openclaw"); err == nil {
		return false, nil
	}
	if _, err := exec.LookPath("clawdbot"); err == nil {
		return false, nil
	}

	npmPrefix, err := getNpmPrefix()
	if err != nil {
		return false, err
	}
	npmBin := filepath.Join(npmPrefix, "bin")

	possiblePath := npmPrefix

	if _, err := os.Stat(filepath.Join(npmPrefix, "openclaw.cmd")); os.IsNotExist(err) {
		if _, err := os.Stat(filepath.Join(npmPrefix, "clawdbot.cmd")); os.IsNotExist(err) {
			possiblePath = npmBin
		}
	}

	psCmd := fmt.Sprintf(`
		$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
		if (-not ($userPath -split ";" | Where-Object { $_ -ieq "%s" })) {
			[Environment]::SetEnvironmentVariable("Path", "$userPath;%s", "User")
		}
	`, possiblePath, possiblePath)

	exec.Command("powershell", "-Command", psCmd).Run()

	return true, nil
}

// RunDoctor 运行诊断
func RunDoctor() error {
	cmdName, err := GetOpenclawPath()
	if err != nil {
		cmdName = "openclaw"
	}

	cmd := exec.Command("cmd", "/c", cmdName, "doctor", "--non-interactive")
	return cmd.Run()
}

// RunOnboard 运行引导
func RunOnboard() error {
	cmdName, err := GetOpenclawPath()
	if err != nil {
		cmdName = "openclaw"
	}
	cmd := exec.Command("cmd", "/c", cmdName, "onboard")
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// ConfigOptions 配置选项
type ConfigOptions struct {
	ApiType       string
	BotToken      string
	AdminID       string
	AnthropicKey  string
	OpenAIBaseURL string
	OpenAIKey     string
	OpenAIModel   string
}

// GenerateAndWriteConfig 生成配置
func GenerateAndWriteConfig(opts ConfigOptions) error {
	userHome, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("无法获取用户目录: %v", err)
	}
	configDir := filepath.Join(userHome, ".openclaw")
	if err := os.MkdirAll(configDir, 0755); err != nil {
		return fmt.Errorf("创建配置目录失败: %v", err)
	}
	configFile := filepath.Join(configDir, "openclaw.json")

	// 生成随机 Token
	tokenBytes := make([]byte, 16)
	if _, err := io.ReadFull(rand.Reader, tokenBytes); err != nil {
		// 降级方案
		copy(tokenBytes, []byte(fmt.Sprintf("%d", time.Now().UnixNano())))
	}
	token := hex.EncodeToString(tokenBytes)

	config := OpenclawConfig{
		Gateway: GatewayConfig{
			Mode: "local",
			Bind: "loopback",
			Port: DefaultGatewayPort,
			Auth: &AuthConfig{
				Mode:  "token",
				Token: token,
			},
		},
		Tools: ToolsConfig{
			Elevated: ElevatedConfig{
				Enabled:   true,
				AllowFrom: map[string][]string{},
			},
			Allow: []string{"exec", "process", "read", "write", "edit", "web_search", "web_fetch", "cron"},
		},
		Channels: ChannelsConfig{
			Telegram: TelegramConfig{
				Enabled:   false,
				DMPolicy:  "pairing",
				AllowFrom: []string{},
			},
		},
	}

	if opts.BotToken != "" {
		config.Channels.Telegram.Enabled = true
		config.Channels.Telegram.BotToken = opts.BotToken
		if opts.AdminID != "" {
			config.Channels.Telegram.AllowFrom = []string{opts.AdminID}
			config.Tools.Elevated.AllowFrom["telegram"] = []string{opts.AdminID}
		}
	}

	if opts.ApiType == "anthropic" {
		config.Env = map[string]string{
			"ANTHROPIC_API_KEY": opts.AnthropicKey,
		}
		config.Agents = AgentsConfig{
			Defaults: AgentDefaults{
				Model: ModelRef{
					Primary: "anthropic/claude-opus-4-5",
				},
			},
		}
	} else if opts.ApiType == "skip" {
		config.Channels.Telegram.Enabled = false
		config.Agents = AgentsConfig{
			Defaults: AgentDefaults{
				Model: ModelRef{
					Primary: "anthropic/claude-opus-4-5",
				},
			},
		}
	} else {
		config.Agents = AgentsConfig{
			Defaults: AgentDefaults{
				Model: ModelRef{
					Primary: fmt.Sprintf("openai-compat/%s", opts.OpenAIModel),
				},
				ElevatedDefault: "full",
				Compaction: &CompactionConfig{
					Mode: "safeguard",
				},
				MaxConcurrent: 4,
			},
		}
		config.Models = &ModelsConfig{
			Mode: "merge",
			Providers: map[string]ProviderConfig{
				"openai-compat": {
					BaseURL: opts.OpenAIBaseURL,
					APIKey:  opts.OpenAIKey,
					API:     "openai-completions",
					Models: []ModelEntry{
						{ID: opts.OpenAIModel, Name: opts.OpenAIModel},
					},
				},
			},
		}
		config.Tools.Exec = &ExecConfig{
			BackgroundMs: 10000,
			TimeoutSec:   1800,
			CleanupMs:    1800000,
			NotifyOnExit: true,
		}
	}

	data, err := json.MarshalIndent(config, "", "  ")
	if err != nil {
		return fmt.Errorf("序列化配置失败: %v", err)
	}

	if err := os.WriteFile(configFile, data, 0644); err != nil {
		return err
	}

	return nil
}

// GetConfigPath 获取配置文件路径
func GetConfigPath() (string, error) {
	userHome, err := os.UserHomeDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(userHome, ".openclaw", "openclaw.json"), nil
}

func loadConfig() (*OpenclawConfig, error) {
	configPath, err := GetConfigPath()
	if err != nil {
		return nil, err
	}
	data, err := os.ReadFile(configPath)
	if err != nil {
		return nil, err
	}
	var config OpenclawConfig
	if err := json.Unmarshal(data, &config); err != nil {
		return nil, err
	}
	return &config, nil
}

// GetGatewayPort 获取网关端口
func GetGatewayPort() int {
	config, err := loadConfig()
	if err != nil {
		return DefaultGatewayPort
	}
	if config.Gateway.Port > 0 {
		return config.Gateway.Port
	}
	return DefaultGatewayPort
}

// GetGatewayToken 获取 Gateway Token
func GetGatewayToken() (string, error) {
	configPath, err := GetConfigPath()
	if err != nil {
		return "", err
	}
	data, err := os.ReadFile(configPath)
	if err != nil {
		return "", err
	}
	var config OpenclawConfig
	if err := json.Unmarshal(data, &config); err != nil {
		return "", err
	}
	if config.Gateway.Auth != nil {
		return config.Gateway.Auth.Token, nil
	}
	return "", nil
}

// StartGateway 启动网关
func StartGateway() error {
	cmdName, err := GetOpenclawPath()
	if err != nil {
		cmdName = "openclaw"
	}

	cmd := exec.Command(cmdName, "gateway", "--verbose")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		HideWindow:    true,
		CreationFlags: 0x08000000,
	}

	return cmd.Start()
}

// IsGatewayRunning 检查端口
func IsGatewayRunning() bool {
	port := GetGatewayPort()
	conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), 500*time.Millisecond)
	if err == nil {
		conn.Close()
		return true
	}
	return false
}

// KillGateway 停止网关
func KillGateway() error {
	port := GetGatewayPort()
	cmd := exec.Command("netstat", "-ano")
	cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	out, err := cmd.Output()
	if err != nil {
		return err
	}

	scanner := bufio.NewScanner(bytes.NewReader(out))
	var pid string
	portStr := fmt.Sprintf(":%d", port)
	for scanner.Scan() {
		line := scanner.Text()
		if strings.Contains(line, portStr) && strings.Contains(line, "LISTENING") {
			fields := strings.Fields(line)
			if len(fields) > 0 {
				pid = fields[len(fields)-1]
				break
			}
		}
	}

	if pid == "" {
		return nil
	}

	killCmd := exec.Command("taskkill", "/F", "/PID", pid)
	killCmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	return killCmd.Run()
}

// UninstallOpenclaw 卸载清理
func UninstallOpenclaw() error {
	npmPath, err := getNpmPath()
	if err != nil {
		return err
	}

	packages := []string{"openclaw", "clawdbot"}
	for _, pkg := range packages {
		cmd := exec.Command(npmPath, "uninstall", "-g", pkg)
		cmd.Stdout = nil
		cmd.Stderr = nil
		cmd.Run()
	}

	RemoveGitProxy()

	userHome, err := os.UserHomeDir()
	if err == nil {
		configDir := filepath.Join(userHome, ".openclaw")
		os.RemoveAll(configDir)

		legacyDir := filepath.Join(userHome, ".clawdbot")
		os.RemoveAll(legacyDir)
	}

	return nil
}
