package csprofiles

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/antonmedv/expr"
	"github.com/antonmedv/expr/vm"
	"github.com/crowdsecurity/crowdsec/pkg/csconfig"
	"github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
	"github.com/crowdsecurity/crowdsec/pkg/models"
)

var (
	scope     = "Country"
	typ       = "ban"
	simulated = false
	duration  = "1h"

	value    = "CH"
	scenario = "ssh-bf"
)

func TestEvaluateProfile(t *testing.T) {
	type args struct {
		profile *csconfig.ProfileCfg
		Alert   *models.Alert
	}
	tests := []struct {
		name                  string
		args                  args
		expectedDecisionCount int // count of expected decisions
		expectedMatchStatus   bool
	}{
		{
			name: "simple pass single expr",
			args: args{
				profile: &csconfig.ProfileCfg{
					Filters:        []string{fmt.Sprintf("Alert.GetScenario() == \"%s\"", scenario)},
					RuntimeFilters: []*vm.Program{},
				},
				Alert: &models.Alert{Remediation: true, Scenario: &scenario},
			},
			expectedDecisionCount: 0,
			expectedMatchStatus:   true,
		},
		{
			name: "simple fail single expr",
			args: args{
				profile: &csconfig.ProfileCfg{
					Filters:        []string{"Alert.GetScenario() == \"Foo\""},
					RuntimeFilters: []*vm.Program{},
				},
				Alert: &models.Alert{Remediation: true},
			},
			expectedDecisionCount: 0,
			expectedMatchStatus:   false,
		},
		{
			name: "1 expr fail 1 expr pass should still eval to match",
			args: args{
				profile: &csconfig.ProfileCfg{
					Filters:        []string{"1==1", "1!=1"},
					RuntimeFilters: []*vm.Program{},
				},
				Alert: &models.Alert{Remediation: true},
			},
			expectedDecisionCount: 0,
			expectedMatchStatus:   true,
		},
		{
			name: "simple filter with  2 decision",
			args: args{
				profile: &csconfig.ProfileCfg{
					Filters:        []string{"1==1"},
					RuntimeFilters: []*vm.Program{},
					Decisions: []models.Decision{
						{Type: &typ, Scope: &scope, Simulated: &simulated, Duration: &duration},
						{Type: &typ, Scope: &scope, Simulated: &simulated, Duration: &duration},
					},
				},
				Alert: &models.Alert{Remediation: true, Scenario: &scenario, Source: &models.Source{Value: &value}},
			},
			expectedDecisionCount: 2,
			expectedMatchStatus:   true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			for _, filter := range tt.args.profile.Filters {
				runtimeFilter, _ := expr.Compile(filter, expr.Env(exprhelpers.GetExprEnv(map[string]interface{}{"Alert": &models.Alert{}})))
				tt.args.profile.RuntimeFilters = append(tt.args.profile.RuntimeFilters, runtimeFilter)
			}
			got, got1, _ := EvaluateProfile(tt.args.profile, tt.args.Alert)
			if !reflect.DeepEqual(len(got), tt.expectedDecisionCount) {
				t.Errorf("EvaluateProfile() got = %+v, want %+v", got, tt.expectedDecisionCount)
			}
			if got1 != tt.expectedMatchStatus {
				t.Errorf("EvaluateProfile() got1 = %v, want %v", got1, tt.expectedMatchStatus)
			}
		})
	}
}
