package gc

import (
	"context"
	"encoding/base64"
	"fmt"
	"io"
	"net/url"

	minio "github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"github.com/minio/minio-go/v7/pkg/lifecycle"

	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/resource"
	"github.com/pingcap/test-infra/services/rms/config"
	"github.com/pingcap/test-infra/services/rms/service/provider"
)

const ArchiveBucketName = "test-infra-testground"
const ArchivePathPrefix = "archive"

var (
	_ provider.ArchiverInterface = &DefaultArchiver{}
)

type DefaultArchiver struct {
	Clients func(resourceType apiresource.ResourceType, testbed string) (resource.Client, error)
}

func NewDefaultArchiver(clients func(resourceType apiresource.ResourceType, testbed string) (resource.Client, error)) *DefaultArchiver {
	return &DefaultArchiver{Clients: clients}
}

func archiver(ctx context.Context, s3Client *minio.Client, testbedName, objectName string, reader io.Reader) (*url.URL, error) {
	_, err := s3Client.PutObject(ctx, ArchiveBucketName, fmt.Sprintf("%s/%s/%s", ArchivePathPrefix, testbedName, objectName), reader, -1, minio.PutObjectOptions{PartSize: 10 * 1024 * 1024})
	if err != nil {
		return nil, err
	}
	browsePath := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s/%s/%s", ArchivePathPrefix, testbedName, objectName)))
	return url.Parse(config.DefaultRMSConfiguration.MinIO.ConsoleBrowserURL + browsePath)
}

func (d DefaultArchiver) Archive(ctx context.Context, handle apiresource.ResourceHandle, action apiresource.GcArtifactAction) (map[string]*url.URL, error) {
	client, err := d.Clients(handle.Type, handle.Testbed)
	if err != nil {
		return nil, err
	}
	minioURL, err := url.Parse(config.DefaultRMSConfiguration.MinIO.URL)
	if err != nil {
		panic(err)
	}
	s3Client, err := minio.New(minioURL.Host, &minio.Options{
		Creds:  credentials.NewStaticV4(config.DefaultRMSConfiguration.MinIO.ID, config.DefaultRMSConfiguration.MinIO.Secret, ""),
		Secure: false,
	})
	if err != nil {
		return nil, err
	}

	config := lifecycle.NewConfiguration()
	config.Rules = []lifecycle.Rule{
		{
			Expiration: lifecycle.Expiration{
				Days: 7,
			},
			ID: "expire-testbeds",
			RuleFilter: lifecycle.Filter{
				Prefix: ArchivePathPrefix + "/",
			},
			Status: "Enabled",
		},
	}
	err = s3Client.SetBucketLifecycle(context.Background(), ArchiveBucketName, config)
	if err != nil {
		return nil, err
	}
	return client.Artifact(handle, action, func(objectName string, reader io.Reader) (*url.URL, error) {
		return archiver(ctx, s3Client, handle.Testbed, objectName, reader)
	})
}
