package asset

import (
	"context"
	"fmt"
	"path/filepath"

	"github.com/tidbcloud/devops/pkg/infra/clients/awscli"
	"github.com/tidbcloud/devops/pkg/infra/config"

	"go.uber.org/dig"
	"gocloud.dev/blob"
)

type AssetParam struct {
	dig.In

	Conf *config.RootConfig
	AWS  awscli.AWSClient
}

type Asset interface {
	// Sub returns a new Asset with the given paths as a subdirectory of the current asset.
	Sub(paths ...string) Asset
	// Bucket returns a blob.Bucket that represents the asset's storage location.
	// Call `blob.Bucket.Close()` when done to release resources.
	//
	// NOTE: Why not use `blob.PrefixedBucket` directly?
	// Because `blob.PrefixedBucket` will close the original bucket and it cannot be propagated,
	// so we need to create a new `blob.Bucket` every time we call `Bucket()`.
	Bucket(ctx context.Context) (*blob.Bucket, error)
}

type core interface {
	OpenBucket(ctx context.Context) (*blob.Bucket, error)
}

type asset struct {
	prefix string

	core core
}

func NewAsset(p AssetParam) (Asset, error) {
	a := &asset{
		prefix: "",
		core: &s3Core{
			conf: p.Conf.Asset,
			aws:  p.AWS,
		},
	}

	return a, nil
}

func (a *asset) Sub(paths ...string) Asset {
	return &asset{
		prefix: filepath.Join(a.prefix, filepath.Join(paths...)),
		core:   a.core,
	}
}

func (a *asset) Bucket(ctx context.Context) (*blob.Bucket, error) {
	root, err := a.core.OpenBucket(ctx)
	if err != nil {
		return nil, fmt.Errorf("open root bucket failed: %w", err)
	}

	if a.prefix == "" {
		return root, nil
	}

	return blob.PrefixedBucket(root, a.prefix+"/"), nil
}
