package utils

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// completionCmd represents the completion command
var completionCmd = &cobra.Command{
	Use:   "completion [bash/powershell/zsh]",
	Short: "Installs bash/powershell/zsh completion commands",
	Long: `Installs bash/powershell/zsh completion commands.
Please note that the completions may be incomplete or broken, see https://github.com/spf13/cobra`,
	Args:      cobra.ExactValidArgs(1),
	ValidArgs: []string{"bash", "powershell", "zsh"},
	Run: func(cmd *cobra.Command, args []string) {
		if args[0] == "bash" {
			if viper.GetBool("utils.completion.source") {
				cmd.Root().GenBashCompletion(os.Stdout)
			} else {
				file, err := getConfigPath("completion.sh")
				if err != nil {
					fmt.Printf("Error saving completion file: %s\n", err)
					os.Exit(1)
				}
				err = cmd.Root().GenBashCompletionFile(file)
				if err != nil {
					fmt.Printf("Error saving completion file: %s\n", err)
					os.Exit(1)
				}

				// Get the value of $HOME
				home, err := os.UserHomeDir()
				if err != nil {
					fmt.Printf("Failed to get $HOME location: %s\n", err)
					os.Exit(1)
				}
				bashrc := filepath.Join(home, ".bashrc")

				absFile, err := filepath.Abs(file)
				if err != nil {
					fmt.Printf("Failed to resolve path: %s\n", err)
					os.Exit(1)
				}
				command := ". " + absFile
				commandFound := false
				// Check for existing text in bashrc
				data, err := ioutil.ReadFile(bashrc)
				if err == nil {
					commandFound = strings.Contains(string(data), command)
				}
				if !commandFound {
					// Append to bashrc
					os.MkdirAll(filepath.Dir(bashrc), os.ModePerm)
					f, err := os.OpenFile(bashrc, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
					if err != nil {
						fmt.Printf("Failed to open bashrc: %s\n", err)
						os.Exit(1)
					}
					_, err = f.WriteString("\r\n" + command + "\r\n")
					if err != nil {
						fmt.Printf("Failed to write to bashrc: %s\n", err)
						f.Close()
						os.Exit(1)
					}
					f.Close()
				}
				fmt.Println("Completions installed! Restart your shell to load them.")
			}
		} else if args[0] == "powershell" {
			if viper.GetBool("utils.completion.source") {
				cmd.Root().GenPowerShellCompletion(os.Stdout)
			} else {
				file, err := getConfigPath("completion.ps1")
				if err != nil {
					fmt.Printf("Error saving completion file: %s\n", err)
					os.Exit(1)
				}
				err = cmd.Root().GenPowerShellCompletionFile(file)
				if err != nil {
					fmt.Printf("Error saving completion file: %s\n", err)
					os.Exit(1)
				}

				// Get the value of $PROFILE (not an environment variable!!)
				profile, err := getPowershellProfile()
				if err != nil {
					fmt.Printf("Failed to get profile location: %s\n", err)
					os.Exit(1)
				}

				absFile, err := filepath.Abs(file)
				if err != nil {
					fmt.Printf("Failed to resolve path: %s\n", err)
					os.Exit(1)
				}
				command := ". " + absFile
				commandFound := false
				// Check for existing text in profile
				data, err := ioutil.ReadFile(profile)
				if err == nil {
					commandFound = strings.Contains(string(data), command)
				}
				if !commandFound {
					// Append to profile
					os.MkdirAll(filepath.Dir(profile), os.ModePerm)
					f, err := os.OpenFile(profile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
					if err != nil {
						fmt.Printf("Failed to open profile: %s\n", err)
						os.Exit(1)
					}
					_, err = f.WriteString("\r\n" + command + "\r\n")
					if err != nil {
						fmt.Printf("Failed to write to profile: %s\n", err)
						f.Close()
						os.Exit(1)
					}
					f.Close()
				}
				fmt.Println("Completions installed! Restart your shell to load them.")
			}
		} else if args[0] == "zsh" {
			if viper.GetBool("utils.completion.source") {
				cmd.Root().GenZshCompletion(os.Stdout)
			} else {
				file, err := getConfigPath("completion.zsh")
				if err != nil {
					fmt.Printf("Error saving completion file: %s\n", err)
					os.Exit(1)
				}
				err = cmd.Root().GenZshCompletionFile(file)
				if err != nil {
					fmt.Printf("Error saving completion file: %s\n", err)
					os.Exit(1)
				}

				// If someone knows how to do this automagically, please PR it!!
				fmt.Println("Completions saved to " + file)
				fmt.Println("You need to put this file in your $fpath manually!")
			}
		}
	},
}

func getConfigPath(fileName string) (string, error) {
	dir, err := os.UserConfigDir()
	if err != nil {
		return "", err
	}
	dir = filepath.Join(dir, "packwiz")
	err = os.MkdirAll(dir, os.ModePerm)
	if err != nil {
		return "", err
	}
	return filepath.Join(dir, fileName), nil
}

func getPowershellProfile() (string, error) {
	ps, err := exec.LookPath("powershell.exe")
	if err != nil {
		return "", err
	}
	cmd := exec.Command(ps, "-NoProfile", "-NonInteractive", "-Command", "$PROFILE")
	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	err = cmd.Run()
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(stdout.String()), nil
}

func init() {
	utilsCmd.AddCommand(completionCmd)

	completionCmd.Flags().Bool("source", false, "Output the source of the commands to be installed, rather than installing them")
	viper.BindPFlag("utils.completion.source", completionCmd.Flags().Lookup("source"))
}