package amimetadata

import (
	"fmt"
	"github.com/aws/amazon-ecs-cli/ecs-cli/modules/clients/aws/amimetadata/mock/sdk"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/golang/mock/gomock"
	"github.com/pkg/errors"
	"github.com/stretchr/testify/assert"
	"testing"
)

type Configurer func(ssmClient *mock_ssmiface.MockSSMAPI) *mock_ssmiface.MockSSMAPI

func TestMetadataClient_GetRecommendedECSLinuxAMI(t *testing.T) {
	tests := []struct {
		instanceTypes []string
		configureMock Configurer
		expectedErr   error
	}{
		{
			// validate that we use the ARM64 optimized AMI for Arm instances
			[]string{"a1.medium", "m6g.medium", "c6gd.16xlarge", "m6g.metal"},
			func(ssmClient *mock_ssmiface.MockSSMAPI) *mock_ssmiface.MockSSMAPI {
				ssmClient.EXPECT().GetParameter(gomock.Any()).Do(func(input *ssm.GetParameterInput) {
					assert.Equal(t, amazonLinux2ARM64RecommendedParameterName, *input.Name)
				}).Return(emptySSMParameterOutput(), nil)
				return ssmClient
			},
			nil,
		},
		{
			// validate that we use GPU optimized AMI for GPU instances
			[]string{"p2.large", "g4dn.xlarge"},
			func(ssmClient *mock_ssmiface.MockSSMAPI) *mock_ssmiface.MockSSMAPI {
				ssmClient.EXPECT().GetParameter(gomock.Any()).Do(func(input *ssm.GetParameterInput) {
					assert.Equal(t, amazonLinux2X86GPURecommendedParameterName, *input.Name)
				}).Return(emptySSMParameterOutput(), nil)
				return ssmClient
			},
			nil,
		},
		{
			// validate that we use the generic AMI for other instances
			[]string{"t2.micro", "m5ad.large", "c4.large", "i3.2xlarge"},
			func(ssmClient *mock_ssmiface.MockSSMAPI) *mock_ssmiface.MockSSMAPI {
				ssmClient.EXPECT().GetParameter(gomock.Any()).Do(func(input *ssm.GetParameterInput) {
					assert.Equal(t, amazonLinux2X86RecommendedParameterName, *input.Name)
				}).Return(emptySSMParameterOutput(), nil)
				return ssmClient
			},
			nil,
		},
		{
			// validate that we throw an error if the AMI is not available in a region
			[]string{"t2.micro"},
			func(ssmClient *mock_ssmiface.MockSSMAPI) *mock_ssmiface.MockSSMAPI {
				ssmClient.EXPECT().GetParameter(gomock.Any()).Do(func(input *ssm.GetParameterInput) {
					assert.Equal(t, amazonLinux2X86RecommendedParameterName, *input.Name)
				}).Return(nil, awserr.New(ssm.ErrCodeParameterNotFound, "some error", nil))
				return ssmClient
			},
			errors.New(fmt.Sprintf(
				"Could not find Recommended Amazon Linux 2 AMI %s in %s; the AMI may not be supported in this region: ParameterNotFound: some error",
				amazonLinux2X86RecommendedParameterName,
				"us-east-1")),
		},
		{
			// validate that we throw unexpected errors
			[]string{"t2.micro"},
			func(ssmClient *mock_ssmiface.MockSSMAPI) *mock_ssmiface.MockSSMAPI {
				ssmClient.EXPECT().GetParameter(gomock.Any()).Do(func(input *ssm.GetParameterInput) {
					assert.Equal(t, amazonLinux2X86RecommendedParameterName, *input.Name)
				}).Return(nil, errors.New("unexpected error"))
				return ssmClient
			},
			errors.New("unexpected error"),
		},
	}

	for _, test := range tests {
		for _, instanceType := range test.instanceTypes {
			m := newMockSSMAPI(t)
			test.configureMock(m)

			c := metadataClient{
				m,
				"us-east-1",
			}
			_, actualErr := c.GetRecommendedECSLinuxAMI(instanceType)

			if test.expectedErr == nil {
				assert.NoError(t, actualErr)
			} else {
				assert.EqualError(t, actualErr, test.expectedErr.Error())
			}
		}
	}
}

func newMockSSMAPI(t *testing.T) *mock_ssmiface.MockSSMAPI {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	return mock_ssmiface.NewMockSSMAPI(ctrl)
}

func emptySSMParameterOutput() *ssm.GetParameterOutput {
	outputJson := "{}"
	return &ssm.GetParameterOutput{
		Parameter: &ssm.Parameter{
			Value: &outputJson,
		},
	}
}
