package installer

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/muyue/muyue/internal/config"
	"github.com/muyue/muyue/internal/platform"
)

type InstallResult struct {
	Tool    string
	Success bool
	Message string
}

type Installer struct {
	config *config.MuyueConfig
	system platform.SystemInfo
}

func New(cfg *config.MuyueConfig) *Installer {
	return &Installer{
		config: cfg,
		system: platform.Detect(),
	}
}

func (i *Installer) InstallTool(name string) InstallResult {
	switch name {
	case "crush":
		return i.installCrush()
	case "claude":
		return i.installClaudeCode()
	case "bmad":
		return i.installBMAD()
	case "starship":
		return i.installStarship()
	case "go":
		return i.installGo()
	case "node":
		return i.installNode()
	case "python":
		return i.installPython()
	case "git":
		return i.installGit()
	case "pnpm":
		return i.installPnpm()
	case "uv":
		return i.installUv()
	case "docker":
		return i.installDocker()
	case "gh":
		return i.installGh()
	default:
		return InstallResult{Tool: name, Success: false, Message: "unknown tool"}
	}
}

func (i *Installer) InstallAll(missing []string) []InstallResult {
	var results []InstallResult
	for _, name := range missing {
		results = append(results, i.InstallTool(name))
	}
	return results
}

func (i *Installer) installCrush() InstallResult {
	if _, err := exec.LookPath("crush"); err == nil {
		return InstallResult{Tool: "crush", Success: true, Message: "already installed"}
	}

	var cmd *exec.Cmd
	switch i.system.OS {
	case platform.Linux:
		cmd = exec.Command("bash", "-c",
			"curl -fsSL https://github.com/charmbracelet/crush/releases/latest/download/crush_"+runtime.GOOS+"_"+runtime.GOARCH+".tar.gz | tar xz -C /usr/local/bin")
	case platform.MacOS:
		cmd = exec.Command("bash", "-c", "brew install charmbracelet/tap/crush")
	case platform.Windows:
		cmd = exec.Command("powershell", "-Command",
			"winget install charmbracelet.crush")
	default:
		return InstallResult{Tool: "crush", Success: false, Message: "unsupported OS"}
	}

	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{
			Tool: "crush", Success: false,
			Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
		}
	}

	return InstallResult{Tool: "crush", Success: true, Message: "installed"}
}

func (i *Installer) installClaudeCode() InstallResult {
	if _, err := exec.LookPath("claude"); err == nil {
		return InstallResult{Tool: "claude", Success: true, Message: "already installed"}
	}

	cmd := exec.Command("npm", "install", "-g", "@anthropic-ai/claude-code")
	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{
			Tool: "claude", Success: false,
			Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
		}
	}

	return InstallResult{Tool: "claude", Success: true, Message: "installed"}
}

func (i *Installer) installBMAD() InstallResult {
	if _, err := exec.LookPath("npx"); err != nil {
		return InstallResult{Tool: "bmad", Success: false, Message: "npx not found, install node first"}
	}

	configDir, err := config.ConfigDir()
	if err != nil {
		return InstallResult{Tool: "bmad", Success: false, Message: err.Error()}
	}

	bmadDir := filepath.Join(configDir, "bmad")
	os.MkdirAll(bmadDir, 0755)

	cmd := exec.Command("npx", "bmad-method@latest", "install",
		"--directory", bmadDir, "--yes")
	cmd.Env = append(os.Environ(), "npm_config_yes=true")

	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{
			Tool: "bmad", Success: false,
			Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
		}
	}

	i.config.BMAD.Installed = true
	i.config.BMAD.Global = true
	return InstallResult{Tool: "bmad", Success: true, Message: "installed globally in ~/.muyue/bmad"}
}

func (i *Installer) installStarship() InstallResult {
	if _, err := exec.LookPath("starship"); err == nil {
		return InstallResult{Tool: "starship", Success: true, Message: "already installed"}
	}

	var cmd *exec.Cmd
	switch i.system.OS {
	case platform.Linux, platform.MacOS:
		cmd = exec.Command("bash", "-c",
			"curl -sS https://starship.rs/install.sh | sh -s -- -y")
	case platform.Windows:
		cmd = exec.Command("powershell", "-Command",
			"winget install Starship.Starship")
	default:
		return InstallResult{Tool: "starship", Success: false, Message: "unsupported OS"}
	}

	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{
			Tool: "starship", Success: false,
			Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
		}
	}

	return InstallResult{Tool: "starship", Success: true, Message: "installed"}
}

func (i *Installer) installGo() InstallResult {
	if _, err := exec.LookPath("go"); err == nil {
		return InstallResult{Tool: "go", Success: true, Message: "already installed"}
	}

	home, _ := os.UserHomeDir()
	goDir := filepath.Join(home, ".local", "go")

	cmd := exec.Command("bash", "-c", fmt.Sprintf(
		"curl -sL https://go.dev/dl/go1.24.3.%s-%s.tar.gz | tar -C %s/.local -xzf -",
		runtime.GOOS, runtime.GOARCH, home,
	))

	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{
			Tool: "go", Success: false,
			Message: fmt.Sprintf("install failed: %s: %s", err, string(output)),
		}
	}

	rcFile := i.getRCFile()
	appendLine(rcFile, "export PATH="+goDir+"/bin:$PATH")

	return InstallResult{Tool: "go", Success: true, Message: "installed in ~/.local/go"}
}

func (i *Installer) installNode() InstallResult {
	if _, err := exec.LookPath("node"); err == nil {
		return InstallResult{Tool: "node", Success: true, Message: "already installed"}
	}

	switch i.system.OS {
	case platform.Linux:
		cmd := exec.Command("bash", "-c",
			"curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && apt-get install -y nodejs")
		if output, err := cmd.CombinedOutput(); err != nil {
			return InstallResult{Tool: "node", Success: false,
				Message: fmt.Sprintf("install failed: %s", string(output))}
		}
	case platform.MacOS:
		cmd := exec.Command("brew", "install", "node")
		if output, err := cmd.CombinedOutput(); err != nil {
			return InstallResult{Tool: "node", Success: false,
				Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
	case platform.Windows:
		cmd := exec.Command("winget", "install", "OpenJS.NodeJS.LTS")
		if output, err := cmd.CombinedOutput(); err != nil {
			return InstallResult{Tool: "node", Success: false,
				Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
	default:
		return InstallResult{Tool: "node", Success: false, Message: "unsupported OS"}
	}

	return InstallResult{Tool: "node", Success: true, Message: "installed"}
}

func (i *Installer) installPython() InstallResult {
	if _, err := exec.LookPath("python3"); err == nil {
		return InstallResult{Tool: "python", Success: true, Message: "already installed"}
	}

	switch i.system.PackageManager {
	case "apt":
		if output, err := exec.Command("apt", "install", "-y", "python3", "python3-pip").CombinedOutput(); err != nil {
			return InstallResult{Tool: "python", Success: false,
				Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
	case "brew":
		if output, err := exec.Command("brew", "install", "python3").CombinedOutput(); err != nil {
			return InstallResult{Tool: "python", Success: false,
				Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
	case "winget":
		if output, err := exec.Command("winget", "install", "Python.Python.3.12").CombinedOutput(); err != nil {
			return InstallResult{Tool: "python", Success: false,
				Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
	default:
		return InstallResult{Tool: "python", Success: false, Message: fmt.Sprintf("install via '%s' not supported", i.system.PackageManager)}
	}

	return InstallResult{Tool: "python", Success: true, Message: "installed"}
}

func (i *Installer) installGit() InstallResult {
	if _, err := exec.LookPath("git"); err == nil {
		return InstallResult{Tool: "git", Success: true, Message: "already installed"}
	}

	switch i.system.PackageManager {
	case "apt":
		if output, err := exec.Command("apt", "install", "-y", "git").CombinedOutput(); err != nil {
			return InstallResult{Tool: "git", Success: false,
				Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
	case "brew":
		if output, err := exec.Command("brew", "install", "git").CombinedOutput(); err != nil {
			return InstallResult{Tool: "git", Success: false,
				Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
	case "winget":
		if output, err := exec.Command("winget", "install", "Git.Git").CombinedOutput(); err != nil {
			return InstallResult{Tool: "git", Success: false,
				Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
	default:
		return InstallResult{Tool: "git", Success: false, Message: fmt.Sprintf("install via '%s' not supported", i.system.PackageManager)}
	}

	if i.config.Profile.Name != "" {
		exec.Command("git", "config", "--global", "user.name", i.config.Profile.Name).Run()
	}
	if i.config.Profile.Email != "" {
		exec.Command("git", "config", "--global", "user.email", i.config.Profile.Email).Run()
	}

	return InstallResult{Tool: "git", Success: true, Message: "installed and configured"}
}


	func (i *Installer) getRCFile() string {
	home, _ := os.UserHomeDir()
	switch i.system.Shell {
	case "zsh":
		return filepath.Join(home, ".zshrc")
	case "fish":
		return filepath.Join(home, ".config", "fish", "config.fish")
	default:
		return filepath.Join(home, ".bashrc")
	}
}

func appendLine(file, line string) {
	data, _ := os.ReadFile(file)
	if strings.Contains(string(data), line) {
		return
	}
	f, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return
	}
	defer f.Close()
	f.WriteString("\n" + line + "\n")
}

func (i *Installer) installPnpm() InstallResult {
	if _, err := exec.LookPath("pnpm"); err == nil {
		return InstallResult{Tool: "pnpm", Success: true, Message: "already installed"}
	}
	cmd := exec.Command("npm", "install", "-g", "pnpm")
	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{Tool: "pnpm", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
	}
	return InstallResult{Tool: "pnpm", Success: true, Message: "installed"}
}

func (i *Installer) installUv() InstallResult {
	if _, err := exec.LookPath("uv"); err == nil {
		return InstallResult{Tool: "uv", Success: true, Message: "already installed"}
	}
	var cmd *exec.Cmd
	switch i.system.OS {
	case platform.Linux, platform.MacOS:
		home, _ := os.UserHomeDir()
		cmd = exec.Command("bash", "-c", "curl -LsSf https://astral.sh/uv/install.sh | sh")
		if output, err := cmd.CombinedOutput(); err != nil {
			return InstallResult{Tool: "uv", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
		}
		rcFile := i.getRCFile()
		appendLine(rcFile, "export PATH="+filepath.Join(home, ".local", "bin")+":$PATH")
		return InstallResult{Tool: "uv", Success: true, Message: "installed (added ~/.local/bin to PATH)"}
	case platform.Windows:
		cmd = exec.Command("powershell", "-Command", "irm https://astral.sh/uv/install.ps1 | iex")
	default:
		return InstallResult{Tool: "uv", Success: false, Message: "unsupported OS"}
	}
	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{Tool: "uv", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
	}
	return InstallResult{Tool: "uv", Success: true, Message: "installed"}
}

func (i *Installer) installDocker() InstallResult {
	if _, err := exec.LookPath("docker"); err == nil {
		return InstallResult{Tool: "docker", Success: true, Message: "already installed"}
	}
	var cmd *exec.Cmd
	switch i.system.PackageManager {
	case "apt":
		cmd = exec.Command("bash", "-c", "apt-get update && apt-get install -y docker.io && systemctl start docker && systemctl enable docker")
	case "pacman":
		cmd = exec.Command("bash", "-c", "pacman -S --noconfirm docker && systemctl start docker && systemctl enable docker")
	case "dnf":
		cmd = exec.Command("bash", "-c", "dnf install -y docker && systemctl start docker && systemctl enable docker")
	case "brew":
		cmd = exec.Command("bash", "-c", "brew install docker")
	default:
		return InstallResult{Tool: "docker", Success: false, Message: fmt.Sprintf("install via '%s' not supported, install manually", i.system.PackageManager)}
	}
	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{Tool: "docker", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
	}
	return InstallResult{Tool: "docker", Success: true, Message: "installed"}
}

func (i *Installer) installGh() InstallResult {
	if _, err := exec.LookPath("gh"); err == nil {
		return InstallResult{Tool: "gh", Success: true, Message: "already installed"}
	}
	var cmd *exec.Cmd
	switch i.system.PackageManager {
	case "apt":
		cmd = exec.Command("bash", "-c", "curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg && echo 'deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main' | tee /etc/apt/sources.list.d/github-cli.list > /dev/null && apt update && apt install gh -y")
	case "pacman":
		cmd = exec.Command("pacman", "-S", "--noconfirm", "github-cli")
	case "dnf":
		cmd = exec.Command("dnf", "install", "-y", "gh")
	case "brew":
		cmd = exec.Command("brew", "install", "gh")
	default:
		return InstallResult{Tool: "gh", Success: false, Message: fmt.Sprintf("install via '%s' not supported, install manually", i.system.PackageManager)}
	}
	if output, err := cmd.CombinedOutput(); err != nil {
		return InstallResult{Tool: "gh", Success: false, Message: fmt.Sprintf("install failed: %s: %s", err, string(output))}
	}
	return InstallResult{Tool: "gh", Success: true, Message: "installed"}
}
