package cmd

import (
	"bytes"
	"fmt"
	"io"
	"os"

	"github.com/SAP/jenkins-library/pkg/command"
	pipergit "github.com/SAP/jenkins-library/pkg/git"
	"github.com/SAP/jenkins-library/pkg/log"
	"github.com/SAP/jenkins-library/pkg/piperutils"
	"github.com/SAP/jenkins-library/pkg/telemetry"
	"github.com/go-git/go-git/v5"
	"github.com/pkg/errors"
)

type batsExecuteTestsUtils interface {
	CloneRepo(URL string) error
	Stdin(in io.Reader)
	Stdout(out io.Writer)
	Stderr(err io.Writer)
	SetEnv([]string)
	FileWrite(path string, content []byte, perm os.FileMode) error
	RunExecutable(e string, p ...string) error
}

type batsExecuteTestsUtilsBundle struct {
	*command.Command
	*piperutils.Files
}

func newBatsExecuteTestsUtils() batsExecuteTestsUtils {
	utils := batsExecuteTestsUtilsBundle{
		Command: &command.Command{},
		Files:   &piperutils.Files{},
	}
	utils.Stdout(log.Writer())
	utils.Stderr(log.Writer())
	return &utils
}

func batsExecuteTests(config batsExecuteTestsOptions, telemetryData *telemetry.CustomData, influx *batsExecuteTestsInflux) {
	utils := newBatsExecuteTestsUtils()

	influx.step_data.fields.bats = false
	err := runBatsExecuteTests(&config, telemetryData, utils)
	if err != nil {
		log.Entry().WithError(err).Fatal("step execution failed")
	}
	influx.step_data.fields.bats = true
}

func runBatsExecuteTests(config *batsExecuteTestsOptions, telemetryData *telemetry.CustomData, utils batsExecuteTestsUtils) error {
	if config.OutputFormat != "tap" && config.OutputFormat != "junit" {
		log.SetErrorCategory(log.ErrorConfiguration)
		return fmt.Errorf("output format '%v' is incorrect. Possible drivers: tap, junit", config.OutputFormat)
	}

	err := utils.CloneRepo(config.Repository)

	if err != nil && !errors.Is(err, git.ErrRepositoryAlreadyExists) {
		return fmt.Errorf("couldn't pull %s repository: %w", config.Repository, err)
	}

	tapOutput := bytes.Buffer{}
	utils.Stdout(io.MultiWriter(&tapOutput, log.Writer()))

	utils.SetEnv(config.EnvVars)
	err = utils.RunExecutable("bats-core/bin/bats", "--recursive", "--tap", config.TestPath)
	if err != nil {
		return fmt.Errorf("failed to run bats test: %w", err)
	}

	err = utils.FileWrite("TEST-"+config.TestPackage+".tap", tapOutput.Bytes(), 0644)
	if err != nil {
		return fmt.Errorf("failed to write tap file: %w", err)
	}

	if config.OutputFormat == "junit" {
		output := bytes.Buffer{}
		utils.Stdout(io.MultiWriter(&output, log.Writer()))

		utils.SetEnv(append(config.EnvVars, "NPM_CONFIG_PREFIX=~/.npm-global"))
		err = utils.RunExecutable("npm", "install", "tap-xunit", "-g")
		if err != nil {
			return fmt.Errorf("failed to install tap-xunit: %w", err)
		}

		homedir, _ := os.UserHomeDir()
		path := "PATH=" + os.Getenv("PATH") + ":" + homedir + "/.npm-global/bin"

		output = bytes.Buffer{}
		utils.Stdout(&output)
		utils.Stdin(&tapOutput)
		utils.SetEnv(append(config.EnvVars, path))
		err = utils.RunExecutable("tap-xunit", "--package="+config.TestPackage)
		if err != nil {
			return fmt.Errorf("failed to run tap-xunit: %w", err)
		}
		err = utils.FileWrite("TEST-"+config.TestPackage+".xml", output.Bytes(), 0644)
		if err != nil {
			return fmt.Errorf("failed to write tap file: %w", err)
		}
	}

	return nil
}

func (b *batsExecuteTestsUtilsBundle) CloneRepo(URL string) error {
	_, err := pipergit.PlainClone("", "", URL, "bats-core")
	return err

}
