package executor

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os/exec"
	"strings"
)

// AllCommandExecutor 正确或者错误的信息全部收集，共同返回
func AllCommandExecutor(singleLineCommand []string) (resultOk bool, resultLog []string) {
	// result
	var resultSlice []string
	resultOk = true

	cmd := exec.Command(singleLineCommand[0], singleLineCommand[1:]...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.DebugF("command  %v stdout error => %v", singleLineCommand, err)
		resultOk = false
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.DebugF("command  %v stderr error => %v", singleLineCommand, err)
		resultOk = false
	}

	if err := cmd.Start(); err != nil {
		log.DebugF("command  %v runtime error => %v", singleLineCommand, err)
		resultOk = false
	}

	// 收集错误或者
	resultSlice = collectOutput(stdout, resultSlice)
	resultSlice = collectOutput(stderr, resultSlice)

	if err := cmd.Wait(); err != nil {
		log.DebugF("command  %v result error => %v", singleLineCommand, err)
		resultOk = false
	}

	log.DebugF("all command of %v result are =>  %v", singleLineCommand, resultSlice)

	return resultOk, resultSlice
}

// AllCompleteExecutor 多行命令的执行函数，返回执行正确与否和执行结果
func AllCompleteExecutor(multiCommand [][]string) (resultOk bool, resultLog []string) {

	var result []string
	resultOk = true

	for _, singleLineCommand := range multiCommand {
		ok, resultLog := AllCommandExecutor(singleLineCommand)
		if !ok {
			// 执行出错
			resultOk = false
		}
		result = append(result, resultLog...)
	}

	return resultOk, result
}

// FormatAllCommandExecutor 收集全部执行结果、错误并且返回
func FormatAllCommandExecutor(singleLineCommand []string) ([]string, error) {

	// result
	var resultSlice []string
	var resultError error
	var cmd *exec.Cmd

	if len(singleLineCommand) > 1 {
		cmd = exec.Command(singleLineCommand[0], singleLineCommand[1:]...)
	} else {
		cmd = exec.Command(singleLineCommand[0])
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.ErrorF("command  %v stdout error => %v", singleLineCommand, err)
		resultError = err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.ErrorF("command  %v stderr error => %v", singleLineCommand, err)
		resultError = err
	}

	if err := cmd.Start(); err != nil {
		log.ErrorF("command  %v runtime error => %v", singleLineCommand, err)
	}

	resultSlice = append(resultSlice, fmt.Sprintf(" 开始执行命令 ====> %s", singleLineCommand), "↓↓↓ 命令 输出 如下 ↓↓↓")
	resultSlice = collectOutput(stdout, resultSlice)
	resultSlice = append(resultSlice, "↓↓↓ 命令 错误 如下 ↓↓↓")
	resultSlice = collectOutput(stderr, resultSlice)

	if err := cmd.Wait(); err != nil {
		log.ErrorF("command  %v result error => %v", singleLineCommand, err)
		resultError = err
	}

	log.DebugF("real time exec result are %v", resultSlice)

	return resultSlice, resultError
}

func PipelineCommandExecutor(pipelineCommand [][]string) (resultOk bool, resultLog []string) {
	if len(pipelineCommand) == 0 {
		return true, nil
	} else if len(pipelineCommand) == 1 {
		log.Debug("输入的PipelineCommand长度有误！")
		return AllCommandExecutor(pipelineCommand[0])
	}

	var c []string
	cmd1 := exec.Command(pipelineCommand[0][0], pipelineCommand[0][1:]...)
	var outputBuf1 bytes.Buffer
	cmd1.Stdout = &outputBuf1
	if err := cmd1.Start(); err != nil {
		sprintf := fmt.Sprintf("Error: The first command can not be startup %s", err)
		return false, append(c, sprintf)
	}
	if err := cmd1.Wait(); err != nil {
		sprintf := fmt.Sprintf("Error: Couldn't wait for the first command: %s", err)
		return false, append(c, sprintf)
	}

	for i := 1; i < len(pipelineCommand); i++ {

		cmd2 := exec.Command(pipelineCommand[i][0], pipelineCommand[i][1:]...)
		sprintf := fmt.Sprintf("current command is %s", pipelineCommand[i])
		c = append(c, sprintf)

		cmd2.Stdin = &outputBuf1
		var outputBuf2 bytes.Buffer
		cmd2.Stdout = &outputBuf2
		if err := cmd2.Start(); err != nil {
			sprintf := fmt.Sprintf("Error: The second command can not be startup: %s", err)
			return false, append(c, sprintf)
		}
		if err := cmd2.Wait(); err != nil {
			sprintf := fmt.Sprintf("Error: Couldn't wait for the second command: %s", err)
			return false, append(c, sprintf)
		}

		// change
		outputBuf1 = outputBuf2
	}

	s := outputBuf1.String()
	split := strings.Split(s, "\n")

	return true, split
}

// PureResultSingleExecute 执行单行命令，忽略输出，只对执行成功与否负责
func PureResultSingleExecute(singleCommand []string) (resultOK bool) {

	cmd := exec.Command(singleCommand[0], singleCommand[1:]...)
	err := cmd.Run()
	if err != nil {
		log.ErrorF("指令 %s 执行 错误, 错误内容为 %s", singleCommand, err.Error())
		return false
	} else {
		log.DebugF("指令 %s 执行 成功", singleCommand)
		return true
	}
}

// PureResultSingleExecuteBatch 批量 执行单行命令，忽略输出，只对执行成功与否负责
func PureResultSingleExecuteBatch(singleCommandList [][]string) (resultOK bool) {

	result := true

	for _, singleCommand := range singleCommandList {
		if !PureResultSingleExecute(singleCommand) {
			result = false
		}
	}

	return result
}

// ReadTimeCommandExecutor 执行命令,并且实时返回结果
func ReadTimeCommandExecutor(singleLineCommand []string) bool {
	cmd := exec.Command(singleLineCommand[0], singleLineCommand[1:]...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.ErrorF("command  %v stdout error => %v", singleLineCommand, err)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.ErrorF("command  %v stderr error => %v", singleLineCommand, err)
	}

	if err := cmd.Start(); err != nil {
		log.ErrorF("command  %v runtime error => %v", singleLineCommand, err)
	}

	go realTimeOutput(stdout)
	go realTimeOutput(stderr)

	if err := cmd.Wait(); err != nil {
		log.ErrorF("command  %v result error => %v", singleLineCommand, err)
		return false
	}

	return true
}

func realTimeOutput(r io.Reader) {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		fmt.Println(scanner.Text())
	}
}

func collectOutput(r io.Reader, resultSlice []string) []string {
	scanner := bufio.NewScanner(r)
	for scanner.Scan() {
		resultLine := scanner.Text()

		resultSlice = append(resultSlice, resultLine)
		// debug usage
		//fmt.Println(resultLine)
	}

	return resultSlice
}

// HardCodeCommandExecutor 执行硬编码的shell命令，如 echo sda > sdasd ;  rpcinfo -p localhost 等
func HardCodeCommandExecutor(hardCodeCommand string) (bool, []string) {

	// result
	var resultSlice []string
	resultOk := true

	cmd := exec.Command("sh", "-c", hardCodeCommand)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.DebugF("hard code command  %v stdout error => %s", hardCodeCommand, err)
		resultOk = false
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.DebugF("hard code command  %v stderr error => %s", hardCodeCommand, err)
		resultOk = false
	}

	if err := cmd.Start(); err != nil {
		log.DebugF("hard code command  %v runtime error => %v", hardCodeCommand, err)
		resultOk = false
	}

	// 收集错误或者
	resultSlice = collectOutput(stdout, resultSlice)
	resultSlice = collectOutput(stderr, resultSlice)

	if err := cmd.Wait(); err != nil {
		log.DebugF("hard code command   %v result error => %v", hardCodeCommand, err)
		resultOk = false
	}

	log.DebugF("hard code command of [ %s ] result are =>  %v", hardCodeCommand, resultSlice)

	return resultOk, resultSlice
}
