package mage_helpers

import (
	_ "embed"
	"fmt"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
)

func CDKCompileTypeScript(cdkDir string) error {
	fmt.Println("Compiling Typescript")

	cmd := exec.Command("tsc")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Dir = cdkDir
	err := cmd.Run()

	if err != nil {
		return err
	}
	return nil
}

func CDKDiff(cdkDir string, env string, stack string, local bool, profile string) error {
	fmt.Println("Getting Diff for: " + stack)

	commandArgs := []string{
		`diff`,
		stack,
		`-c`,
		fmt.Sprintf(`config=%v`, env),
		`-c`,
		fmt.Sprintf(`exclusively=%v`, stack),
		`-c`,
		`aws-cdk:enableDiffNoFail=true`,
	}

	if local {
		commandArgs = append(commandArgs, fmt.Sprintf(`--profile=%v`, profile))
	}

	commandArgs = append(commandArgs,
		`--require-approval=never`,
		`--exclusively=true`,
		`--progress=events`)

	cmd := BuildCommand("cdk", commandArgs)
	cmd.Dir = cdkDir

	output, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Println(string(output))
		return err
	}
	fmt.Println(string(output))
	return nil
}

func CDKDeploy(cdkDir string, env string, stack string, exclusively bool, local bool, profile string) error {
	commandArgs := []string{
		`deploy`,
		fmt.Sprintf(`"%s"`, stack),
		`-c`,
		fmt.Sprintf(`config=%v`, env),
	}

	if exclusively {
		commandArgs = append(commandArgs, `-c`,
			fmt.Sprintf(`exclusively="%v"`, stack))
	}

	if local {
		commandArgs = append(commandArgs, fmt.Sprintf(`--profile=%v`, profile))

		// if env == "dev" {
		// 	commandArgs = append(commandArgs, `--hotswap`) // make it go fast
		// }
	}

	commandArgs = append(commandArgs,
		`--require-approval=never`,
		fmt.Sprintf(`--exclusively=%v`, strconv.FormatBool(exclusively)),
		`--progress=events`,
		`--app=./cdk.out`)

	cmd := BuildCommand("cdk", commandArgs)
	cmd.Dir = cdkDir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	fmt.Printf("==================%v==================\n", stack)
	err := cmd.Run()
	if err != nil {
		return err
	}
	fmt.Printf("==================%v==================\n\n\n\n", stack)
	return nil
}

func CDKWatch(cdkDir string, env string, stack string, exclusively bool, profile string) error {
	commandArgs := []string{
		`watch`,
		fmt.Sprintf(`"%s"`, stack),
		`-c`,
		fmt.Sprintf(`config=%v`, env),
	}

	if exclusively {
		commandArgs = append(commandArgs, `-c`,
			fmt.Sprintf(`exclusively="%v"`, stack))
	}

	commandArgs = append(commandArgs, fmt.Sprintf(`--profile=%v`, profile))

	commandArgs = append(commandArgs,
		`--require-approval=never`,
		fmt.Sprintf(`--exclusively=%v`, strconv.FormatBool(exclusively)))

	cmd := BuildCommand("cdk", commandArgs)
	cmd.Dir = cdkDir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	fmt.Printf("==================%v==================\n", stack)
	err := cmd.Run()
	if err != nil {
		return err
	}
	fmt.Printf("==================%v==================\n\n\n\n", stack)
	return nil
}

func CDKSynthAll(cdkDir string, env string, profile string, local bool) error {
	commandArgs := []string{
		`synth`,
		`"*"`,
		`-c`,
		fmt.Sprintf(`config=%v`, env),
		`-c`,
		`aws-cdk:enableDiffNoFail=true`,
	}

	if local {
		commandArgs = append(commandArgs, fmt.Sprintf(`--profile=%v`, profile))
	}

	commandArgs = append(commandArgs,
		`--progress=events`)

	cmd := BuildCommand("cdk", commandArgs)
	cmd.Dir = cdkDir

	output, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Println("Failed to run CDK Synth")
		fmt.Println(err)
		fmt.Println(string(output))
		return err
	}
	fmt.Println(string(output))
	return nil
}

func CDKSynthDebug(cdkDir string, env string, handler string, appName string, profile string) error {
	// Check if sandbox environment is active and get the sandbox name from the config file
	sandboxFilePath := "cdk/sandbox/config.dev.sandbox.yaml"
	sandboxName := ""
	if _, err := os.Stat(sandboxFilePath); err == nil {
		sandboxYamlFile, err := ioutil.ReadFile(sandboxFilePath)
		if err != nil {
			return err
		}

		var sandboxData = make(map[string]interface{})
		err = yaml.Unmarshal(sandboxYamlFile, &sandboxData)
		if err != nil {
			return err
		}

		overrides := sandboxData["Overrides"].(map[interface{}]interface{})
		sandboxName = overrides["SandboxName"].(string)
	}

	var stack string
	if len(sandboxName) == 0 {
		stack = fmt.Sprintf("%v-%v-core", appName, env)
	} else {
		stack = fmt.Sprintf("sandbox-%v-%v-core", env, sandboxName)
	}

	commandArgs := []string{
		`synth`,
		stack,
		`-c`,
		fmt.Sprintf(`config=%v`, env),
		fmt.Sprintf("--profile=%v", profile),
		`--require-approval=never`,
		`--exclusively=true`,
		`--no-staging`,
	}

	cmd := BuildCommand("cdk", commandArgs)
	cmd.Dir = cdkDir

	output, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Println(string(output))
		return err
	}

	file, err := os.Create("core/" + handler + "/template.yml")
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.WriteString(string(output))
	if err != nil {
		return err
	}

	// Copy nested stack to handler_api folder

	err = filepath.Walk(cdkDir+"/cdk.out/", func(path string, info os.FileInfo, err error) error {
		if strings.Contains(path, "nested.template.json") {

			filename := filepath.Base(path)
			err := Copy(path, "./core/"+handler+"/"+filename)
			if err != nil {
				return err
			}
		}

		return nil
	})
	if err != nil {
		return err
	}

	return nil
}

var (
	//go:embed clear-cdk-cache.sh
	script []byte
)

func ClearCDKCache(cdkDir string) error {
	cmd := exec.Command("rm", "-rf", "./cdk.out")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Dir = cdkDir
	err := cmd.Run()
	if err != nil {
		return err
	}
	return nil

	// runScript := fmt.Sprintf("%s", string(script))
	//
	// var out bytes.Buffer
	// cmd := exec.Command("/bin/bash", "-s")
	// cmd.Stdin = bytes.NewBuffer([]byte(runScript))
	// cmd.Dir = cdkDir
	// cmd.Stderr = os.Stderr
	// cmd.Stdout = os.Stdout
	// err := cmd.Run()
	// fmt.Println(string(out.Bytes()))
	// if err != nil {
	// 	return err
	// }
	// return nil
}

func FunctionFromTemplate(path string) ([]string, error) {
	yamlFile, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	template := map[string]interface{}{}
	err = yaml.Unmarshal(yamlFile, template)
	if err != nil {
		return nil, err
	}

	resources := template["Resources"].(map[interface{}]interface{})

	functions := []string{}
	for _, function := range resources {
		function := function.(map[interface{}]interface{})
		if function["Type"] == "AWS::Lambda::Function" {
			properties := function["Properties"].(map[interface{}]interface{})
			name, exists := properties["FunctionName"]
			if exists {
				functions = append(functions, name.(string))
			}

		}
	}

	return functions, nil
}