package utils

import (
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
)

var (
	defaultShell string
	defaultArgs  []string
	shellOnce    sync.Once
)

// GetShell 返回当前操作系统的 shell 和参数
func GetShell() (shell string, args []string) {
	shellOnce.Do(func() {
		if runtime.GOOS == "windows" {
			defaultShell = "cmd"
			defaultArgs = []string{}
			return
		}

		// 1. 优先在 PATH 中查找 bash
		if path, err := exec.LookPath("bash"); err == nil {
			defaultShell = path
			defaultArgs = []string{}
			return
		}

		// 2. 其次使用环境变量中的 SHELL
		if envShell := os.Getenv("SHELL"); envShell != "" {
			if _, err := os.Stat(envShell); err == nil {
				defaultShell = envShell
				defaultArgs = []string{}
				return
			}
		}

		// 3. 尝试在 PATH 中查找 zsh 或 sh
		for _, s := range []string{"zsh", "sh"} {
			if path, err := exec.LookPath(s); err == nil {
				defaultShell = path
				defaultArgs = []string{}
				return
			}
		}

		// 4. 最后回退到硬编码路径
		shells := []string{"/usr/bin/bash", "/bin/bash", "/usr/bin/sh", "/bin/sh"}
		for _, sh := range shells {
			if _, err := os.Stat(sh); err == nil {
				defaultShell = sh
				defaultArgs = []string{}
				return
			}
		}

		defaultShell = "sh"
		defaultArgs = []string{}
	})

	return defaultShell, defaultArgs
}

// GetShellCommand 返回执行命令的 shell 和参数
func GetShellCommand(command string) (shell string, args []string) {
	shell, _ = GetShell()
	if runtime.GOOS == "windows" {
		return shell, []string{"/c", command}
	}
	return shell, []string{"-c", command}
}

// NewShellCmd 创建一个交互式 shell 命令
func NewShellCmd() *exec.Cmd {
	shell, _ := GetShell()
	if runtime.GOOS == "windows" {
		return exec.Command(shell)
	}
	// Unix 系统使用 -i 启用交互模式
	return exec.Command(shell, "-i")
}

// NewShellCommandCmd 创建一个执行指定命令的 shell 命令
func NewShellCommandCmd(command string) *exec.Cmd {
	shell, args := GetShellCommand(command)
	return exec.Command(shell, args...)
}

// QuotePath 转义并包裹路径，防止 Shell 注入
func QuotePath(path string) string {
	if path == "" {
		return "''"
	}
	// 在 Unix-like 系统中，单引号包裹是最安全的
	// 需要将路径中的 ' 替换为 '\'' (结束当前引号，转义一个单引号，重新开启引号)
	return "'" + strings.ReplaceAll(path, "'", "'\\''") + "'"
}
