package gcp

import (
	"context"
	"errors"
	"os"
	"testing"

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

	cloudiam "cloud.google.com/go/iam"
	. "github.com/onsi/gomega"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers/gcp/giam"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers/gcp/giam/fake"
	mocks "github.com/tidbcloud/observability/test/mockresourceworker/providers/gcp/gcs"
	"go.uber.org/mock/gomock"
	"google.golang.org/api/googleapi"
	iam "google.golang.org/api/iam/v1"
)

func newMockIAM(mock giam.Iamclient, gcsmock CloudStorageClient) *IAMClient {
	return &IAMClient{
		cscli:   gcsmock,
		service: mock,
		project: "project",
	}
}
func TestIAMNew(t *testing.T) {
	g := NewGomegaWithT(t)
	os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "../../../../test/files/application_default_credentials.json")
	defer os.Unsetenv("GOOGLE_APPLICATION_CREDENTIALS")
	g.Expect(NewIAM("project1", "region")).Error().Should(Succeed())
}
func TestIAMCreate(t *testing.T) {
	metrics.SetupMetrics()

	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	mockclient := mocks.NewMockCloudStorageClient(ctl)
	gcsclient := newMockGCS(mockclient)
	iamclient := &fake.FakeGiamClient{
		Createfunc: func(name string, createserviceaccountrequest *iam.CreateServiceAccountRequest) (*iam.ServiceAccount, error) {
			return &iam.ServiceAccount{}, nil
		},
	}
	iamcli := newMockIAM(iamclient, *gcsclient)
	g.Expect(iamcli.CreateServiceAccount(context.Background(), "test01")).Error().Should(Succeed())
	iamclient.Createfunc = func(name string, createserviceaccountrequest *iam.CreateServiceAccountRequest) (*iam.ServiceAccount, error) {
		return &iam.ServiceAccount{}, errors.New("errors")
	}
	g.Expect(iamcli.CreateServiceAccount(context.Background(), "test01")).Error().Should(HaveOccurred())
}

func TestIAMDelete(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	mockclient := mocks.NewMockCloudStorageClient(ctl)
	gcsclient := newMockGCS(mockclient)
	iamclient := &fake.FakeGiamClient{
		Deletefunc: func(name string) (*iam.Empty, error) {
			return &iam.Empty{}, nil
		},
	}
	iamcli := newMockIAM(iamclient, *gcsclient)
	g.Expect(iamcli.DeleteServiceAccount(context.Background(), "test01")).Error().Should(Succeed())
	iamclient.Deletefunc = func(name string) (*iam.Empty, error) {
		return &iam.Empty{}, errors.New("errors")
	}
	g.Expect(iamcli.DeleteServiceAccount(context.Background(), "test01")).Error().Should(HaveOccurred())
}

func TestIAMGet(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	mockclient := mocks.NewMockCloudStorageClient(ctl)
	gcsclient := newMockGCS(mockclient)
	iamclient := &fake.FakeGiamClient{
		Getfunc: func(name string) (*iam.ServiceAccount, error) {
			return &iam.ServiceAccount{
				Email: "test@email.com",
			}, nil
		},
	}
	iamcli := newMockIAM(iamclient, *gcsclient)
	accout, exits, err := iamcli.GetServiceAccount(context.Background(), "test01")
	g.Expect(exits).Should(BeTrue())
	g.Expect(err).Should(Succeed())
	g.Expect(accout).Should(Equal(&iam.ServiceAccount{
		Email: "test@email.com",
	}))
	//not found
	iamclient.Getfunc = func(name string) (*iam.ServiceAccount, error) {
		var gapierr googleapi.Error
		gapierr.Code = 404
		return nil, &gapierr
	}
	_, exits, err = iamcli.GetServiceAccount(context.Background(), "test01")
	g.Expect(exits).ShouldNot(BeTrue())
	g.Expect(err).Should(Succeed())
	//error
	iamclient.Getfunc = func(name string) (*iam.ServiceAccount, error) {
		return nil, errors.New("errors")
	}
	_, exits, err = iamcli.GetServiceAccount(context.Background(), "test01")
	g.Expect(exits).ShouldNot(BeTrue())
	g.Expect(err).Should(HaveOccurred())
}

func TestPolicyGet(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	mockclient := mocks.NewMockCloudStorageClient(ctl)
	gcsclient := newMockGCS(mockclient)
	iamclient := &fake.FakeGiamClient{
		Getiamfunc: func(resource string) (*iam.Policy, error) {
			return &iam.Policy{}, nil
		},
	}
	iamcli := newMockIAM(iamclient, *gcsclient)
	g.Expect(iamcli.GetServiceAccountIAM(context.Background(), "test01")).Error().Should(Succeed())
	//error
	iamclient.Getiamfunc = func(resource string) (*iam.Policy, error) {
		return nil, errors.New("errors")
	}
	g.Expect(iamcli.GetServiceAccountIAM(context.Background(), "test01")).Error().Should(HaveOccurred())
}

func TestPolicySet(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	mockclient := mocks.NewMockCloudStorageClient(ctl)
	gcsclient := newMockGCS(mockclient)
	iamclient := &fake.FakeGiamClient{
		Setiamfunc: func(resource string, setiampolicyrequest *iam.SetIamPolicyRequest) (*iam.Policy, error) {
			return &iam.Policy{}, nil
		},
	}
	iamcli := newMockIAM(iamclient, *gcsclient)
	g.Expect(iamcli.SetServiceAccountIAM(context.Background(), "test01", &iam.Policy{})).Error().Should(Succeed())
	//error
	iamclient.Setiamfunc = func(resource string, setiampolicyrequest *iam.SetIamPolicyRequest) (*iam.Policy, error) {
		return nil, errors.New("error")
	}
	g.Expect(iamcli.SetServiceAccountIAM(context.Background(), "test01", &iam.Policy{})).Error().ShouldNot(Succeed())

}

func TestBucketPolicy(t *testing.T) {
	g := NewGomegaWithT(t)
	ctl := gomock.NewController(t)
	defer ctl.Finish()
	mockclient := mocks.NewMockCloudStorageClient(ctl)
	gcsclient := newMockGCS(mockclient)
	handleclient := mocks.NewMockBucketHandle(ctl)
	iamhandleclient := mocks.NewMockIamHandle3(ctl)
	iamclient := &fake.FakeGiamClient{}
	iamcli := newMockIAM(iamclient, *gcsclient)
	//get
	mockclient.EXPECT().Bucket(gomock.Any()).Return(handleclient).AnyTimes()
	handleclient.EXPECT().IAMV3().Return(iamhandleclient).AnyTimes()
	iamhandleclient.EXPECT().Policy(gomock.Any()).Return(&cloudiam.Policy3{}, nil).AnyTimes()
	g.Expect(iamcli.GetBucketPolicy(context.TODO(), "test-iam")).Error().Should(Succeed())
	//set
	iamhandleclient.EXPECT().SetPolicy(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
	g.Expect(iamcli.SetBucketPolicy(context.TODO(), "test-iam", &cloudiam.Policy3{})).Error().Should(Succeed())
	//delete
	g.Expect(iamcli.DeleteBucketPolicy(context.TODO(), "test-iam", "p1")).Should(HaveOccurred())

}
