package azure

import (
	"context"
	"fmt"
	"os"
	"path"

	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
	"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
	"github.com/tidbcloud/metering/pkg/common/utils"
)

type AzureProviderBlob struct {
	cli           *azblob.Client
	account       string
	containerName string
}

func NewAzureProviderBlob(cred *azidentity.DefaultAzureCredential, account string, containerName string) (*AzureProviderBlob, error) {
	client, err := azblob.NewClient(account, cred, nil)
	if err != nil {
		return nil, err
	}
	return &AzureProviderBlob{
		cli:           client,
		account:       account,
		containerName: containerName,
	}, nil
}

// ListObjectInTime lists  blobs which perfix has time range like 20240801-20240831 in the container
// the parameter time is the time point  like 20240801. we will build a range like 20240801-20240831
func (a *AzureProviderBlob) ListObjectInTime(ctx context.Context, now int64, perfix string) ([]string, error) {
	dateRange := utils.GenerateDateRangeAzure(now)
	blobperfix := path.Join(perfix, dateRange)
	pager := a.cli.NewListBlobsFlatPager(a.containerName, &azblob.ListBlobsFlatOptions{
		Prefix: &blobperfix,
	})
	var blobs []string
	for pager.More() {
		page, err := pager.NextPage(ctx)
		if err != nil {
			return nil, err
		}
		for _, blob := range page.Segment.BlobItems {
			blobs = append(blobs, *blob.Name)
		}
	}
	return blobs, nil
}

// ListObjectByName lists  blobs which giving name  in the container
func (a *AzureProviderBlob) ListObjectByName(ctx context.Context, blobperfix string) ([]string, error) {
	pager := a.cli.NewListBlobsFlatPager(a.containerName, &azblob.ListBlobsFlatOptions{
		Prefix: &blobperfix,
	})
	var blobs []string
	for pager.More() {
		page, err := pager.NextPage(ctx)
		if err != nil {
			return nil, err
		}
		for _, blob := range page.Segment.BlobItems {
			blobs = append(blobs, *blob.Name)
		}
	}
	return blobs, nil
}

// DownloadObjects downloads blobs to destinationDir
func (a *AzureProviderBlob) DownloadObjects(ctx context.Context, blobs []string, destinationDir string) error {
	for _, blobName := range blobs {
		filePath := path.Join(destinationDir, path.Base(blobName))
		file, err := os.Create(filePath)
		if err != nil {
			return fmt.Errorf("failed to create file %s: %w", filePath, err)
		}
		defer file.Close()
		_, err = a.cli.DownloadFile(ctx, a.containerName, blobName, file, nil)
		if err != nil {
			return fmt.Errorf("failed to download blob %s: %w", blobName, err)
		}
	}

	return nil
}

// UploadObject uploads blob to container
func (a *AzureProviderBlob) UploadObject(ctx context.Context, blobName string, sourcefile string) error {

	file, err := os.Open(sourcefile)
	if err != nil {
		return fmt.Errorf("failed to open file %s: %w", sourcefile, err)
	}
	defer file.Close()
	_, err = a.cli.UploadFile(ctx, a.containerName, blobName, file, nil)
	if err != nil {
		return fmt.Errorf("failed to upload blob %s: %w", blobName, err)
	}

	return nil
}
