package rules

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"iwarma.ru/console/correlator/events"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/spf13/viper"

	log "github.com/sirupsen/logrus"
	"iwarma.ru/console/correlator/config"
)

func TestExecActionGetType(t *testing.T) {
	var action ExecAction

	if action.GetType() != ExecActionType {
		t.Errorf("Got bad action type: %v", action.GetType())
	}
}

func TestExecActionToInterface(t *testing.T) {
	action := ExecAction{
		Path: "/usr/bin/bash",
		Args: "-c \"go version\"",
		Env:  "DEBUG=1 VERBOSE=1",
		Cwd:  "/usr/bin",
	}

	m, err := action.ToInterface()
	if err != nil {
		t.Errorf("Can't create interface: %v", err)
	}

	action_type, ok := m["type"].(string)
	if !ok {
		t.Error("Can't get type")
	}

	if action_type != ExecActionType {
		t.Errorf("Got bad type: %v", t)
	}

	action_path, ok := m["path"].(string)
	if !ok {
		t.Error("Can't get path")
	}

	if action_path != action.Path {
		t.Errorf("Got bad path: %v", action_path)
	}

	action_args, ok := m["args"].(string)
	if !ok {
		t.Error("Can't get args")
	}

	if action_args != action.Args {
		t.Errorf("Got bad args: %v", action_args)
	}

	action_env, ok := m["env"].(string)
	if !ok {
		t.Error("Can't get env")
	}

	if action_env != action.Env {
		t.Errorf("Bad action env: %v", action_env)
	}

	action_cwd, ok := m["cwd"].(string)
	if !ok {
		t.Error("Can't get cwd")
	}

	if action_cwd != action.Cwd {
		t.Errorf("Bad action cwd: %v", action_cwd)
	}
}

func TestExecActionMarshall(t *testing.T) {
	action := ExecAction{
		Path: "/usr/bin/bash",
		Args: "-c \"go version\"",
		Env:  "DEBUG=1 VERBOSE=1",
		Cwd:  "/usr/bin",
	}

	b, err := json.Marshal(action)
	if err != nil {
		t.Errorf("Can't serialize action: %v", err)
	}

	if string(b) != `{"args":"-c \"go version\"","cwd":"/usr/bin","env":"DEBUG=1 VERBOSE=1","path":"/usr/bin/bash","type":"exec"}` {
		t.Errorf("Bad action serializer result: %v", string(b))
	}
}

func TestExecActionUnmarshall(t *testing.T) {
	str := `{"args":"-c \"go version\"","cwd":"/usr/bin","env":"DEBUG=1 VERBOSE=1","path":"/usr/bin/bash","type":"exec"}`

	var action ExecAction

	err := json.Unmarshal([]byte(str), &action)
	if err != nil {
		t.Errorf("Can't unmarshall action: %v", err)
	}

	if action.Path != "/usr/bin/bash" {
		t.Errorf("Got bad path: %v", action.Path)
	}

	if action.Args != "-c \"go version\"" {
		t.Errorf("Got bad args: %v", action.Args)
	}

	if action.Env != "DEBUG=1 VERBOSE=1" {
		t.Errorf("Got bad env: %v", action.Env)
	}

	if action.Cwd != "/usr/bin" {
		t.Errorf("Got bad cwd: %v", action.Cwd)
	}
}

func TestExecActionUnmarshallBad(t *testing.T) {
	str := `{"args":"-c \"go version\"","cwd":"/usr/bin","env":"DEBUG=1 VERBOSE=1","path":"/usr/bin/bash","type":"bash"}`

	var action ExecAction

	err := json.Unmarshal([]byte(str), &action)
	if err == nil {
		t.Error("Can unmarshall bad json")
	}
}

func TestExecParseInterface1(t *testing.T) {
	s := "Test"

	action := &ExecAction{}
	err := action.ParseInterface(s)
	if err == nil {
		t.Error("Can process bad interface")
	}
}

func TestExecParseInterface2(t *testing.T) {
	m := make(map[string]interface{})

	m["type"] = 3

	action := &ExecAction{}
	err := action.ParseInterface(m)
	if err == nil {
		t.Error("Can process bad type")
	}
}

func TestExecParseInterface3(t *testing.T) {
	m := make(map[string]interface{})

	action := &ExecAction{}
	err := action.ParseInterface(m)
	if err == nil {
		t.Error("Can process bad type")
	}
}

func TestExecParseInterface4(t *testing.T) {
	m := make(map[string]interface{})

	m["type"] = "bash"

	action := &ExecAction{}
	err := action.ParseInterface(m)
	if err == nil {
		t.Error("Can process bad type")
	}
}

func TestExecParseInterface5(t *testing.T) {
	m := make(map[string]interface{})

	m["type"] = ExecActionType

	action := &ExecAction{}
	err := action.ParseInterface(m)
	if err == nil {
		t.Error("Can process bad type")
	}
}

func TestExecParseInterface6(t *testing.T) {
	m := make(map[string]interface{})

	m["type"] = ExecActionType
	m["path"] = 15

	action := &ExecAction{}
	err := action.ParseInterface(m)
	if err == nil {
		t.Error("Can process bad type")
	}
}

func TestExecParseInterface7(t *testing.T) {
	m := make(map[string]interface{})

	m["type"] = ExecActionType
	m["path"] = "/usr/bin"
	m["args"] = "version"
	m["env"] = "DEBUG"
	m["cwd"] = "/usr"

	action := &ExecAction{}
	err := action.ParseInterface(m)
	if err != nil {
		t.Error("Can't process good interface")
	}

	if action.Path != m["path"] {
		t.Errorf("Got bad path: %v", action.Path)
	}

	if action.Args != m["args"] {
		t.Errorf("Got bad args: %v", action.Args)
	}

	if action.Env != m["env"] {
		t.Errorf("Got bad env: %v", action.Env)
	}

	if action.Cwd != m["cwd"] {
		t.Errorf("Got bad cwd: %v", action.Cwd)
	}
}

func TestExecActionPerform(t *testing.T) {
	file, err := ioutil.TempFile("", "test_action.*.sh")
	if err != nil {
		t.Error(err)
		return
	}
	defer os.Remove(file.Name())

	script := `#!/bin/bash
	echo "$@ $T_1 $(pwd)"`

	_, err = file.Write([]byte(script))
	if err != nil {
		t.Error(err)
	}

	err = file.Sync()
	if err != nil {
		t.Error(err)
	}

	err = file.Close()
	if err != nil {
		t.Error(err)
	}

	err = os.Chmod(file.Name(), 0775)
	if err != nil {
		t.Error(err)
	}

	path := filepath.Dir(file.Name())

	action := ExecAction{
		Path: file.Name(),
		Args: "AAA BBB",
		Env:  "T_1=RRR",
		Cwd:  path,
	}

	eventsArray := make([]*events.Event, 1)
	eventsArray[0] = &events.Event{
		events.FirstEvent: time.Now(),
		events.LastEvent:  time.Now(),
		events.EventCount: 5,
		"event_severity":  5,
		"sign_name":       file.Name(),
	}

	var buf bytes.Buffer
	log.SetOutput(&buf)
	log.SetLevel(log.DebugLevel)

	viper.Set(config.Verbose, true)
	err = action.Perform(&eventsArray)
	if err != nil {
		t.Error(err)
	}

	if !strings.Contains(buf.String(), `Executabe output:\nAAA BBB RRR /tmp\n`) {
		t.Errorf("Bad executable output: %v", buf.String())
	}
	log.SetLevel(log.ErrorLevel)
}
