package storage

import (
	"context"
	"fmt"
	"net/url"
	"time"

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

var _ ObjectStorage = (*storageMinio)(nil)

type storageMinio struct {
	client *minio.Client

	endpoint     string
	accessKey    string
	accessSecret string
	secure       bool

	bucket string
}

func newMinioStorage(endpoint, accessKey, accessSecret string, secure bool, bucket string) (ObjectStorage, error) {
	client, err := minio.New(endpoint, &minio.Options{
		Creds:  credentials.NewStaticV4(accessKey, accessSecret, ""),
		Secure: secure,
	})
	if err != nil {
		return nil, err
	}
	return &storageMinio{
		client:       client,
		endpoint:     endpoint,
		accessKey:    accessKey,
		accessSecret: accessSecret,
		secure:       secure,
		bucket:       bucket,
	}, nil
}

// PresignedPutObject implements ObjectStorage
func (m *storageMinio) PresignedPutObject(ctx context.Context, objectName string, expires time.Duration) (u *url.URL, err error) {
	return m.client.PresignedPutObject(ctx, m.bucket, objectName, expires)
}

// PresignedGetObject implements ObjectStorage
func (m *storageMinio) PresignedGetObject(ctx context.Context, objectName string, expires time.Duration) (u *url.URL, err error) {
	return m.client.PresignedGetObject(ctx, m.bucket, objectName, expires, nil)
}

// MoveObject implements ObjectStorage
func (m *storageMinio) MoveObject(ctx context.Context, srcObject, dstObject string) (err error) {
	dst := minio.CopyDestOptions{Bucket: m.bucket, Object: dstObject}
	src := minio.CopySrcOptions{Bucket: m.bucket, Object: srcObject}
	if _, err := m.client.CopyObject(ctx, dst, src); err != nil {
		return fmt.Errorf("move object failed while copy: %v", err)
	}
	if err := m.client.RemoveObject(ctx, m.bucket, srcObject, minio.RemoveObjectOptions{}); err != nil {
		return fmt.Errorf("move object failed while remove: %v", err)
	}
	return nil
}

// ObjectExists implements ObjectStorage
func (m *storageMinio) ObjectExists(ctx context.Context, objectName string) (bool, error) {
	_, err := m.client.StatObject(ctx, m.bucket, objectName, minio.StatObjectOptions{})
	if err != nil {
		er := minio.ToErrorResponse(err)
		switch er.Code {
		case "NoSuchKey", "NoSuchBucket":
			return false, nil
		default:
			return false, err
		}
	}
	return true, nil
}