package alerts

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/stretchr/testify/require"
	"testing"
)

var memoryCalc = &MetricPercentThresholdCalculator{
	Metric:  "memory",
	Percent: 0.2,
}

var diskCalculator = &MetricPercentThresholdCalculator{
	Metric:  "disk",
	Percent: 0.8,
}

var iopsCalculator = &MetricPercentThresholdCalculator{
	Metric:  "iops",
	Percent: 1.0,
}

func makeInstance(instanceIdentifier string, instanceType string, diskSize int64, piops *int64) *rds.DBInstance {
	return &rds.DBInstance{
		DBInstanceIdentifier: aws.String(instanceIdentifier),
		DBInstanceClass:      aws.String(instanceType),
		AllocatedStorage:     aws.Int64(diskSize),
		Iops:                 piops,
	}
}

func makeAlarm(metricName string, comparisonOperator string, period int64, evaluationPeriods int64, statistic *string, alarmDescription string, threshold float64, extendedStatistic *string) *cloudwatch.MetricAlarm {
	return &cloudwatch.MetricAlarm{
		MetricName:         aws.String(metricName),
		ComparisonOperator: aws.String(comparisonOperator),
		Period:             aws.Int64(period),
		EvaluationPeriods:  aws.Int64(evaluationPeriods),
		Statistic:          statistic,
		AlarmDescription:   aws.String(alarmDescription),
		Threshold:          aws.Float64(threshold),
		ExtendedStatistic:  extendedStatistic,
	}
}

func testInstance(t *testing.T, instance *rds.DBInstance, expectedMemory, expectedDisk, expectedIops float64) {
	mem, err := memoryCalc.Threshold(instance)
	require.Nil(t, err)
	require.Equal(t, expectedMemory, mem)

	disk, err := diskCalculator.Threshold(instance)
	require.Nil(t, err)
	require.Equal(t, expectedDisk, disk)

	iops, err := iopsCalculator.Threshold(instance)
	require.Nil(t, err)
	require.Equal(t, expectedIops, iops)
}

func TestPercentCalculator(t *testing.T) {
	smallInstanceNoPiops := makeInstance("test-instance", "db.t2.small", 100, nil)
	testInstance(t, smallInstanceNoPiops, 400, 80, 300)

	largeInstanceWithPiops := makeInstance("test-instance", "db.r3.large", 1000, aws.Int64(10000))
	testInstance(t, largeInstanceWithPiops, 3000, 800, 10000)

	massiveInstanceWithPiops := makeInstance("test-instance", "db.m4.10xlarge", 25000, aws.Int64(100000))
	testInstance(t, massiveInstanceWithPiops, 32000, 20000, 100000)
}

func TestAlarmEquality(t *testing.T) {
	smallInstanceNoPiops := makeInstance("test-instance", "db.t2.small", 100, nil)
	addAdvancedAlarm(MinimumAlarms, "TestMetric", cloudwatch.ComparisonOperatorGreaterThanThreshold, 1, "A test alarm of %s", 60, 3, aws.String("Average"), nil)
	requestedAlarm := requestedAlarms["TestMetric"]
	alarm := makeAlarm("TestMetric", cloudwatch.ComparisonOperatorGreaterThanThreshold, 60, 3, aws.String("Average"), "A test alarm of test-instance", 1, nil)

	match, err := requestedAlarm.Matches(alarm, smallInstanceNoPiops)
	require.Nil(t, err)
	require.True(t, match)

	alarm.Statistic = aws.String("Test")
	match, err = requestedAlarm.Matches(alarm, smallInstanceNoPiops)
	require.Nil(t, err)
	require.False(t, match)

	alarm.Statistic = nil
	match, err = requestedAlarm.Matches(alarm, smallInstanceNoPiops)
	require.Nil(t, err)
	require.False(t, match)

	alarm.Statistic = nil
	alarm.ExtendedStatistic = aws.String("p99")
	match, err = requestedAlarm.Matches(alarm, smallInstanceNoPiops)
	require.Nil(t, err)
	require.False(t, match)

	requestedAlarm.statistic = nil
	requestedAlarm.extendedStatistic = aws.String("p99")
	fmt.Printf("STAT %v %v\n", alarm.Statistic, requestedAlarm.Statistic())
	match, err = requestedAlarm.Matches(alarm, smallInstanceNoPiops)
	require.Nil(t, err)
	require.True(t, match)

	addPercentAlarm(MinimumAlarms, "TestMetric2", cloudwatch.ComparisonOperatorGreaterThanThreshold, "disk", 0.2, 1, "A test alarm of %s")
	requestedAlarm = requestedAlarms["TestMetric2"]
	alarm = makeAlarm("TestMetric2", cloudwatch.ComparisonOperatorGreaterThanThreshold, 60, 3, aws.String("Average"), "A test alarm of test-instance", 20, nil)

	match, err = requestedAlarm.Matches(alarm, smallInstanceNoPiops)
	require.Nil(t, err)
	require.True(t, match)
}
