package utils

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/url"
	"strings"

	"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/resources/armsubscriptions"
	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
	"github.com/juju/errors"
)

func WriteAzureBlobFile(blobUrl string, content []byte) error {
	blobClient, err := getBlobClient(blobUrl)
	if err != nil {
		return err
	}

	// 上传文件到 Blob
	_, err = blobClient.UploadStream(context.Background(), bytes.NewReader(content), nil)
	if err != nil {
		return errors.Annotatef(err, "failed to upload blob content: %s", blobUrl)
	}
	return nil
}

func ReadAzureBlobFile(blobUrl string) ([]byte, error) {
	blobClient, err := getBlobClient(blobUrl)
	if err != nil {
		return nil, err
	}

	// 下载 Blob 内容
	response, err := blobClient.DownloadStream(context.Background(), nil)
	if err != nil {
		if bloberror.HasCode(err, bloberror.BlobNotFound) {
			return nil, errors.NotFoundf(blobUrl)
		}
		return nil, errors.Annotatef(err, "failed to read blob content: %s", blobUrl)
	}
	defer response.Body.Close()
	bytes, err := io.ReadAll(response.Body)
	if err != nil {
		return nil, errors.Annotatef(err, "failed to read blob content: %s", blobUrl)
	}
	return bytes, nil
}

type blobURLComponents struct {
	AccountName   string
	ContainerName string
	BlobName      string
	SASToken      string
}

func parseBlobURL(blobURL string) (*blobURLComponents, error) {
	// 解析 URL
	parsedURL, err := url.Parse(blobURL)
	if err != nil {
		return nil, fmt.Errorf("failed to parse blob URL: %v", err)
	}

	// 提取存储账户名称
	hostParts := strings.Split(parsedURL.Hostname(), ".")
	if len(hostParts) < 3 {
		return nil, fmt.Errorf("invalid blob URL: %s", blobURL)
	}
	accountName := hostParts[0] // <account_name>.blob.core.windows.net

	// 提取容器名称和 Blob 名称
	pathParts := strings.SplitN(strings.TrimPrefix(parsedURL.Path, "/"), "/", 2)
	if len(pathParts) < 2 {
		return nil, fmt.Errorf("invalid blob URL path: %s", parsedURL.Path)
	}
	containerName := pathParts[0]
	blobName := pathParts[1]

	// 提取 SAS Token（如果存在）
	sasToken := parsedURL.RawQuery

	// 返回解析结果
	return &blobURLComponents{
		AccountName:   accountName,
		ContainerName: containerName,
		BlobName:      blobName,
		SASToken:      sasToken,
	}, nil
}

func getBlobClient(blobUrl string) (*blockblob.Client, error) {
	urlParts, err := parseBlobURL(blobUrl)
	if err != nil {
		return nil, err
	}

	// 使用 Azure CLI 凭据
	cred, err := azidentity.NewAzureCLICredential(nil)
	if err != nil {
		return nil, err
	}

	accountName := urlParts.AccountName
	accountKey, err := getStorageAccountKey(cred, accountName)
	if err != nil {
		return nil, err
	}

	credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
	if err != nil {
		return nil, errors.Annotatef(err, "failed to create storage credential")
	}

	// 创建 Blob 服务客户端
	serviceURL := fmt.Sprintf("https://%s.blob.core.windows.net/", accountName)
	client, err := azblob.NewClientWithSharedKeyCredential(serviceURL, credential, nil)
	if err != nil {
		return nil, errors.Annotatef(err, "failed to create storage accounts client")
	}

	containerClient := client.ServiceClient().NewContainerClient(urlParts.ContainerName)
	return containerClient.NewBlockBlobClient(urlParts.BlobName), err
}

func getStorageAccountKey(cred *azidentity.AzureCLICredential, accountName string) (string, error) {
	subscriptions, err := getSubscriptions(cred)
	if err != nil {
		return "", err
	}

	for _, sub := range subscriptions {
		resourceGroupName, err := findStorageAccountInResourceGroup(cred, sub, accountName)
		if err != nil {
			return "", err
		}
		if resourceGroupName == "" {
			continue
		}
		return getStorageAccountAccountKey(cred, sub, resourceGroupName, accountName)

	}
	return "", errors.NotValidf("storage account '%s'", accountName)
}

func findStorageAccountInResourceGroup(cred *azidentity.AzureCLICredential, subscriptionID, storageAccountName string) (string, error) {
	// 创建资源组客户端
	resourceGroupsClient, err := armresources.NewResourceGroupsClient(subscriptionID, cred, nil)
	if err != nil {
		return "", errors.Annotatef(err, "failed to create resource groups client")
	}

	// 列出所有资源组
	ctx := context.Background()
	resourceGroupsPager := resourceGroupsClient.NewListPager(nil)
	for resourceGroupsPager.More() {
		page, err := resourceGroupsPager.NextPage(ctx)
		if err != nil {
			return "", errors.Annotatef(err, "failed to list resource groups")
		}

		// 遍历资源组查找存储账户
		for _, rg := range page.ResourceGroupListResult.Value {
			ok, err := isStorageAccountInResourceGroup(cred, subscriptionID, *rg.Name, storageAccountName)
			if err == nil && ok {
				return *rg.Name, nil
			}
		}
	}
	return "", nil
}

// isStorageAccountInResourceGroup 查找存储账户是否在指定的资源组中
func isStorageAccountInResourceGroup(cred *azidentity.AzureCLICredential, subscriptionID, resourceGroupName, storageAccountName string) (bool, error) {
	client, err := armstorage.NewAccountsClient(subscriptionID, cred, nil)
	if err != nil {
		return false, err
	}

	pager := client.NewListByResourceGroupPager(resourceGroupName, nil)
	for pager.More() {
		page, err := pager.NextPage(context.Background())
		if err != nil {
			return false, errors.Annotatef(err, "failed to list storage accounts in resource group '%s'", storageAccountName)
		}

		for _, account := range page.Value {
			if strings.EqualFold(*account.Name, storageAccountName) {
				return true, nil
			}
		}
	}

	return false, nil
}

func getStorageAccountAccountKey(cred *azidentity.AzureCLICredential, subscriptionID, resourceGroupName, storageAccountName string) (string, error) {
	client, err := armstorage.NewAccountsClient(subscriptionID, cred, nil)
	if err != nil {
		return "", err
	}

	resp, err := client.ListKeys(context.Background(), resourceGroupName, storageAccountName, nil)
	if err != nil {
		return "", err
	}

	if len(resp.Keys) == 0 {
		return "", errors.NotFoundf("storage account keys")
	}
	return *resp.Keys[0].Value, nil
}

func getSubscriptions(cred *azidentity.AzureCLICredential) ([]string, error) {
	subscriptionsClient, err := armsubscriptions.NewClient(cred, nil)
	if err != nil {
		return nil, errors.Annotatef(err, "failed to create subscriptions client")
	}

	ctx := context.Background()
	subscriptionsPager := subscriptionsClient.NewListPager(nil)

	var subscriptions []string
	for subscriptionsPager.More() {
		page, err := subscriptionsPager.NextPage(ctx)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to get next subscriptions page")
		}

		for _, subscription := range page.SubscriptionListResult.Value {
			subscriptions = append(subscriptions, *subscription.SubscriptionID)
		}
	}
	return subscriptions, nil
}
