package actions

import (
	"encoding/json"
	"fmt"
	"os/exec"
	"strings"

	"github.com/k4lipso/gokill/internal"
)

type Command struct {
	Command    string   `json:"command"`
	ActionChan ActionResultChan
}

func isCommandAvailable(name string) bool {
  cmd := exec.Command("/bin/sh", "-c", "command -v "+name)
  if err := cmd.Run(); err != nil {
		return false
  }

  return true
}

func (c Command) DryExecute() {
	internal.LogDoc(c).Infof("Test Executing Command: %s", c.Command)
	command, _, err := c.splitCommandString()

	if err != nil {
		internal.LogDoc(c).Errorf("Error during argument parsing of command '%s'", c.Command)
		c.ActionChan <- fmt.Errorf("Error on Command action: %s", err)
		return
	}

	isAvailable := isCommandAvailable(command)

	if !isAvailable {
		internal.LogDoc(c).Warningf("Command %s not found", command)
		c.ActionChan <- fmt.Errorf("Command %s not found!", command)
		return
	}

	c.ActionChan <- nil
}

func (c Command) splitCommandString() (string, []string, error) {
	splitted := strings.Fields(c.Command)

	if len(splitted) == 0 {
		return "", nil, fmt.Errorf("Command is empty")
	}

	if len(splitted) == 1 {
		return splitted[0], []string(nil), nil
	}

	return splitted[0], splitted[1:], nil
}

func (c Command) Execute() {
	command, args, err := c.splitCommandString()
	internal.LogDoc(c).Infof("Executing command: ", c.Command)

	if err != nil {
		c.ActionChan <- err
		return
	}

	cmd := exec.Command(command, args...)

	stdout, err := cmd.Output()

	if err != nil {
		internal.LogDoc(c).Errorf("%s", err.Error())
		c.ActionChan <- fmt.Errorf("Executing Command '%s' failed: %s", c.Command, err)
	}

	internal.LogDoc(c).Infof("Command Output:\n%s", string(stdout[:]))

	c.ActionChan <- nil
}

func CreateCommand(config internal.ActionConfig, c ActionResultChan) (Command, error) {
	result := Command{}

	err := json.Unmarshal(config.Options, &result)

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

	if result.Command == "" {
		return Command{}, internal.OptionMissingError{"command"}
	}

	result.ActionChan = c

	return result, nil
}

func (cc Command) Create(config internal.ActionConfig, c ActionResultChan) (Action, error) {
	return CreateCommand(config, c)
}

func (p Command) GetName() string {
	return "Command"
}

func (p Command) GetDescription() string {
	return "Invokes given command using exec."
}

func (p Command) GetExample() string {
	return `
	{
		"type": "Command",
		"options": {
			"command": "srm /path/to/file"
		}
	}
	`
}

func (p Command) GetOptions() []internal.ConfigOption {
	return []internal.ConfigOption{
		{"command", "string", "command to execute", ""},
	}
}
