package storage

import (
	"bytes"
	"io/ioutil"
	"strings"

	minio "github.com/minio/minio-go"
)

type Minio struct {
	db *minio.Client
}

func NewMinio(addr, user, pass string) (*Minio, error) {
	db, err := minio.New(addr, user, pass, strings.HasPrefix(addr, "https://"))
	return &Minio{db: db}, err
}

func (m *Minio) List(ns []string, limits ...string) ([]string, error) {
	namespace := resolveNamespace(ns)
	limits = resolveLimits(limits)
	done := make(chan struct{})
	defer close(done)
	keys := []string{}
	for resp := range m.db.ListObjects(namespace, "", true, done) {
		if resp.Key < limits[0] {
			continue
		} else if resp.Key > limits[1] {
			break
		}
		keys = append(keys, resp.Key)
	}
	return keys, nil
}

func (m *Minio) Get(key string, ns ...string) ([]byte, error) {
	namespace := resolveNamespace(ns)
	obj, err := m.db.GetObject(namespace, key, minio.GetObjectOptions{})
	if err == nil {
		var stat minio.ObjectInfo
		if stat, err = obj.Stat(); err == nil {
			err = stat.Err
		}
	}
	if isNotExistErr(err) {
		return nil, ErrNotFound
	} else if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(obj)
}

func (m *Minio) Set(key string, value []byte, ns ...string) error {
	namespace := resolveNamespace(ns)
	if ok, err := m.db.BucketExists(namespace); err != nil {
		return err
	} else if !ok {
		if err := m.db.MakeBucket(namespace, ""); err != nil {
			return err
		}
	}
	_, err := m.db.PutObject(namespace, key, bytes.NewBuffer(value), int64(len(value)), minio.PutObjectOptions{})
	return err
}

func (m *Minio) Close() error {
	m.db = nil
	return nil
}

func isNotExistErr(err error) bool {
	return err != nil && strings.Contains(err.Error(), "does not exist")
}
