package infrastructure

import (
	"context"
	"encoding/json"
	"oc-datacenter/conf"

	oclib "cloud.o-forge.io/core/oc-lib"
	"github.com/minio/madmin-go/v4"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"

	"github.com/necmettindev/randomstring"
)
	
type MinioService struct{
	Url			string
	RootKey		string
	RootSecret	string
	MinioAdminClient *madmin.AdminClient
}

type StatementEntry struct {
    Effect    string   `json:"Effect"`
    Action    []string `json:"Action"`
    Resource  string `json:"Resource"`
}

type PolicyDocument struct {
    Version   	string           `json:"Version"`
    Statement 	[]StatementEntry `json:"Statement"`
}


func NewMinioService(url string) *MinioService {
	return &MinioService{	
						Url: url, 
						RootKey: conf.GetConfig().MinioRootKey,
						RootSecret: conf.GetConfig().MinioRootSecret,
					}
}

func (m *MinioService) CreateClient() error {
	cred := credentials.NewStaticV4(m.RootKey,m.RootSecret,"")
	cli, err := madmin.NewWithOptions(m.Url, &madmin.Options{Creds: cred, Secure: false}) // Maybe in the future we should use the secure option ?
	if err != nil {
		return err
	}

	m.MinioAdminClient = cli
	return nil
}

func (m *MinioService) CreateCredentials(executionId string) (string,string,error){

	policy := PolicyDocument{
		Version: "2012-10-17",
		Statement: []StatementEntry{
			{
				Effect:   "Allow",
				Action:   []string{"s3:GetObject", "s3:PutObject"},
				Resource: "arn:aws:s3:::"+executionId+"/*",
			},
		},
	}

	p, err := json.Marshal(policy)
	if err != nil {
		return "","",err
	}

	randAccess, randSecret := getRandomCreds()

	req := madmin.AddServiceAccountReq{
		Policy: p,
		TargetUser: m.RootKey,
		AccessKey: randAccess,
		SecretKey: randSecret,
	}
	
	res, err := m.MinioAdminClient.AddServiceAccount(context.Background(), req)
	if err != nil {
		return "", "", err
	}

	
	return res.AccessKey, res.SecretKey, nil

}

func getRandomCreds() (string, string){
	opts := randomstring.GenerationOptions{
		Length: 20,
	}

	a, _ := randomstring.GenerateString(opts)

	opts.Length = 40
	s, _ := randomstring.GenerateString(opts)

	return a,s

}

func (m *MinioService) CreateBucket(executionId string) error {

	l := oclib.GetLogger()
	cred := credentials.NewStaticV4(m.RootKey,m.RootSecret,"")
	client, err := minio.New(m.Url, &minio.Options{
		Creds: cred,
		Secure: false,
	})
	if err != nil {
		l.Error().Msg("Error when creating the minio client for the data plane")
		return err
	}

	err = client.MakeBucket(context.Background(), executionId, minio.MakeBucketOptions{ForceCreate: false})
	if err != nil && err.(minio.ErrorResponse).Code != "BucketAlreadyOwnedByYou" {
		l.Error().Msg("Error when creating the bucket for namespace " + executionId)
		return err
	}

	l.Info().Msg("Created the bucket " + executionId + " on " + m.Url + " minio")
	return nil
}