package storage

import (
	"context"
	"fmt"
	"io"
	"strings"

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

type MinioStorageHandler struct {
	*minio.Client

	BucketName string
}

func NewMinioStorageHandler(cfg *viper.Viper) (*MinioStorageHandler, error) {
	minioClient, err := minio.New(cfg.GetString("storage.files.minio.endpoint"), &minio.Options{
		Creds:  credentials.NewStaticV4(cfg.GetString("storage.files.minio.access_key"), cfg.GetString("storage.files.minio.secret_key"), ""),
		Secure: cfg.GetBool("storage.files.minio.use_ssl"),
	})
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	return &MinioStorageHandler{
		Client:     minioClient,
		BucketName: cfg.GetString("storage.files.minio.bucket_name"),
	}, nil
}

func (s *MinioStorageHandler) Put(reader io.Reader, prefix string, filename string, size int64, metadata map[string]string) error {
	s.Client.PutObject(context.TODO(), s.BucketName, prefix+"/"+filename, reader, size, minio.PutObjectOptions{

		UserMetadata: metadata,
	})
	return nil
}

func (s *MinioStorageHandler) List(prefix string) []FileInfo {
	res := []FileInfo{}
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	objectCh := s.Client.ListObjects(ctx, s.BucketName, minio.ListObjectsOptions{
		Prefix:    prefix,
		Recursive: true,
	})

	for object := range objectCh {
		if object.Err != nil {
			fmt.Println("Error : ", object.Err)
			continue
		}

		objinfo, err := s.Client.StatObject(context.Background(), s.BucketName, object.Key, minio.StatObjectOptions{})
		if err != nil {
			fmt.Println(err)
			continue
		}

		path := strings.Split(object.Key, "/")

		fileinfo := FileInfo{
			Key:          object.Key,
			FileName:     path[len(path)-1],
			LastModified: object.LastModified,
			ContentType:  object.ContentType,
			Metadata:     objinfo.UserMetadata,
		}

		res = append(res, fileinfo)
	}

	return res
}

func (s *MinioStorageHandler) Get(prefix string, file string) (io.Reader, *FileInfo, error) {
	object, err := s.Client.GetObject(context.Background(), s.BucketName, prefix+"/"+file, minio.GetObjectOptions{})
	if err != nil {
		fmt.Println(err)
		return nil, nil, err
	}
	objinfo, err := s.Client.StatObject(context.Background(), s.BucketName, prefix+"/"+file, minio.StatObjectOptions{})
	if err != nil {
		fmt.Println(err)
		return nil, nil, err
	}

	path := strings.Split(objinfo.Key, "/")
	fileinfo := &FileInfo{
		Key:          objinfo.Key,
		FileName:     path[len(path)-1],
		LastModified: objinfo.LastModified,
		ContentType:  objinfo.ContentType,
		Metadata:     objinfo.UserMetadata,
	}

	return object, fileinfo, nil
}

func (s *MinioStorageHandler) Copy(src string, dst string) error {
	srcOpts := minio.CopySrcOptions{
		Bucket: s.BucketName,
		Object: src,
	}

	// Destination object
	dstOpts := minio.CopyDestOptions{
		Bucket: s.BucketName,
		Object: dst,
	}

	_, err := s.Client.CopyObject(context.Background(), dstOpts, srcOpts)
	if err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}
