package cli

import (
	"fmt"
	"os"

	log "github.com/Sirupsen/logrus"
	"github.com/bitrise-io/bitrise/bitrise"
	"github.com/bitrise-io/bitrise/models"
	"github.com/bitrise-io/go-utils/colorstring"
	"github.com/bitrise-io/go-utils/fileutil"
	"github.com/bitrise-io/go-utils/pathutil"
	"github.com/bitrise-io/goinp/goinp"
	"github.com/codegangsta/cli"
)

const (
	defaultStepLibSource = "https://github.com/bitrise-io/bitrise-steplib.git"
	//
	defaultSecretsContent = `envs:
- MY_HOME: $HOME
- MY_SECRET_PASSWORD: XyZ
  opts:
    # You can include some options as well if you
    #  want to change how the value is passed to a command.
    is_expand: no
    # For example you can use is_expand: no
    #  if you want to make it sure that
    #  the value is preserved as-it-is, and won't be
    #  expanded before use.
    # For example if your password contains the dollar sign ($)
    #  it would (by default) be expanded as an environment variable,
    #  just like $HOME would be expanded/replaced with your home
    #  directory path.
    # You can prevent this with is_expand: no`

	defaultBitriseYMLContentFormat = `format_version: %s
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

title: Template configuration.
summary: |-
  Template 'bitrise.yml', generated by 'bitrise init'.
description: |-
  Configuration (environments) specified in 'app' will be available
    for every workflow.

  The Trigger Map ('trigger_map') defines mapping between trigger patterns
    and workflows.
  You can run workflows directly with bitrise: bitrise run workflow-name
    Or you can 'trigger' a build: bitrise trigger some-pattern

   With this example 'trigger_map' if you 'bitrise trigger test'
    or 'bitrise trigger test-1' or specify any other pattern
    which starts with 'test' then the 'test' workflow will be used.
   In any other case (ex: 'bitrise trigger something-else') the
    workflow called 'fallback' will be used.

  Workflows ('workflows') are where you can define different, separate scenarios,
    which you can then 'bitrise run' or 'bitrise trigger'.

app:
  envs:
  - BITRISE_APP_TITLE: "%s"
  - BITRISE_DEV_BRANCH: "%s"

trigger_map:
- pattern: test**
  is_pull_request_allowed: true
  workflow: test
- pattern: "*"
  is_pull_request_allowed: true
  workflow: fallback

workflows:
  test:
    description: |-
      You can use this workflow by running: bitrise run test
      Or by triggering: bitrise trigger test
      If you use trigger, you can trigger it with any pattern
        which starts with 'test', like: bitrise trigger test-1
        or: bitrise trigger test/1
      You can define the mapping between trigger patterns and
        workflows in the 'trigger_map' section.
    steps:
    - timestamp:
        outputs:
        - UNIX_TIMESTAMP:
        - ISO_DATETIME:
    - script:
        title: Hello Bitrise!
        inputs:
        - content: |-
            #!/bin/bash
            echo "Welcome to Bitrise!"
            echo "Current time is: ${ISO_DATETIME}"
            echo
            echo "Your specified Project Title is: ${BITRISE_APP_TITLE}"
            echo " and the primary development branch is: ${BITRISE_DEV_BRANCH}"
  fallback:
    description: |-
      This is a fallback workflow. As defined in the 'trigger_map' section
      this workflow will be selected in any case if you run 'bitrise trigger'
      with any pattern which does not match any other in
      the 'tigger_map' definition. For example 'bitrise trigger my-custom-pattern'
      or 'bitrise trigger my/custom/pattern' will both select this workflow.
    steps:
    - script:
        title: Fallback
        inputs:
        - content: |-
            #!/bin/bash
            echo "This is a the fallback workflow, used"
            echo " if you 'bitrise trigger' a build but the pattern"
            echo " does not match any other pattern in the trigger_map"
`
)

func generateBitriseYMLContent(userInputProjectTitle, userInputDevBranch string) (string, []string, error) {
	bitriseConfContent := fmt.Sprintf(defaultBitriseYMLContentFormat,
		models.Version, userInputProjectTitle, userInputDevBranch)

	_, warnings, err := bitrise.ConfigModelFromYAMLBytes([]byte(bitriseConfContent))
	if err != nil {
		return "", warnings, err
	}

	return bitriseConfContent, warnings, nil
}

func initConfig(c *cli.Context) {
	PrintBitriseHeaderASCIIArt(c.App.Version)

	bitriseConfigFileRelPath := "./" + DefaultBitriseConfigFileName
	bitriseSecretsFileRelPath := "./" + DefaultSecretsFileName

	if exists, err := pathutil.IsPathExists(bitriseConfigFileRelPath); err != nil {
		log.Fatalf("Failed to init path (%s), error: %s", bitriseConfigFileRelPath, err)
	} else if exists {
		ask := fmt.Sprintf("A config file already exists at %s - do you want to overwrite it?", bitriseConfigFileRelPath)
		if val, err := goinp.AskForBool(ask); err != nil {
			log.Fatalf("Failed to ask for input, error: %s", err)
		} else if !val {
			log.Info("Init canceled, existing file won't be overwritten.")
			os.Exit(0)
		}
	}

	userInputProjectTitle := ""
	userInputDevBranch := ""
	if val, err := goinp.AskForString("What's the BITRISE_APP_TITLE?"); err != nil {
		log.Fatalf("Failed to ask for input, error: %s", err)
	} else {
		userInputProjectTitle = val
	}
	if val, err := goinp.AskForString("What's your development branch's name?"); err != nil {
		log.Fatalf("Failed to ask for input, error: %s", err)
	} else {
		userInputDevBranch = val
	}

	bitriseConfContent, warnings, err := generateBitriseYMLContent(userInputProjectTitle, userInputDevBranch)
	for _, warning := range warnings {
		log.Warnf("warning: %s", warning)
	}
	if err != nil {
		log.Fatalf("Invalid Bitrise YML, error: %s", err)
	}

	if err := fileutil.WriteStringToFile(bitriseConfigFileRelPath, bitriseConfContent); err != nil {
		log.Fatalf("Failed to init the bitrise config file, error: %s", err)
	} else {
		fmt.Println()
		fmt.Println("# NOTES about the " + DefaultBitriseConfigFileName + " config file:")
		fmt.Println()
		fmt.Println("We initialized a " + DefaultBitriseConfigFileName + " config file for you.")
		fmt.Println("If you're in this folder you can use this config file")
		fmt.Println(" with bitrise automatically, you don't have to")
		fmt.Println(" specify it's path.")
		fmt.Println()
	}

	if initialized, err := saveSecretsToFile(bitriseSecretsFileRelPath, defaultSecretsContent); err != nil {
		log.Fatalf("Failed to init the secrets file, error: %s", err)
	} else if initialized {
		fmt.Println()
		fmt.Println("# NOTES about the " + DefaultSecretsFileName + " secrets file:")
		fmt.Println()
		fmt.Println("We also created a " + DefaultSecretsFileName + " file")
		fmt.Println(" in this directory, to keep your passwords, absolute path configurations")
		fmt.Println(" and other secrets separate from your")
		fmt.Println(" main configuration file.")
		fmt.Println("This way you can safely commit and share your configuration file")
		fmt.Println(" and ignore this secrets file, so nobody else will")
		fmt.Println(" know about your secrets.")
		fmt.Println(colorstring.Yellow("You should NEVER commit this secrets file into your repository!!"))
		fmt.Println()
	}

	// add the general .bitrise* item
	//  which will include both secret files like .bitrise.secrets.yml
	//  and the .bitrise work temp dir
	if err := addToGitignore(".bitrise*"); err != nil {
		log.Fatalf("Failed to add .gitignore pattern, error: %s", err)
	}
	fmt.Println(colorstring.Green("For your convenience we added the pattern '.bitrise*' to your .gitignore file"))
	fmt.Println(" to make it sure that no secrets or temporary work directories will be")
	fmt.Println(" committed into your repository.")

	fmt.Println()
	fmt.Println("Hurray, you're good to go!")
	fmt.Println("You can simply run:")
	fmt.Println("-> bitrise run test")
	fmt.Println("to test the sample configuration (which contains")
	fmt.Println("an example workflow called 'test').")
	fmt.Println()
	fmt.Println("Once you tested this sample setup you can")
	fmt.Println(" open the " + DefaultBitriseConfigFileName + " config file,")
	fmt.Println(" modify it and then run a workflow with:")
	fmt.Println("-> bitrise run YOUR-WORKFLOW-NAME")
	fmt.Println(" or trigger a build with a pattern:")
	fmt.Println("-> bitrise trigger YOUR/PATTERN")
}

func saveSecretsToFile(pth, secretsStr string) (bool, error) {
	if exists, err := pathutil.IsPathExists(pth); err != nil {
		return false, err
	} else if exists {
		ask := fmt.Sprintf("A secrets file already exists at %s - do you want to overwrite it?", pth)
		if val, err := goinp.AskForBool(ask); err != nil {
			return false, err
		} else if !val {
			log.Infoln("Init canceled, existing file (" + pth + ") won't be overwritten.")
			return false, nil
		}
	}

	if err := fileutil.WriteStringToFile(pth, secretsStr); err != nil {
		return false, err
	}
	return true, nil
}

func addToGitignore(ignorePattern string) error {
	return fileutil.AppendStringToFile(".gitignore", "\n"+ignorePattern+"\n")
}
