package scalers

import (
	"context"
	"fmt"
	"net/http"
	"testing"
)

const (
	testInvalidRestAPITemplate = "testInvalidRestAPITemplate"
)

type parseActiveMQMetadataTestData struct {
	name       string
	metadata   map[string]string
	authParams map[string]string
	isError    bool
}

type activeMQMetricIdentifier struct {
	metadataTestData *parseActiveMQMetadataTestData
	scalerIndex      int
	name             string
}

// Setting metric identifier mock name
var activeMQMetricIdentifiers = []activeMQMetricIdentifier{
	{&testActiveMQMetadata[1], 0, "s0-activemq-testQueue"},
	{&testActiveMQMetadata[9], 1, "s1-activemq-testQueue"},
}

var testActiveMQMetadata = []parseActiveMQMetadataTestData{
	{
		name:       "nothing passed",
		metadata:   map[string]string{},
		authParams: map[string]string{},
		isError:    true,
	},
	{
		name: "properly formed metadata",
		metadata: map[string]string{
			"managementEndpoint": "localhost:8161",
			"destinationName":    "testQueue",
			"brokerName":         "localhost",
			"targetQueueSize":    "10",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: false,
	},
	{
		name: "no metricName passed, metricName is generated from destinationName",
		metadata: map[string]string{
			"managementEndpoint": "localhost:8161",
			"destinationName":    "testQueue",
			"brokerName":         "localhost",
			"targetQueueSize":    "10",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: false,
	},
	{
		name: "Invalid targetQueueSize using a string",
		metadata: map[string]string{
			"managementEndpoint": "localhost:8161",
			"destinationName":    "testQueue",
			"brokerName":         "localhost",
			"targetQueueSize":    "AA",
			"metricName":         "testMetricName",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: true,
	},
	{
		name: "missing management endpoint should fail",
		metadata: map[string]string{
			"destinationName": "testQueue",
			"brokerName":      "localhost",
			"targetQueueSize": "10",
			"metricName":      "testMetricName",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: true,
	},
	{
		name: "missing destination name, should fail",
		metadata: map[string]string{
			"managementEndpoint": "localhost:8161",
			"brokerName":         "localhost",
			"targetQueueSize":    "10",
			"metricName":         "testMetricName",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: true,
	},
	{
		name: "missing broker name, should fail",
		metadata: map[string]string{
			"managementEndpoint": "localhost:8161",
			"destinationName":    "testQueue",
			"targetQueueSize":    "10",
			"metricName":         "testMetricName",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: true,
	},
	{
		name: "missing username, should fail",
		metadata: map[string]string{
			"managementEndpoint": "localhost:8161",
			"destinationName":    "testQueue",
			"brokerName":         "localhost",
			"targetQueueSize":    "10",
			"metricName":         "testMetricName",
		},
		authParams: map[string]string{
			"password": "pass123",
		},
		isError: true,
	},
	{
		name: "missing password, should fail",
		metadata: map[string]string{
			"managementEndpoint": "localhost:8161",
			"destinationName":    "testQueue",
			"brokerName":         "localhost",
			"targetQueueSize":    "10",
			"metricName":         "testMetricName",
		},
		authParams: map[string]string{
			"username": "testUsername",
		},
		isError: true,
	},
	{
		name: "properly formed metadata with restAPITemplate",
		metadata: map[string]string{
			"restAPITemplate": "http://localhost:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=testQueue/QueueSize",
			"targetQueueSize": "10",
			"metricName":      "testMetricName",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: false,
	},
	{
		name: "invalid restAPITemplate, should fail",
		metadata: map[string]string{
			"restAPITemplate": testInvalidRestAPITemplate,
			"targetQueueSize": "10",
			"metricName":      "testMetricName",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: true,
	},
	{
		name: "missing username, should fail",
		metadata: map[string]string{
			"restAPITemplate": "http://localhost:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=testQueue/QueueSize",
			"targetQueueSize": "10",
			"metricName":      "testMetricName",
		},
		authParams: map[string]string{
			"password": "pass123",
		},
		isError: true,
	},
	{
		name: "missing password, should fail",
		metadata: map[string]string{
			"restAPITemplate": "http://localhost:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost,destinationType=Queue,destinationName=testQueue/QueueSize",
			"targetQueueSize": "10",
			"metricName":      "testMetricName",
		},
		authParams: map[string]string{
			"username": "testUsername",
		},
		isError: true,
	},
}

func TestActiveMQDefaultCorsHeader(t *testing.T) {
	metadata := map[string]string{"managementEndpoint": "localhost:8161", "destinationName": "queue1", "brokerName": "broker-activemq", "username": "myUserName", "password": "myPassword"}
	meta, err := parseActiveMQMetadata(&ScalerConfig{TriggerMetadata: metadata, AuthParams: nil})

	if err != nil {
		t.Error("Expected success but got error", err)
	}
	if !(meta.corsHeader == "http://localhost:8161") {
		t.Errorf("Expected http://localhost:8161 but got %s", meta.corsHeader)
	}
}

func TestActiveMQCorsHeader(t *testing.T) {
	metadata := map[string]string{"managementEndpoint": "localhost:8161", "destinationName": "queue1", "brokerName": "broker-activemq", "username": "myUserName", "password": "myPassword", "corsHeader": "test"}
	meta, err := parseActiveMQMetadata(&ScalerConfig{TriggerMetadata: metadata, AuthParams: nil})

	if err != nil {
		t.Error("Expected success but got error", err)
	}
	if !(meta.corsHeader == "test") {
		t.Errorf("Expected test but got %s", meta.corsHeader)
	}
}

func TestParseActiveMQMetadata(t *testing.T) {
	for _, testData := range testActiveMQMetadata {
		t.Run(testData.name, func(t *testing.T) {
			metadata, err := parseActiveMQMetadata(&ScalerConfig{TriggerMetadata: testData.metadata, AuthParams: testData.authParams})
			if err != nil && !testData.isError {
				t.Error("Expected success but got error", err)
			}
			if testData.isError && err == nil {
				t.Error("Expected error but got success")
			}
			if metadata != nil && metadata.password != "" && metadata.password != testData.authParams["password"] {
				t.Error("Expected password from configuration but found something else: ", metadata.password)
				fmt.Println(testData)
			}
		})
	}
}

var testDefaultTargetQueueSize = []parseActiveMQMetadataTestData{
	{
		name: "properly formed metadata",
		metadata: map[string]string{
			"managementEndpoint": "localhost:8161",
			"destinationName":    "testQueue",
			"brokerName":         "localhost",
		},
		authParams: map[string]string{
			"username": "testUsername",
			"password": "pass123",
		},
		isError: false,
	},
}

func TestParseDefaultTargetQueueSize(t *testing.T) {
	for _, testData := range testDefaultTargetQueueSize {
		t.Run(testData.name, func(t *testing.T) {
			metadata, err := parseActiveMQMetadata(&ScalerConfig{TriggerMetadata: testData.metadata, AuthParams: testData.authParams})
			switch {
			case err != nil && !testData.isError:
				t.Error("Expected success but got error", err)
			case testData.isError && err == nil:
				t.Error("Expected error but got success")
			case metadata.targetQueueSize != defaultTargetQueueSize:
				t.Error("Expected default targetQueueSize =", defaultTargetQueueSize, "but got", metadata.targetQueueSize)
			}
		})
	}
}

func TestActiveMQGetMetricSpecForScaling(t *testing.T) {
	for _, testData := range activeMQMetricIdentifiers {
		ctx := context.Background()
		metadata, err := parseActiveMQMetadata(&ScalerConfig{TriggerMetadata: testData.metadataTestData.metadata, AuthParams: testData.metadataTestData.authParams, ScalerIndex: testData.scalerIndex})
		if err != nil {
			t.Fatal("Could not parse metadata:", err)
		}
		mockActiveMQScaler := activeMQScaler{
			metadata:   metadata,
			httpClient: http.DefaultClient,
		}

		metricSpec := mockActiveMQScaler.GetMetricSpecForScaling(ctx)
		metricName := metricSpec[0].External.Metric.Name
		if metricName != testData.name {
			t.Errorf("Wrong External metric source name: %s, expected: %s", metricName, testData.name)
		}
	}
}
