package aws

import (
	"context"
	"testing"

	"github.com/tidbcloud/observability/pkg/common/metrics"

	"github.com/aws/aws-sdk-go-v2/aws"
	iamapi "github.com/aws/aws-sdk-go-v2/service/iam"
	"github.com/aws/aws-sdk-go-v2/service/iam/types"
	. "github.com/onsi/gomega"
	mocks "github.com/tidbcloud/observability/test/mockresourceworker/providers/aws"
	"go.uber.org/mock/gomock"
)

func newMockIAM(cfg aws.Config, mock iamClient) IAMClient {
	return IAMClient{
		cli: mock,
		cfg: cfg,
	}
}

func TestIAMCreateRole(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMockiamClient(ctl)
	iamclient := newMockIAM(cfg, mockclient)
	mockclient.EXPECT().CreateRole(context.TODO(), &iamapi.CreateRoleInput{
		RoleName:                 aws.String("test1"),
		AssumeRolePolicyDocument: aws.String("testdoc"),
	}).Return(&iamapi.CreateRoleOutput{
		Role: &types.Role{
			RoleName:                 aws.String("test1"),
			AssumeRolePolicyDocument: aws.String("testdoc"),
		},
	}, nil)
	g.Expect(iamclient.CreateRole(context.TODO(), "test1", "testdoc")).Should(Succeed())
}

func TestIAMGetRole(t *testing.T) {
	metrics.SetupMetrics()

	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMockiamClient(ctl)
	iamclient := newMockIAM(cfg, mockclient)
	mockclient.EXPECT().GetRole(context.TODO(), &iamapi.GetRoleInput{
		RoleName: aws.String("test1"),
	}).Return(&iamapi.GetRoleOutput{
		Role: &types.Role{
			RoleName:                 aws.String("test1"),
			AssumeRolePolicyDocument: aws.String("testdoc"),
		},
	}, nil)
	role, exists, err := iamclient.GetRole(context.TODO(), "test1")
	g.Expect(role).Should(Equal(&iamapi.GetRoleOutput{
		Role: &types.Role{
			RoleName:                 aws.String("test1"),
			AssumeRolePolicyDocument: aws.String("testdoc"),
		},
	}))
	g.Expect(exists).Should(Equal(true))
	g.Expect(err).ShouldNot(HaveOccurred())
	//notfound
	mockclient.EXPECT().GetRole(context.TODO(), &iamapi.GetRoleInput{
		RoleName: aws.String("notfound"),
	}).Return(nil, &types.NoSuchEntityException{})
	_, exists, err = iamclient.GetRole(context.TODO(), "notfound")
	g.Expect(exists).Should(Equal(false))
	g.Expect(err).ShouldNot(HaveOccurred())
	mockclient.EXPECT().GetRole(context.TODO(), &iamapi.GetRoleInput{
		RoleName: aws.String("errors"),
	}).Return(nil, &types.PolicyEvaluationException{})
	g.Expect(iamclient.GetRole(context.TODO(), "errors")).Error().Should(HaveOccurred())
}

func TestIAMListRolePolicies(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMockiamClient(ctl)
	iamclient := newMockIAM(cfg, mockclient)
	mockclient.EXPECT().ListRolePolicies(context.TODO(), &iamapi.ListRolePoliciesInput{
		RoleName: aws.String("test1"),
		Marker:   nil,
	}).Return(&iamapi.ListRolePoliciesOutput{
		PolicyNames: []string{
			"policy1",
			"policy2",
		},
	}, nil)
	g.Expect(iamclient.ListRolePolicies(context.TODO(), "test1", nil)).Error().ShouldNot(HaveOccurred())
}

func TestIAMPutRolePolicy(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMockiamClient(ctl)
	iamclient := newMockIAM(cfg, mockclient)
	mockclient.EXPECT().PutRolePolicy(context.TODO(), &iamapi.PutRolePolicyInput{
		RoleName:       aws.String("test1"),
		PolicyName:     aws.String("policy1"),
		PolicyDocument: aws.String("policyDoc"),
	}).Return(&iamapi.PutRolePolicyOutput{}, nil)
	g.Expect(iamclient.PutRolePolicy(context.TODO(), "test1", "policy1", "policyDoc")).Error().ShouldNot(HaveOccurred())
}

func TestIAMDeleteRolePolicy(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMockiamClient(ctl)
	iamclient := newMockIAM(cfg, mockclient)
	mockclient.EXPECT().DeleteRolePolicy(context.TODO(), &iamapi.DeleteRolePolicyInput{
		RoleName:   aws.String("test1"),
		PolicyName: aws.String("policy1"),
	}).Return(&iamapi.DeleteRolePolicyOutput{}, nil)
	g.Expect(iamclient.DeleteRolePolicy(context.TODO(), "test1", "policy1")).Error().ShouldNot(HaveOccurred())
}

func TestIAMDeleteRole(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMockiamClient(ctl)
	iamclient := newMockIAM(cfg, mockclient)
	mockclient.EXPECT().DeleteRole(context.TODO(), &iamapi.DeleteRoleInput{
		RoleName: aws.String("test1"),
	}).Return(&iamapi.DeleteRoleOutput{}, nil)
	g.Expect(iamclient.DeleteRole(context.TODO(), "test1")).ShouldNot(HaveOccurred())
	//notfound
	mockclient.EXPECT().DeleteRole(context.TODO(), &iamapi.DeleteRoleInput{
		RoleName: aws.String("notfound"),
	}).Return(&iamapi.DeleteRoleOutput{}, &types.NoSuchEntityException{})
	g.Expect(iamclient.DeleteRole(context.TODO(), "notfound")).ShouldNot(HaveOccurred())
	//other errors
	mockclient.EXPECT().DeleteRole(context.TODO(), &iamapi.DeleteRoleInput{
		RoleName: aws.String("errors"),
	}).Return(&iamapi.DeleteRoleOutput{}, &types.InvalidInputException{})
	g.Expect(iamclient.DeleteRole(context.TODO(), "errors")).Should(HaveOccurred())
}

func TestIAMUpdateAssumeRolePolicy(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	var cfg aws.Config
	mockclient := mocks.NewMockiamClient(ctl)
	iamclient := newMockIAM(cfg, mockclient)
	mockclient.EXPECT().UpdateAssumeRolePolicy(context.TODO(), &iamapi.UpdateAssumeRolePolicyInput{
		RoleName:       aws.String("test1"),
		PolicyDocument: aws.String("doc1"),
	}).Return(&iamapi.UpdateAssumeRolePolicyOutput{}, nil)
	g.Expect(iamclient.UpdateAssumeRolePolicy(context.TODO(), "test1", "doc1")).ShouldNot(HaveOccurred())
}
