package api

import (
	"bytes"
	"io/ioutil"
	"iwarma.ru/console/correlator/es"
	"iwarma.ru/console/correlator/events"
	"iwarma.ru/console/correlator/util"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"iwarma.ru/console/correlator/rules"
)

func TestAddRulesHandler(t *testing.T) {

	util.SetupTest(t)
	defer util.TearDownTest(t)

	client, err := es.NewElastic()
	if err != nil {
		t.Errorf("%v", err)
		return
	}

	err = events.ClearIndex(client, events.GetAggregatedIndexName(), events.GetNormalizedIndexName())
	if err != nil {
		t.Errorf("%v", err)
		return
	}

	// Prepare store
	store := rules.NewRuleStore(client)

	rulesStr := `
	[
		{
			"name":"Test2",
			"depth":"600s",
			"id":"4",
			"predicat":{
			   "name":"top_predicat",
			   "type":"query_string",
			   "field":"destination_host",
			   "value":[
				  {
					 "name":"top_predicat_child1",
					 "type":"match",
					 "field":"sign_category",
					 "value":"Firewall",
					 "parent":"top_predicat"
				  },
				  {
					 "name":"top_predicat_child2",
					 "type":"match",
					 "field":"device_vendor",
					 "value":"armaif",
					 "parent":"top_predicat"
				  }
			   ],
			   "parent":"top_predicat"
			},
			"actions":[
			   {
				  "type":"firewall",
				  "quick":true,
				  "action":"pass",
				  "armaif":"1",
				  "sensor":{
					 "ip":"1.1.1.1",
					 "key":"3uIhK/pCIKaLhkv9cmYg4V7DQ1Adt4zZLovThtbfaYzqr1YUdG4DdK6lKuqEjq0vfrDac2KczPbm7dwW",
					 "scheme":"http",
					 "secret":"5bYfRxkmDwFFY3WL/AHIooFTVQwHdDYSGT8I/6zvzwiSPAMrc9YNNS1CMRoSerxtUnkXRm2ZTEe4LtWo"
				  },
				  "enabled":true,
				  "gateway":"",
				  "protocol":"any",
				  "sequence":"5000",
				  "direction":"in",
				  "interface":"lan",
				  "ipprotocol":"inet",
				  "source_net":"any",
				  "description":"",
				  "source_port":"",
				  "destination_net":"any",
				  "destination_port":""
			   }
			],
			"multi":false
		 }
	 ]`

	body := bytes.NewBuffer([]byte(rulesStr))

	request := httptest.NewRequest("POST", "/add_many/", body)
	request.Header.Add("Content-Type", "application/json")
	w := httptest.NewRecorder()

	handler := AddRulesHandler(store)
	handler(w, request)

	resp := w.Result()

	if resp.StatusCode != http.StatusOK {
		t.Errorf("Bad response status code: %v", resp.StatusCode)
	}

	// Let's check what we have
	stat := store.GetStat()

	if stat.RuleCount != 1 {
		t.Errorf("Bad rule count. Expect 1 got %v", stat.RuleCount)
	}
}

func TestAddRulesHandler2(t *testing.T) {
	util.SetupTest(t)
	defer util.TearDownTest(t)

	client, err := es.NewElastic()
	if err != nil {
		t.Errorf("%v", err)
		return
	}

	err = events.ClearIndex(client, events.GetAggregatedIndexName(), events.GetNormalizedIndexName())
	if err != nil {
		t.Errorf("%v", err)
		return
	}

	// Prepare store
	store := rules.NewRuleStore(client)

	rulesStr := `
	[
		{
			"name":"Test2",
			"depth":"600s",
			"id":"4",
			"predicat":{
			   "name":"top_predicat",
			   "type":"query_string",
			   "field":"",
			   "value":[
				  {
					 
					 "type":"query_string",
					 "field":"sign_category",
					 
					 "operands":"sign_category:\"Firewall\""
				  },
				  {
					 
					 "type":"query_string",
					 "field":"",
					 "operands":"device_vendor:\"armaif\" ",
					 
				  }
			   ],
			  
			},
			"actions":[
			   {
				  "type":"firewall",
				  "quick":true,
				  "action":"pass",
				  "armaif":"1",
				  "sensor":{
					 "ip":"1.1.1.1",
					 "key":"3uIhK/pCIKaLhkv9cmYg4V7DQ1Adt4zZLovThtbfaYzqr1YUdG4DdK6lKuqEjq0vfrDac2KczPbm7dwW",
					 "scheme":"http",
					 "secret":"5bYfRxkmDwFFY3WL/AHIooFTVQwHdDYSGT8I/6zvzwiSPAMrc9YNNS1CMRoSerxtUnkXRm2ZTEe4LtWo"
				  },
				  "enabled":true,
				  "gateway":"",
				  "protocol":"any",
				  "sequence":"5000",
				  "direction":"in",
				  "interface":"lan",
				  "ipprotocol":"inet",
				  "source_net":"any",
				  "description":"",
				  "source_port":"",
				  "destination_net":"any",
				  "destination_port":""
			   }
			],
			"multi":false
		 }
	 ]`

	body := bytes.NewBuffer([]byte(rulesStr))

	request := httptest.NewRequest("POST", "/add_many/", body)
	w := httptest.NewRecorder()

	handler := AddRulesHandler(store)
	handler(w, request)

	resp := w.Result()

	if resp.StatusCode != http.StatusBadRequest {
		t.Errorf("Bad response status code: %v", resp.StatusCode)
	}

	// Let's check what we have
	stat := store.GetStat()

	if stat.RuleCount != 0 {
		t.Errorf("Bad rule count. Expect 0 got %v", stat.RuleCount)
	}
}

func TestClearStoreHandler(t *testing.T) {
	util.SetupTest(t)
	defer util.TearDownTest(t)

	client, err := es.NewElastic()
	if err != nil {
		t.Errorf("%v", err)
		return
	}

	err = events.ClearIndex(client, events.GetAggregatedIndexName(), events.GetNormalizedIndexName())
	if err != nil {
		t.Errorf("%v", err)
		return
	}

	// Prepare store
	store := rules.NewRuleStore(client)

	actions := make([]rules.Action, 1)
	action := rules.TestAction{
		PerformError:        true,
		ParseInterfaceError: false,
		MarshalError:        false,
		UnmarshalError:      false,
	}
	actions[0] = &action

	store.AddRules(rules.Rule{
		Id:          "1",
		Name:        "TestRule",
		Description: "Some long description",
		Multi:       false,
		Depth:       time.Second * 30,
		Predicate:   rules.NewPredicate("", "device_vendor:TestDevice"),
		Actions:     actions,
	})

	request := httptest.NewRequest("GET", "/clear/", nil)
	w := httptest.NewRecorder()

	handler := ClearStoreHandler(store)
	handler(w, request)

	resp := w.Result()

	if resp.StatusCode != http.StatusOK {
		t.Errorf("Bad response status code: %v", resp.StatusCode)
	}

	// Check what we have
	stat := store.GetStat()
	if stat.RuleCount != 0 {
		t.Errorf("Bad rule count. Expect 0, got %v", stat.RuleCount)
	}
}

func TestCorrelatorStatHandler(t *testing.T) {
	util.SetupTest(t)
	defer util.TearDownTest(t)

	client, err := es.NewElastic()
	if err != nil {
		t.Errorf("%v", err)
		return
	}

	err = events.ClearIndex(client, events.GetAggregatedIndexName(), events.GetNormalizedIndexName())
	if err != nil {
		t.Errorf("%v", err)
		return
	}

	// Prepare store
	store := rules.NewRuleStore(client)

	actions := make([]rules.Action, 1)
	action := rules.TestAction{
		PerformError:        true,
		ParseInterfaceError: false,
		MarshalError:        false,
		UnmarshalError:      false,
	}
	actions[0] = &action

	store.AddRules(rules.Rule{
		Id:          "1",
		Name:        "TestRule",
		Description: "Some long description",
		Multi:       false,
		Depth:       time.Second * 30,
		Predicate:   rules.NewPredicate("", "device_vendor:TestDevice"),
		Actions:     actions,
	})

	request := httptest.NewRequest("GET", "/stat/", nil)
	w := httptest.NewRecorder()
	duration := time.Second * 30
	ruleStore := rules.NewRuleStore(client)
	eventsStore := events.NewAggregatedEventStore(client, events.TimeWindow{
		Begin: time.Now().UTC(),
		End:   time.Now().UTC().Add(duration),
	})
	handler := StatHandler(eventsStore, ruleStore)
	handler(w, request)

	resp := w.Result()

	if resp.StatusCode != http.StatusOK {
		t.Errorf("Bad response status code: %v", resp.StatusCode)
	}

	// Let's check response
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()

	if string(body) != `{"aggregator":{"events_processed":0,"events_aggregated":0,"average_iteration":{"value":"0s"}},"correlator":{"rule_count":0,"average_time":{"value":"0s"},"average_rule_time":{},"incident_count":0,"events_count":0,"errors":{}}}` {
		t.Errorf("Bad response: %v", string(body))
	}
}
