package mage_helpers

import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"strings"

	"github.com/thoas/go-funk"

	"github.com/magefile/mage/sh"
)

func GetDirs(path string) []os.FileInfo {
	filesDir, err := ioutil.ReadDir(path)
	// filesDir, err := ioutil.ReadDir("/Users/johan/src/Shiplogic/backends/backend/billing")
	if err != nil {
		log.Fatal(err)
	}

	dirs := funk.Filter(filesDir, func(info os.FileInfo) bool {
		if info.IsDir() && info.Name() == "build" {
			return false
		}

		return info.IsDir()
	}).([]os.FileInfo)

	return dirs
}

func copyFilesToBuildDir(currentDir string, buildDir string) error {
	files, err := AllFiles(currentDir)
	if err != nil {
		return err
	}
	for _, file := range files {
		fileExtension := filepath.Ext(file)
		if fileExtension == ".go" || fileExtension == ".md" || fileExtension == ".html" || fileExtension == ".mjml" || fileExtension == ".txt" {
			continue
		}

		// Copy files to build DIR
		err := Copy(file, buildDir+"/"+path.Base(file))
		if err != nil {
			return err
		}
	}
	return nil
}

func AllFiles(dir string) ([]string, error) {
	files := []string{}
	err := filepath.Walk(dir,
		func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if info.IsDir() {
				return nil
			}

			files = append(files, path)
			return nil
		})

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

func Copy(src, dst string) error {
	fileInfo, err := os.Stat(src)
	if err != nil {
		return err
	}

	if fileInfo.Mode() == os.ModeSymlink {
		src, err = os.Readlink(src)
		if err != nil {
			return err
		}
	}

	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()

	_, err = io.Copy(out, in)
	if err != nil {
		return err
	}

	err = os.Chmod(dst, fileInfo.Mode())
	if err != nil {
		return err
	}

	return out.Close()
}

func CurrentBasePath() string {
	currentDir, _ := os.Getwd()
	base := path.Base(currentDir)
	return base
}

func CurrentEnv(defaultEnv ...string) string {

	// Get env from CI commit branch
	env := os.Getenv("CI_COMMIT_BRANCH")

	// Get env from current git branch
	if env == "" {
		commandArgs := []string{
			`rev-parse`,
			`--abbrev-ref`,
			`HEAD`,
		}

		var err error
		env, err = sh.Output("git", commandArgs...)
		if err != nil {
			return ""
		}
	}

	if env != "dev" && env != "stage" && env != "qa" && env != "sandbox" && env != "prod" && env != "playground" {
		if defaultEnv != nil && len(defaultEnv) > 0 {
			forcedEnv := defaultEnv[0]
			env = forcedEnv
		} else {
			env = "dev"
		}
	}
	return env
}

const (
	exitCodeErr       = 1
	exitCodeInterrupt = 2
)

func killWithContext(ctx context.Context) {
	cancelContext, cancel := context.WithCancel(ctx)
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	defer func() {
		signal.Stop(signalChan)
		cancel()
	}()
	go func() {
		select {
		case <-signalChan: // first signal, cancel context
			cancel()
			os.Exit(exitCodeInterrupt)
		case <-cancelContext.Done():
		}
		<-signalChan // second signal, hard exit
		os.Exit(exitCodeInterrupt)
	}()
}

func BuildCommand(command string, args []string) *exec.Cmd {
	fmt.Println(fmt.Sprintf("Running command: %s %s", command, strings.Join(args, " ")))
	cmd := exec.Command(command, args...)
	return cmd
}