package cloudwatch

import (
	"context"
	"encoding/json"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	cloudwatchLogsTypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"

	"github.com/grafana/grafana-plugin-sdk-go/backend"
	"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
	"github.com/stretchr/testify/assert"
)

func Test_executeLogAnomaliesQuery(t *testing.T) {
	origNewCWClient := NewCWClient
	t.Cleanup(func() {
		NewCWClient = origNewCWClient
	})

	var cli fakeCWLogsClient
	NewCWLogsClient = func(aws.Config) models.CWLogsClient {
		return &cli
	}

	t.Run("getCWLogsClient is called with correct suppression state", func(t *testing.T) {
		testcases := []struct {
			name                    string
			suppressionStateInQuery string
			result                  cloudwatchLogsTypes.SuppressionState
		}{
			{
				"suppressed state",
				"suppressed",
				cloudwatchLogsTypes.SuppressionStateSuppressed,
			},
			{
				"unsuppressed state",
				"unsuppressed",
				cloudwatchLogsTypes.SuppressionStateUnsuppressed,
			},
			{
				"empty state",
				"",
				"",
			},
			{
				"all state",
				"all",
				"",
			},
		}

		for _, tc := range testcases {
			t.Run(tc.name, func(t *testing.T) {
				cli = fakeCWLogsClient{anomalies: []cloudwatchLogsTypes.Anomaly{}}
				ds := newTestDatasource()

				_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
					Headers:       map[string]string{headerFromAlert: ""},
					PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
					Queries: []backend.DataQuery{
						{
							TimeRange: backend.TimeRange{From: time.Unix(0, 0), To: time.Unix(1, 0)},
							JSON: json.RawMessage(`{
						"queryMode":    "Logs",
						"logsMode": "Anomalies",
						"suppressionState": "` + tc.suppressionStateInQuery + `",
						"region": "us-east-1"
						}`),
						},
					},
				})
				assert.NoError(t, err)
				assert.Equal(t, tc.result, cli.calls.listAnomalies[0].SuppressionState)
			})
		}
	})
}

func Test_executeLogAnomaliesQuery_returns_data_frames(t *testing.T) {
	origNewCWClient := NewCWClient
	t.Cleanup(func() {
		NewCWClient = origNewCWClient
	})

	var cli fakeCWLogsClient
	NewCWLogsClient = func(aws.Config) models.CWLogsClient {
		return &cli
	}
	t.Run("returns log anomalies data frames", func(t *testing.T) {
		cli = fakeCWLogsClient{anomalies: []cloudwatchLogsTypes.Anomaly{
			{
				AnomalyId:          aws.String("anomaly-1"),
				AnomalyDetectorArn: aws.String("arn:aws:logs:us-east-1:123456789012:anomaly-detector:anomaly-detector-1"),
				FirstSeen:          1622505600000, // June 1, 2021 00:00:00 GMT
				LastSeen:           1622592000000, // June 2, 2021 00:00:00 GMT
				LogGroupArnList:    []string{"arn:aws:logs:us-east-1:1234567:log-group-1:id-1", "arn:aws:logs:us-east-1:1234567:log-group-2:id-2"},
				Description:        aws.String("Description 1"),
				State:              cloudwatchLogsTypes.StateActive,
				Priority:           aws.String("high"),
				PatternString:      aws.String(`{"ClusterName":"PetSite","Namespace":"default","Service":"service-petsite",,"instance":"instance"-5:Token-6,"job":"kubernetes-service-endpoints","pod_name":"pod_name"-9,"prom_metric_type":"counter"}`),
				Suppressed:         aws.Bool(false),
				Histogram: map[string]int64{
					"1622505600000": 5,
					"1622519200000": 10,
					"1622532800000": 7,
				},
			},
			{
				AnomalyId:          aws.String("anomaly-2"),
				AnomalyDetectorArn: aws.String("arn:aws:logs:us-east-1:123456789012:anomaly-detector:anomaly-detector-2"),
				FirstSeen:          1622592000000, // June 2, 2021 00:00:00 GMT
				LastSeen:           1622678400000, // June 3, 2021 00:00:00 GMT
				LogGroupArnList:    []string{"arn:aws:logs:us-east-1:1234567:log-group-1:id-3", "arn:aws:logs:us-east-1:1234567:log-group-2:id-4"},
				Description:        aws.String("Description 2"),
				State:              cloudwatchLogsTypes.StateSuppressed,
				Priority:           aws.String("low"),
				PatternString:      aws.String(`{"ClusterName":"PetSite","Namespace":"default","Service":"service-petsite","dotnet_collection_count_total":"dotnet_collection_count_total"-3}`),
				Suppressed:         aws.Bool(true),
				Histogram: map[string]int64{
					"1622592000000": 3,
				},
			},
		}}

		ds := newTestDatasource()

		resp, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
			Headers:       map[string]string{headerFromAlert: ""},
			PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
			Queries: []backend.DataQuery{
				{
					TimeRange: backend.TimeRange{From: time.Unix(0, 0), To: time.Unix(1, 0)},
					JSON: json.RawMessage(`{
							"queryMode":    "Logs",
							"logsMode": "Anomalies",
							"suppressionState": "all",
							"region": "us-east-1"
							}`),
				},
			},
		})

		assert.NoError(t, err)
		assert.Len(t, resp.Responses, 1)
		for _, r := range resp.Responses {
			assert.Len(t, r.Frames, 1)
			frame := r.Frames[0]
			assert.Equal(t, "Log anomalies", frame.Name)
			assert.Len(t, frame.Fields, 10)

			stateField := frame.Fields[0]
			assert.Equal(t, "state", stateField.Name)
			assert.Equal(t, "Active", stateField.At(0))
			assert.Equal(t, "Suppressed", stateField.At(1))

			descriptionField := frame.Fields[1]
			assert.Equal(t, "description", descriptionField.Name)
			assert.Equal(t, "Description 1", descriptionField.At(0))
			assert.Equal(t, "Description 2", descriptionField.At(1))

			priorityField := frame.Fields[2]
			assert.Equal(t, "priority", priorityField.Name)
			assert.Equal(t, "high", priorityField.At(0))
			assert.Equal(t, "low", priorityField.At(1))

			patternStringField := frame.Fields[3]
			assert.Equal(t, "patternString", patternStringField.Name)
			assert.Equal(t, `{"ClusterName":"PetSite","Namespace":"default","Service":"service-petsite",,"instance":"instance"-5:Token-6,"job":"kubernetes-service-endpoints","pod_name":"pod_name"-9,"prom_metric_type":"counter"}`, patternStringField.At(0))
			assert.Equal(t, `{"ClusterName":"PetSite","Namespace":"default","Service":"service-petsite","dotnet_collection_count_total":"dotnet_collection_count_total"-3}`, patternStringField.At(1))

			histogramField := frame.Fields[4]
			assert.Equal(t, "logTrend", histogramField.Name)

			histogram0 := histogramField.At(0).(*json.RawMessage)
			var histData0 map[string]int64
			err = json.Unmarshal(*histogram0, &histData0)
			assert.NoError(t, err)
			assert.Equal(t, int64(5), histData0["1622505600000"])
			assert.Equal(t, int64(10), histData0["1622519200000"])
			assert.Equal(t, int64(7), histData0["1622532800000"])

			firstSeenField := frame.Fields[5]
			assert.Equal(t, "firstSeen", firstSeenField.Name)
			assert.Equal(t, time.Unix(1622505600, 0), firstSeenField.At(0))
			assert.Equal(t, time.Unix(1622592000, 0), firstSeenField.At(1))

			lastSeenField := frame.Fields[6]
			assert.Equal(t, "lastSeen", lastSeenField.Name)
			assert.Equal(t, time.Unix(1622592000, 0), lastSeenField.At(0))
			assert.Equal(t, time.Unix(1622678400, 0), lastSeenField.At(1))

			suppressedField := frame.Fields[7]
			assert.Equal(t, "suppressed", suppressedField.Name)
			assert.Equal(t, false, suppressedField.At(0))
			assert.Equal(t, true, suppressedField.At(1))

			logGroupArnListField := frame.Fields[8]
			assert.Equal(t, "logGroupArnList", logGroupArnListField.Name)
			assert.Equal(t, "arn:aws:logs:us-east-1:1234567:log-group-1:id-1,arn:aws:logs:us-east-1:1234567:log-group-2:id-2", logGroupArnListField.At(0))
			assert.Equal(t, "arn:aws:logs:us-east-1:1234567:log-group-1:id-3,arn:aws:logs:us-east-1:1234567:log-group-2:id-4", logGroupArnListField.At(1))

			anomalyDetectorArnField := frame.Fields[9]
			assert.Equal(t, "anomalyArn", anomalyDetectorArnField.Name)
			assert.Equal(t, "arn:aws:logs:us-east-1:123456789012:anomaly-detector:anomaly-detector-1", anomalyDetectorArnField.At(0))
			assert.Equal(t, "arn:aws:logs:us-east-1:123456789012:anomaly-detector:anomaly-detector-2", anomalyDetectorArnField.At(1))
		}
	})
}
