package config

import (
	"encoding/base64"
	"errors"
	"fmt"
	"os"
	"strings"

	"github.com/dokku/dokku/plugins/common"
)

func export(appName string, merged bool, format string) error {
	env := getEnvironment(appName, merged)
	exportType := ExportFormatExports
	suffix := "\n"

	exportTypes := map[string]ExportFormat{
		"docker-args":      ExportFormatDockerArgs,
		"docker-args-keys": ExportFormatDockerArgsKeys,
		"envfile":          ExportFormatEnvfile,
		"exports":          ExportFormatExports,
		"json":             ExportFormatJSON,
		"json-list":        ExportFormatJSONList,
		"pack-keys":        ExportFormatPackArgKeys,
		"pretty":           ExportFormatPretty,
		"shell":            ExportFormatShell,
	}

	exportType, ok := exportTypes[format]
	if !ok {
		return fmt.Errorf("Unknown export format: %v", format)
	}

	if exportType == ExportFormatShell {
		suffix = " "
	}

	exported := env.Export(exportType)
	fmt.Print(exported + suffix)
	return nil
}

// SubBundle implements the logic for config:bundle without app name validation
func SubBundle(appName string, merged bool) error {
	env := getEnvironment(appName, merged)
	return env.ExportBundle(os.Stdout)
}

// SubClear implements the logic for config:clear without app name validation
func SubClear(appName string, noRestart bool) error {
	return UnsetAll(appName, !noRestart)
}

// SubExport implements the logic for config:export without app name validation
func SubExport(appName string, merged bool, format string) error {
	return export(appName, merged, format)
}

// SubGet implements the logic for config:get without app name validation
func SubGet(appName string, keys []string, quoted bool) error {
	if len(keys) == 0 {
		return errors.New("Expected: key")
	}

	if len(keys) != 1 {
		return fmt.Errorf("Unexpected argument(s): %v", keys[1:])
	}

	value, ok := Get(appName, keys[0])
	if !ok {
		os.Exit(1)
		return nil
	}

	if quoted {
		fmt.Printf("'%s'\n", singleQuoteEscape(value))
	} else {
		fmt.Printf("%s\n", value)
	}

	return nil
}

// SubImport imports environment variables from a file
func SubImport(appName string, replace bool, noRestart bool, format string, filename string) error {
	if filename == "-" {
		common.LogInfo1Quiet("Reading config vars from stdin")
	}

	if format == "" {
		format = "envfile"
	}

	var env *Env
	var err error
	switch format {
	case "envfile":
		env, err = loadFromFile(appName, filename)
		if err != nil {
			return err
		}
	case "json":
		env, err = loadFromFileJSON(appName, filename)
		if err != nil {
			return err
		}
	default:
		return fmt.Errorf("Unknown format: %s", format)
	}

	if len(env.Map()) == 0 {
		return errors.New("No config vars to set")
	}

	for k, v := range env.Map() {
		common.LogInfo1Quiet(fmt.Sprintf("Setting config var %s=%s", k, v))
	}

	return SetMany(appName, env.Map(), replace, !noRestart)
}

// SubKeys implements the logic for config:keys without app name validation
func SubKeys(appName string, merged bool) error {
	env := getEnvironment(appName, merged)
	for _, k := range env.Keys() {
		fmt.Println(k)
	}
	return nil
}

// SubSet implements the logic for config:set without app name validation
func SubSet(appName string, pairs []string, noRestart bool, encoded bool) error {
	if len(pairs) == 0 {
		return errors.New("At least one env pair must be given")
	}

	updated := make(map[string]string)
	for _, e := range pairs {
		parts := strings.SplitN(e, "=", 2)
		if len(parts) == 1 {
			return fmt.Errorf("Invalid env pair: %v", e)
		}

		key, value := parts[0], parts[1]
		if encoded {
			decoded, err := base64.StdEncoding.DecodeString(value)
			if err != nil {
				return fmt.Errorf("%s for key '%s'", err.Error(), key)
			}
			value = string(decoded)
		}
		updated[key] = value
	}

	return SetMany(appName, updated, false, !noRestart)
}

// SubShow implements the logic for config:show without app name validation
func SubShow(appName string, merged bool, shell bool, export bool) error {
	env := getEnvironment(appName, merged)
	if shell && export {
		return errors.New("Only one of --shell and --export can be given")
	}
	if shell {
		common.LogWarn("Deprecated: Use 'config:export --format shell' instead")
		fmt.Print(env.Export(ExportFormatShell))
	} else if export {
		common.LogWarn("Deprecated: Use 'config:export --format exports' instead")
		fmt.Println(env.Export(ExportFormatExports))
	} else {
		contextName := "global"
		if appName != "" {
			contextName = appName
		}
		common.LogInfo2Quiet(contextName + " env vars")
		fmt.Println(env.Export(ExportFormatPretty))
	}

	return nil
}

// SubUnset implements the logic for config:unset without app name validation
func SubUnset(appName string, keys []string, noRestart bool) error {
	if len(keys) == 0 {
		return fmt.Errorf("At least one key must be given")
	}

	return UnsetMany(appName, keys, !noRestart)
}
