package azure

import (
	"context"
	"errors"
	"net/http"
	"testing"

	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
	azfake "github.com/Azure/azure-sdk-for-go/sdk/azcore/fake"
	"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v6/fake"
	. "github.com/onsi/gomega"
)

func TestNewPLSClient(t *testing.T) {
	g := NewWithT(t)
	_, err := NewPLSClient("fake", "fake", &azfake.TokenCredential{})
	g.Expect(err).To(Succeed())
}

func TestPLSGet(t *testing.T) {
	g := NewWithT(t)

	f := func(name string, server *fake.PrivateLinkServicesServer, expected error) {
		t.Run(name, func(t *testing.T) {
			cli, err := armnetwork.NewPrivateLinkServicesClient("fake", &azfake.TokenCredential{}, &arm.ClientOptions{
				ClientOptions: azcore.ClientOptions{
					Transport: fake.NewPrivateLinkServicesServerTransport(server),
				},
			})
			g.Expect(err).To(Succeed())
			p := &PLSClient{
				cli:           cli,
				resourceGroup: "fake",
			}
			_, err = p.Get(context.TODO(), "fake")
			g.Expect(errors.Is(err, expected)).To(BeTrue())
		})
	}

	f("get private link service failed", &fake.PrivateLinkServicesServer{
		Get: func(ctx context.Context, resourceGroupName, serviceName string, options *armnetwork.PrivateLinkServicesClientGetOptions) (resp azfake.Responder[armnetwork.PrivateLinkServicesClientGetResponse], errResp azfake.ErrorResponder) {
			errResp.SetError(errBoom)
			return
		},
	}, errBoom)

	f("success get private link service", &fake.PrivateLinkServicesServer{
		Get: func(ctx context.Context, resourceGroupName, serviceName string, options *armnetwork.PrivateLinkServicesClientGetOptions) (resp azfake.Responder[armnetwork.PrivateLinkServicesClientGetResponse], errResp azfake.ErrorResponder) {
			resp.SetResponse(http.StatusOK, armnetwork.PrivateLinkServicesClientGetResponse{}, nil)
			return
		},
	}, nil)

}

func TestDeleteUserPermissions(t *testing.T) {
	g := NewWithT(t)
	cases := map[string]struct {
		subs2Remove []string
		expectErr   bool
		getFn       func(ctx context.Context, resourceGroupName, serviceName string, options *armnetwork.PrivateLinkServicesClientGetOptions) (resp azfake.Responder[armnetwork.PrivateLinkServicesClientGetResponse], errResp azfake.ErrorResponder)
		updateFn    func(ctx context.Context, resourceGroupName, serviceName string, parameters armnetwork.PrivateLinkService, options *armnetwork.PrivateLinkServicesClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[armnetwork.PrivateLinkServicesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder)
	}{
		"delete user permissions successfully": {
			subs2Remove: []string{"fake-1"},
			getFn: func(ctx context.Context, resourceGroupName, serviceName string, options *armnetwork.PrivateLinkServicesClientGetOptions) (resp azfake.Responder[armnetwork.PrivateLinkServicesClientGetResponse], errResp azfake.ErrorResponder) {
				plsResp := armnetwork.PrivateLinkServicesClientGetResponse{}
				plsResp.ID = to.Ptr("fake-id")
				plsResp.Name = to.Ptr("fake")
				plsResp.Properties = &armnetwork.PrivateLinkServiceProperties{
					AutoApproval: &armnetwork.PrivateLinkServicePropertiesAutoApproval{
						Subscriptions: []*string{to.Ptr("fake-1"), to.Ptr("fake-2")},
					},
					Visibility: &armnetwork.PrivateLinkServicePropertiesVisibility{
						Subscriptions: []*string{to.Ptr("fake-1"), to.Ptr("fake-2")},
					},
				}
				resp.SetResponse(http.StatusOK, plsResp, nil)
				return
			},
			updateFn: func(ctx context.Context, resourceGroupName, serviceName string, parameters armnetwork.PrivateLinkService, options *armnetwork.PrivateLinkServicesClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[armnetwork.PrivateLinkServicesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) {
				g.Expect(parameters.Properties.AutoApproval.Subscriptions).To(Equal([]*string{to.Ptr("fake-2")}))
				g.Expect(parameters.Properties.Visibility.Subscriptions).To(Equal([]*string{to.Ptr("fake-2")}))

				resp.AddNonTerminalResponse(http.StatusCreated, nil)

				plsResp := armnetwork.PrivateLinkServicesClientCreateOrUpdateResponse{}
				plsResp.PrivateLinkService = parameters
				resp.SetTerminalResponse(http.StatusOK, plsResp, nil)
				return
			},
		},
		"get privatelinkservice failed": {
			subs2Remove: []string{"fake-1"},
			expectErr:   true,
			getFn: func(ctx context.Context, resourceGroupName, serviceName string, options *armnetwork.PrivateLinkServicesClientGetOptions) (resp azfake.Responder[armnetwork.PrivateLinkServicesClientGetResponse], errResp azfake.ErrorResponder) {
				errResp.SetResponseError(http.StatusNotFound, "FakeErrorCode")
				return
			},
		},
	}

	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			// prepare
			fServer := fake.PrivateLinkServicesServer{
				Get:                 tc.getFn,
				BeginCreateOrUpdate: tc.updateFn,
			}

			cli, err := armnetwork.NewPrivateLinkServicesClient("fake", &azfake.TokenCredential{}, &arm.ClientOptions{
				ClientOptions: azcore.ClientOptions{
					Transport: fake.NewPrivateLinkServicesServerTransport(&fServer),
				},
			})
			g.Expect(err).To(BeNil())
			plsClient := &PLSClient{
				cli:           cli,
				resourceGroup: "fake",
			}

			err = plsClient.DeleteUserPermissions(context.Background(), "fake", tc.subs2Remove)
			if tc.expectErr {
				g.Expect(err).NotTo(BeNil())
			} else {
				g.Expect(err).To(BeNil())
			}
		})
	}

}

func TestAddUserPermissions(t *testing.T) {
	g := NewWithT(t)
	cases := map[string]struct {
		subs2Add  []string
		expectErr bool
		getFn     func(ctx context.Context, resourceGroupName, serviceName string, options *armnetwork.PrivateLinkServicesClientGetOptions) (resp azfake.Responder[armnetwork.PrivateLinkServicesClientGetResponse], errResp azfake.ErrorResponder)
		updateFn  func(ctx context.Context, resourceGroupName, serviceName string, parameters armnetwork.PrivateLinkService, options *armnetwork.PrivateLinkServicesClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[armnetwork.PrivateLinkServicesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder)
	}{
		"add user permissions successfully": {
			subs2Add: []string{"fake-1"},
			getFn: func(ctx context.Context, resourceGroupName, serviceName string, options *armnetwork.PrivateLinkServicesClientGetOptions) (resp azfake.Responder[armnetwork.PrivateLinkServicesClientGetResponse], errResp azfake.ErrorResponder) {
				plsResp := armnetwork.PrivateLinkServicesClientGetResponse{}
				plsResp.ID = to.Ptr("fake-id")
				plsResp.Name = to.Ptr("fake")
				plsResp.Properties = &armnetwork.PrivateLinkServiceProperties{
					AutoApproval: &armnetwork.PrivateLinkServicePropertiesAutoApproval{
						Subscriptions: []*string{to.Ptr("fake-2")},
					},
					Visibility: &armnetwork.PrivateLinkServicePropertiesVisibility{
						Subscriptions: []*string{to.Ptr("fake-2")},
					},
				}
				resp.SetResponse(http.StatusOK, plsResp, nil)
				return
			},
			updateFn: func(ctx context.Context, resourceGroupName, serviceName string, parameters armnetwork.PrivateLinkService, options *armnetwork.PrivateLinkServicesClientBeginCreateOrUpdateOptions) (resp azfake.PollerResponder[armnetwork.PrivateLinkServicesClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) {
				g.Expect(parameters.Properties.AutoApproval.Subscriptions).To(Equal([]*string{to.Ptr("fake-2"), to.Ptr("fake-1")}))
				g.Expect(parameters.Properties.Visibility.Subscriptions).To(Equal([]*string{to.Ptr("fake-2"), to.Ptr("fake-1")}))

				resp.AddNonTerminalResponse(http.StatusCreated, nil)

				plsResp := armnetwork.PrivateLinkServicesClientCreateOrUpdateResponse{}
				plsResp.PrivateLinkService = parameters
				resp.SetTerminalResponse(http.StatusOK, plsResp, nil)
				return
			},
		},
		"get privatelinkservice failed": {
			subs2Add:  []string{"fake-1"},
			expectErr: true,
			getFn: func(ctx context.Context, resourceGroupName, serviceName string, options *armnetwork.PrivateLinkServicesClientGetOptions) (resp azfake.Responder[armnetwork.PrivateLinkServicesClientGetResponse], errResp azfake.ErrorResponder) {
				errResp.SetResponseError(http.StatusNotFound, "FakeErrorCode")
				return
			},
		},
	}

	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			// prepare
			fServer := fake.PrivateLinkServicesServer{
				Get:                 tc.getFn,
				BeginCreateOrUpdate: tc.updateFn,
			}

			cli, err := armnetwork.NewPrivateLinkServicesClient("fake", &azfake.TokenCredential{}, &arm.ClientOptions{
				ClientOptions: azcore.ClientOptions{
					Transport: fake.NewPrivateLinkServicesServerTransport(&fServer),
				},
			})
			g.Expect(err).To(BeNil())
			plsClient := &PLSClient{
				cli:           cli,
				resourceGroup: "fake",
			}

			err = plsClient.AddUserPermissions(context.Background(), "fake", tc.subs2Add)
			if tc.expectErr {
				g.Expect(err).NotTo(BeNil())
			} else {
				g.Expect(err).To(BeNil())
			}
		})
	}

}
