package cmd

import (
	"fmt"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"gitlab.com/pcanilho/go-jira"
	"gitlab.com/pcanilho/go-jira-cli/cmd/formatters"
	"gitlab.com/pcanilho/go-jira-cli/internal"
	"log"
	"os"
	"strings"
)

var (
	version, commit string
	name            = "jira-cli"
)

const header = `
 _______  _______ 
(  ___  )(  ____ \
| (   ) || (    \/
| |   | || |      
| |   | || |      
| |   | || |      
| (___) || (____/\
(_______)(_______/  Paulo Canilho (https://canilho.net/)`

// Output
var outputFormatter formatters.Formatter
var output, outputExpanded interface{}
var outputIssueList []jira.Issue

// rootCmd represents the root application command
var rootCmd = &cobra.Command{
	Use:              name,
	Version:          fmt.Sprintf("%s\n\nv%s (%s)", header, version, commit),
	Short:            "A wrapper module for Jira based interactions",
	PersistentPreRun: func(cmd *cobra.Command, args []string) { OverrideArgsFromEnv() },
	PersistentPostRun: func(cmd *cobra.Command, args []string) {
		outputFormatter = formatters.NewFormatter(format)
		if expand && outputExpanded != nil {
			fmt.Println(outputFormatter.Serialise(outputExpanded))
		} else if output != nil {
			fmt.Println(output)
		} else if outputIssueList != nil {
			fmt.Println(outputFormatter.Serialise(outputIssueList))
		}
	},
}

var (
	// Arguments
	jiraUsername, jiraPassword, jiraUrl string
	verbose                             int
	skipPrompts                         bool
	skipVerifyTLS                       bool
	format                              string
)

type envArg struct {
	ArgName, EnvName, Usage string
}

// Internal controller
var jiraController internal.Controller

// envMap accumulates all the arguments and associated environment variable name as well at the command usage
var envMap = map[*string]envArg{
	&jiraUsername: {
		ArgName: "jira.username",
		EnvName: "JIRA_USERNAME",
		Usage:   "[JIRA_USERNAME] the username used to authenticate with Jira",
	},
	&jiraPassword: {
		ArgName: "jira.password",
		EnvName: "JIRA_PASSWORD",
		Usage:   "[JIRA_PASSWORD] the password used to authenticate with Jira",
	},
	&jiraUrl: {
		ArgName: "jira.url",
		EnvName: "JIRA_URL",
		Usage:   "[JIRA_URL] the Jira instance URL",
	},
}

func init() {
	viper.AutomaticEnv()

	// Verbosity
	rootCmd.PersistentFlags().CountVarP(&verbose, "verbose", "v", "verbosity level")

	// Output format
	rootCmd.PersistentFlags().StringVar(&format, "format", "json", "output format used with the `--expand` flag (e.g. json, table, markdown, csv, html, yaml)")

	// Skip-Verify TLS
	rootCmd.PersistentFlags().BoolVar(&skipVerifyTLS, "tls.skip-verify", false, "skip verify TLS certificates")

	// Expand
	rootCmd.PersistentFlags().BoolVar(&expand, "expand", false, "if specified additional fields will be printed")

	// Environment
	for key, entry := range envMap {
		rootCmd.PersistentFlags().StringVar(key, entry.ArgName, "", entry.Usage)
		if err := viper.BindEnv(entry.EnvName, entry.ArgName); err != nil {
			log.Fatalln(err)
		}
	}

	// Force
	rootCmd.PersistentFlags().BoolVarP(&skipPrompts, "force", "f", false, "skip all prompts if specified")

	// Add commands
	rootCmd.AddCommand(interactiveCmd)
	rootCmd.AddCommand(licenseCmd)
	rootCmd.AddCommand(issueCmd)
	rootCmd.AddCommand(userCmd)
}

func Execute() error {
	return rootCmd.Execute()
}

func HelpCalled() bool {
	for _, arg := range os.Args {
		if strings.Contains(arg, "help") || arg == "-h" {
			return true
		}
	}
	return false
}

func VersionCalled() bool {
	for _, arg := range os.Args {
		if strings.Contains(arg, "version") || arg == "-v" {
			return true
		}
	}
	return false
}

func OverrideArgsFromEnv() {
	setIfNotEmpty(&jiraUsername)
	setIfNotEmpty(&jiraPassword)
	setIfNotEmpty(&jiraUrl)

	instance, err := internal.NewJira(nil, jiraUsername, jiraPassword, jiraUrl, skipVerifyTLS)
	if err != nil {
		log.Fatalf("Unable to initialise the Jira controller. Error [%v]", err)
	}
	jiraController = instance
}

func setIfNotEmpty(key *string) {
	mappedVal, found := envMap[key]
	if !found {
		return
	}
	viperVal := viper.GetString(mappedVal.EnvName)
	if len(strings.TrimSpace(viperVal)) != 0 {
		*key = viperVal
	}
}