// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

package main

import (
	"context"
	"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
	"log"
	"os"
)

var (
	subscriptionID     string
	location           = "eastus"
	resourceGroupName  = "sample-resource-group"
	serverName         = "sample2server"
	storageAccountName = "sample2storage2account"
	containerName      = "blob2container"
)

var (
	resourcesClientFactory *armresources.ClientFactory
	storageClientFactory   *armstorage.ClientFactory
	sqlClientFactory       *armsql.ClientFactory
)

var (
	resourceGroupClient                  *armresources.ResourceGroupsClient
	accountsClient                       *armstorage.AccountsClient
	blobContainersClient                 *armstorage.BlobContainersClient
	serversClient                        *armsql.ServersClient
	serverSecurityAlertPoliciesClient    *armsql.ServerSecurityAlertPoliciesClient
	serverVulnerabilityAssessmentsClient *armsql.ServerVulnerabilityAssessmentsClient
)

func main() {
	subscriptionID = os.Getenv("AZURE_SUBSCRIPTION_ID")
	if len(subscriptionID) == 0 {
		log.Fatal("AZURE_SUBSCRIPTION_ID is not set.")
	}

	cred, err := azidentity.NewDefaultAzureCredential(nil)
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	resourcesClientFactory, err = armresources.NewClientFactory(subscriptionID, cred, nil)
	if err != nil {
		log.Fatal(err)
	}
	resourceGroupClient = resourcesClientFactory.NewResourceGroupsClient()

	storageClientFactory, err = armstorage.NewClientFactory(subscriptionID, cred, nil)
	if err != nil {
		log.Fatal(err)
	}
	accountsClient = storageClientFactory.NewAccountsClient()
	blobContainersClient = storageClientFactory.NewBlobContainersClient()

	sqlClientFactory, err = armsql.NewClientFactory(subscriptionID, cred, nil)
	if err != nil {
		log.Fatal(err)
	}
	serversClient = sqlClientFactory.NewServersClient()
	serverSecurityAlertPoliciesClient = sqlClientFactory.NewServerSecurityAlertPoliciesClient()
	serverVulnerabilityAssessmentsClient = sqlClientFactory.NewServerVulnerabilityAssessmentsClient()

	resourceGroup, err := createResourceGroup(ctx)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("resources group:", *resourceGroup.ID)

	storageAccount, err := createStorageAccount(ctx)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("storage account:", *storageAccount.ID)

	blobContainer, err := createBlobContainers(ctx)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("blob container:", *blobContainer.ID)

	accountKey, err := regenerateStorageAccountKey(ctx)
	if err != nil {
		log.Fatal(err)
	}
	accessKey := accountKey[0].Value
	log.Println("account Key:", *accessKey)

	server, err := createServer(ctx)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("server:", *server.ID)

	serverSecurityAlertPolicy, err := createServerSecurityAlertPolicy(ctx)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("server security alert policy:", *serverSecurityAlertPolicy.ID)

	containerPath := "https://" + storageAccountName + ".blob.core.windows.net/" + containerName + "/"
	serverVulnerabilityAssessment, err := createServerVulnerabilityAssessment(ctx, containerPath, *accessKey)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("server vulnerability assessment:", *serverVulnerabilityAssessment.ID)

	keepResource := os.Getenv("KEEP_RESOURCE")
	if len(keepResource) == 0 {
		err = cleanup(ctx)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("cleaned up successfully.")
	}
}

func createStorageAccount(ctx context.Context) (*armstorage.Account, error) {

	pollerResp, err := accountsClient.BeginCreate(
		ctx,
		resourceGroupName,
		storageAccountName,
		armstorage.AccountCreateParameters{
			Kind: to.Ptr(armstorage.KindStorageV2),
			SKU: &armstorage.SKU{
				Name: to.Ptr(armstorage.SKUNameStandardLRS),
			},
			Location: to.Ptr(location),
			Properties: &armstorage.AccountPropertiesCreateParameters{
				AccessTier: to.Ptr(armstorage.AccessTierCool),
				Encryption: &armstorage.Encryption{
					Services: &armstorage.EncryptionServices{
						File: &armstorage.EncryptionService{
							KeyType: to.Ptr(armstorage.KeyTypeAccount),
							Enabled: to.Ptr(true),
						},
						Blob: &armstorage.EncryptionService{
							KeyType: to.Ptr(armstorage.KeyTypeAccount),
							Enabled: to.Ptr(true),
						},
					},
					KeySource: to.Ptr(armstorage.KeySourceMicrosoftStorage),
				},
			},
		}, nil)
	if err != nil {
		return nil, err
	}
	resp, err := pollerResp.PollUntilDone(ctx, nil)
	if err != nil {
		return nil, err
	}
	return &resp.Account, nil
}

func createBlobContainers(ctx context.Context) (*armstorage.BlobContainer, error) {

	resp, err := blobContainersClient.Create(
		ctx,
		resourceGroupName,
		storageAccountName,
		containerName,
		armstorage.BlobContainer{
			ContainerProperties: &armstorage.ContainerProperties{},
		},
		nil,
	)
	if err != nil {
		return nil, err
	}

	return &resp.BlobContainer, nil
}

func regenerateStorageAccountKey(ctx context.Context) ([]*armstorage.AccountKey, error) {

	resp, err := accountsClient.RegenerateKey(
		ctx,
		resourceGroupName,
		storageAccountName,
		armstorage.AccountRegenerateKeyParameters{
			KeyName: to.Ptr("key2"),
		},
		nil,
	)
	if err != nil {
		return nil, err
	}
	return resp.Keys, nil
}

func createServer(ctx context.Context) (*armsql.Server, error) {

	pollerResp, err := serversClient.BeginCreateOrUpdate(
		ctx,
		resourceGroupName,
		serverName,
		armsql.Server{
			Location: to.Ptr(location),
			Properties: &armsql.ServerProperties{
				AdministratorLogin:         to.Ptr("dummylogin"),
				AdministratorLoginPassword: to.Ptr("QWE123!@#"),
			},
		},
		nil,
	)
	if err != nil {
		return nil, err
	}
	resp, err := pollerResp.PollUntilDone(ctx, nil)
	if err != nil {
		return nil, err
	}
	return &resp.Server, nil
}

func createServerSecurityAlertPolicy(ctx context.Context) (*armsql.ServerSecurityAlertPolicy, error) {

	pollerResp, err := serverSecurityAlertPoliciesClient.BeginCreateOrUpdate(
		ctx,
		resourceGroupName,
		serverName,
		armsql.SecurityAlertPolicyNameDefault,
		armsql.ServerSecurityAlertPolicy{
			Properties: &armsql.SecurityAlertsPolicyProperties{
				State:              to.Ptr(armsql.SecurityAlertsPolicyStateEnabled),
				DisabledAlerts:     []*string{},
				EmailAccountAdmins: to.Ptr(true),
				EmailAddresses:     []*string{},
			},
		},
		nil,
	)
	if err != nil {
		return nil, err
	}
	resp, err := pollerResp.PollUntilDone(ctx, nil)
	if err != nil {
		return nil, err
	}
	return &resp.ServerSecurityAlertPolicy, nil
}

func createServerVulnerabilityAssessment(ctx context.Context, containerPath, accessKey string) (*armsql.ServerVulnerabilityAssessment, error) {

	resp, err := serverVulnerabilityAssessmentsClient.CreateOrUpdate(
		ctx,
		resourceGroupName,
		serverName,
		armsql.VulnerabilityAssessmentNameDefault,
		armsql.ServerVulnerabilityAssessment{
			Properties: &armsql.ServerVulnerabilityAssessmentProperties{
				StorageContainerPath:    to.Ptr(containerPath),
				StorageAccountAccessKey: to.Ptr(accessKey),
			},
		},
		nil,
	)
	if err != nil {
		return nil, err
	}
	return &resp.ServerVulnerabilityAssessment, nil
}

func createResourceGroup(ctx context.Context) (*armresources.ResourceGroup, error) {

	resourceGroupResp, err := resourceGroupClient.CreateOrUpdate(
		ctx,
		resourceGroupName,
		armresources.ResourceGroup{
			Location: to.Ptr(location),
		},
		nil)
	if err != nil {
		return nil, err
	}
	return &resourceGroupResp.ResourceGroup, nil
}

func cleanup(ctx context.Context) error {

	pollerResp, err := resourceGroupClient.BeginDelete(ctx, resourceGroupName, nil)
	if err != nil {
		return err
	}

	_, err = pollerResp.PollUntilDone(ctx, nil)
	if err != nil {
		return err
	}
	return nil
}
