package capabilities

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"regexp"

	"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2018-07-01/network"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
)

var regex = regexp.MustCompile("/resourceGroups/(.+?)/")

func NewAKSVirtualNetworksHandler() *AKSVirtualNetworksHandler {
	return &AKSVirtualNetworksHandler{}
}

type AKSVirtualNetworksHandler struct {
}

type virtualNetworksRequestBody struct {
	// BaseURL specifies the Azure Resource management endpoint, it defaults "https://management.azure.com/".
	BaseURL string `json:"baseUrl"`
	// AuthBaseURL specifies the Azure OAuth 2.0 authentication endpoint, it defaults "https://login.microsoftonline.com/".
	AuthBaseURL string `json:"authBaseUrl"`
	// credentials
	ClientID       string `json:"clientId"`
	ClientSecret   string `json:"clientSecret"`
	SubscriptionID string `json:"subscriptionId"`
	TenantID       string `json:"tenantId"`
}

type virtualNetworksResponseBody struct {
	Name          string   `json:"name"`
	ResourceGroup string   `json:"resourceGroup"`
	Subnets       []subnet `json:"subnets"`
}

type subnet struct {
	Name         string `json:"name"`
	AddressRange string `json:"addressRange"`
}

func (g *AKSVirtualNetworksHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	if req.Method != http.MethodPost {
		writer.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	writer.Header().Set("Content-Type", "application/json")

	var body virtualNetworksRequestBody
	if err := extractRequestBody(writer, req, &body); err != nil {
		handleErr(writer, err)
		return
	}

	if err := validateVirtualNetworksRequestBody(&body); err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		handleErr(writer, err)
		return
	}

	clientID := body.ClientID
	clientSecret := body.ClientSecret
	subscriptionID := body.SubscriptionID
	tenantID := body.TenantID

	baseURL := body.BaseURL
	authBaseURL := body.AuthBaseURL
	if baseURL == "" {
		baseURL = azure.PublicCloud.ResourceManagerEndpoint
	}
	if authBaseURL == "" {
		authBaseURL = azure.PublicCloud.ActiveDirectoryEndpoint
	}

	oAuthConfig, err := adal.NewOAuthConfig(authBaseURL, tenantID)
	if err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		handleErr(writer, fmt.Errorf("failed to configure azure oauth: %v", err))
		return
	}

	spToken, err := adal.NewServicePrincipalToken(*oAuthConfig, clientID, clientSecret, baseURL)
	if err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		handleErr(writer, fmt.Errorf("failed to create token: %v", err))
		return
	}

	authorizer := autorest.NewBearerAuthorizer(spToken)

	client := network.NewVirtualNetworksClientWithBaseURI(baseURL, subscriptionID)
	client.Authorizer = authorizer

	var networks []virtualNetworksResponseBody

	pointer, err := client.ListAll(context.Background())
	if err != nil {
		writer.WriteHeader(http.StatusBadRequest)
		handleErr(writer, fmt.Errorf("failed to get networks: %v", err))
		return
	}

	for pointer.NotDone() {
		var batch []virtualNetworksResponseBody

		for _, azureNetwork := range pointer.Values() {
			var subnets []subnet

			if azureNetwork.Subnets != nil {
				for _, azureSubnet := range *azureNetwork.Subnets {
					if azureSubnet.Name != nil {
						subnets = append(subnets, subnet{
							Name:         *azureSubnet.Name,
							AddressRange: *azureSubnet.AddressPrefix,
						})
					}
				}
			}

			if azureNetwork.ID == nil {
				writer.WriteHeader(http.StatusInternalServerError)
				handleErr(writer, errors.New("no ID on virtual network"))
				return
			}

			match := regex.FindStringSubmatch(*azureNetwork.ID)

			if len(match) < 2 || match[1] == "" {
				writer.WriteHeader(http.StatusInternalServerError)
				handleErr(writer, errors.New("could not parse virtual network ID"))
				return
			}

			if azureNetwork.Name == nil {
				writer.WriteHeader(http.StatusInternalServerError)
				handleErr(writer, errors.New("no name on virtual network"))
				return
			}

			batch = append(batch, virtualNetworksResponseBody{
				Name:          *azureNetwork.Name,
				ResourceGroup: match[1],
				Subnets:       subnets,
			})
		}

		networks = append(networks, batch...)

		err = pointer.Next()
		if err != nil {
			writer.WriteHeader(http.StatusInternalServerError)
			handleErr(writer, err)
			return
		}
	}

	serialized, err := json.Marshal(networks)
	if err != nil {
		writer.WriteHeader(http.StatusInternalServerError)
		handleErr(writer, err)
		return
	}

	writer.Write(serialized)
}

func validateVirtualNetworksRequestBody(body *virtualNetworksRequestBody) error {
	if body.ClientID == "" {
		return fmt.Errorf("invalid clientID")
	}

	if body.ClientSecret == "" {
		return fmt.Errorf("invalid clientSecret")
	}

	if body.SubscriptionID == "" {
		return fmt.Errorf("invalid subscriptionID")
	}

	if body.TenantID == "" {
		return fmt.Errorf("invalid tenantID")
	}

	return nil
}
