package client

import (
	"context"
	"fmt"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources"
)

// TerminateResourceGroupResources deletes all resources from the resource group.
func (c *Client) TerminateResourceGroupResources(ctx context.Context) error {
	const timeOut = 10 * time.Minute
	ctx, cancel := context.WithTimeout(ctx, timeOut)
	defer cancel()

	pollers := make(chan *runtime.Poller[armresources.ClientDeleteByIDResponse], 20)
	delete := make(chan struct{}, 1)
	wg := &sync.WaitGroup{}
	wg.Add(2)

	go func() { // This routine lists resources and starts their deletion, where possible.
		defer wg.Done()
		defer func() {
			close(pollers)
			for range delete { // drain channel
			}
		}()

		for {
			ids, err := c.getResourceIDList(ctx)
			if err != nil {
				time.Sleep(3 * time.Second)
				continue
			}

			if len(ids) == 0 {
				return
			}

			for _, id := range ids {
				poller, err := c.deleteResourceByID(ctx, id)
				if err != nil {
					continue
				}
				pollers <- poller
			}

			select {
			case <-ctx.Done():
				return
			case _, ok := <-delete:
				if !ok { // channel was closed
					return
				}
			}
		}
	}()

	go func() { // This routine polls for for the deletions to complete.
		defer wg.Done()
		defer close(delete)

		for poller := range pollers {
			_, err := poller.PollUntilDone(ctx, nil)
			if err != nil {
				continue
			}
			select {
			case delete <- struct{}{}:
			default:
			}
		}
	}()

	wg.Wait()

	return nil
}

func (c *Client) getResourceIDList(ctx context.Context) ([]string, error) {
	var ids []string
	pager := c.resourceAPI.NewListByResourceGroupPager(c.resourceGroup, nil)
	for pager.More() {
		page, err := pager.NextPage(ctx)
		if err != nil {
			return nil, fmt.Errorf("getting next page of ListByResourceGroup: %w", err)
		}
		for _, resource := range page.Value {
			if resource.ID == nil {
				return nil, fmt.Errorf("resource %v has no ID", resource)
			}
			ids = append(ids, *resource.ID)
		}
	}
	return ids, nil
}

func (c *Client) deleteResourceByID(ctx context.Context, id string,
) (*runtime.Poller[armresources.ClientDeleteByIDResponse], error) {
	apiVersion := "2020-02-02"

	// First try, API version unknown, will fail.
	poller, err := c.resourceAPI.BeginDeleteByID(ctx, id, apiVersion, nil)
	if isVersionWrongErr(err) {
		// bad hack, but easiest way to get the right API version
		apiVersion = parseAPIVersionFromErr(err)
		poller, err = c.resourceAPI.BeginDeleteByID(ctx, id, apiVersion, nil)
	}
	return poller, err
}

func isVersionWrongErr(err error) bool {
	if err == nil {
		return false
	}
	return strings.Contains(err.Error(), "NoRegisteredProviderFound") &&
		strings.Contains(err.Error(), "The supported api-versions are")
}

var apiVersionRegex = regexp.MustCompile(` (\d\d\d\d-\d\d-\d\d)'`)

func parseAPIVersionFromErr(err error) string {
	if err == nil {
		return ""
	}
	matches := apiVersionRegex.FindStringSubmatch(err.Error())
	return matches[1]
}