package commands

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

	"github.com/spf13/cobra"
)

// installShortcutsCmd creates desktop + Start Menu shortcuts on Windows so
// non-technical users can launch Muyue without opening a terminal. It also
// adds the install directory to the user's PATH (per-user, no admin).
//
// Implementation note: shortcut (.lnk) creation on Windows is most reliable
// via WScript.Shell COM. We invoke it via PowerShell — keeps the Go binary
// dependency-free and works on any Windows 10+ host.
var installShortcutsCmd = &cobra.Command{
	Use:   "install-shortcuts",
	Short: "Create Desktop + Start Menu shortcuts (Windows only) and add Muyue to PATH",
	RunE: func(cmd *cobra.Command, args []string) error {
		if runtime.GOOS != "windows" {
			fmt.Println("install-shortcuts is a Windows-only command (no-op on this platform)")
			return nil
		}

		exe, err := os.Executable()
		if err != nil {
			return fmt.Errorf("locate executable: %w", err)
		}
		exe, _ = filepath.Abs(exe)
		installDir := filepath.Dir(exe)

		fmt.Println("Installing Muyue shortcuts...")
		fmt.Printf("  Source     : %s\n", exe)

		// Provide a clean `muyue.exe` next to the platform-suffixed binary so
		// users can type `muyue` once the install dir is on PATH. Copy (not
		// rename) because the running .exe is locked on Windows.
		canonicalExe := filepath.Join(installDir, "muyue.exe")
		if !strings.EqualFold(exe, canonicalExe) {
			if err := copyFile(exe, canonicalExe); err != nil {
				fmt.Fprintf(os.Stderr, "  Copy       : warning — could not create muyue.exe: %v\n", err)
				canonicalExe = exe
			} else {
				fmt.Printf("  Canonical  : %s\n", canonicalExe)
			}
		}

		desktop, err := userShellFolder("Desktop")
		if err != nil {
			return fmt.Errorf("locate Desktop folder: %w", err)
		}
		startMenu, err := userShellFolder("Programs")
		if err != nil {
			return fmt.Errorf("locate Start Menu Programs folder: %w", err)
		}

		desktopLnk := filepath.Join(desktop, "Muyue.lnk")
		startLnk := filepath.Join(startMenu, "Muyue.lnk")

		if err := createWindowsShortcut(desktopLnk, canonicalExe, installDir, "Muyue — AI-powered dev environment"); err != nil {
			return fmt.Errorf("create desktop shortcut: %w", err)
		}
		fmt.Printf("  Desktop    : %s\n", desktopLnk)

		if err := createWindowsShortcut(startLnk, canonicalExe, installDir, "Muyue — AI-powered dev environment"); err != nil {
			return fmt.Errorf("create Start Menu shortcut: %w", err)
		}
		fmt.Printf("  Start Menu : %s\n", startLnk)

		if err := addUserPATH(installDir); err != nil {
			fmt.Fprintf(os.Stderr, "  PATH       : warning — could not add %s to user PATH: %v\n", installDir, err)
		} else {
			fmt.Printf("  PATH       : added %s\n", installDir)
		}

		fmt.Println("\nDone — double-click the Muyue icon on your Desktop to launch.")
		fmt.Println("\nTo use 'muyue' from this PowerShell session right now, run:")
		fmt.Printf("  $env:Path += ';%s'\n", installDir)
		fmt.Println("(New terminals will pick up the user PATH automatically.)")
		return nil
	},
}

// copyFile duplicates src to dst, overwriting an existing dst (used to drop a
// `muyue.exe` next to the platform-suffixed binary so the command is callable
// as `muyue` from PATH).
func copyFile(src, dst string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()
	if _, err := io.Copy(out, in); err != nil {
		return err
	}
	return out.Sync()
}

func init() {
	rootCmd.AddCommand(installShortcutsCmd)
}

// userShellFolder asks Windows for a user shell folder via PowerShell —
// resilient to OneDrive redirection and non-default profile locations.
// `which` is one of: Desktop, Programs (Start Menu Programs), StartMenu.
func userShellFolder(which string) (string, error) {
	ps := fmt.Sprintf(`[Environment]::GetFolderPath('%s')`, which)
	out, err := exec.Command("powershell", "-NoLogo", "-NoProfile", "-Command", ps).Output()
	if err != nil {
		return "", err
	}
	path := stripTrailingWhitespace(string(out))
	if path == "" {
		return "", fmt.Errorf("empty path for %s", which)
	}
	return path, nil
}

func stripTrailingWhitespace(s string) string {
	for len(s) > 0 && (s[len(s)-1] == '\n' || s[len(s)-1] == '\r' || s[len(s)-1] == ' ' || s[len(s)-1] == '\t') {
		s = s[:len(s)-1]
	}
	return s
}

// createWindowsShortcut generates a .lnk via WScript.Shell COM. The arguments
// are passed through PowerShell variables (not interpolated into the script
// body) to avoid quoting issues with paths containing spaces or special chars.
func createWindowsShortcut(lnkPath, target, workingDir, description string) error {
	script := `
$lnk = $env:MUYUE_LNK
$target = $env:MUYUE_TARGET
$workdir = $env:MUYUE_WORKDIR
$desc = $env:MUYUE_DESC
$wsh = New-Object -ComObject WScript.Shell
$sc = $wsh.CreateShortcut($lnk)
$sc.TargetPath = $target
$sc.WorkingDirectory = $workdir
$sc.Description = $desc
$sc.IconLocation = "$target,0"
$sc.Save()
`
	cmd := exec.Command("powershell", "-NoLogo", "-NoProfile", "-Command", script)
	cmd.Env = append(os.Environ(),
		"MUYUE_LNK="+lnkPath,
		"MUYUE_TARGET="+target,
		"MUYUE_WORKDIR="+workingDir,
		"MUYUE_DESC="+description,
	)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("powershell: %v: %s", err, string(out))
	}
	return nil
}

// addUserPATH appends installDir to the user's PATH if not already present.
// Uses PowerShell to read/write the User-scope environment via .NET API,
// which broadcasts WM_SETTINGCHANGE so new processes pick it up.
func addUserPATH(installDir string) error {
	script := `
$dir = $env:MUYUE_INSTALL_DIR
$current = [Environment]::GetEnvironmentVariable('Path', 'User')
if ($current -eq $null) { $current = '' }
$parts = $current -split ';' | Where-Object { $_ -ne '' }
if ($parts -notcontains $dir) {
    $new = if ($current -eq '') { $dir } else { "$current;$dir" }
    [Environment]::SetEnvironmentVariable('Path', $new, 'User')
}
`
	cmd := exec.Command("powershell", "-NoLogo", "-NoProfile", "-Command", script)
	cmd.Env = append(os.Environ(), "MUYUE_INSTALL_DIR="+installDir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("powershell: %v: %s", err, string(out))
	}
	return nil
}
