package storage

import (
	"bytes"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/ncw/rclone/fs"
	"github.com/ncw/rclone/fs/config"
	"github.com/ncw/rclone/fs/object"

	_ "github.com/ncw/rclone/backend/drive"
	_ "github.com/ncw/rclone/backend/local"
	_ "github.com/ncw/rclone/backend/s3"
)

type RClone struct {
	ns string
}

func NewRClone(rclone string, ns ...string) (*RClone, error) {
	namespace := path.Join(ns...)
	_, err := os.Stat(rclone)
	if err == nil {
		os.Setenv("RCLONE_CONFIG", rclone)
		config.ConfigPath = rclone
		config.LoadConfig()
	}
	return &RClone{
		ns: namespace,
	}, err
}

func (rc *RClone) Get(key string, ns ...string) ([]byte, error) {
	namespace := resolveNamespace(ns)
	namespace = path.Join(rc.ns, namespace)
	key = path.Join(namespace, key)
	f, err := fs.NewFs(path.Dir(key))
	if err != nil {
		return nil, err
	}
	obj, err := f.NewObject(path.Base(key))
	if err != nil {
		return nil, err
	}
	r, err := obj.Open()
	if err != nil {
		return nil, err
	}
	return ioutil.ReadAll(r)
}

func (rc *RClone) Set(key string, value []byte, ns ...string) error {
	if len(value) == 0 {
		return rc.Del(key, ns...)
	}
	namespace := resolveNamespace(ns)
	namespace = path.Join(rc.ns, namespace)
	key = path.Join(namespace, key)
	f, err := fs.NewFs(path.Dir(key))
	if err != nil {
		return err
	}
	obj := object.NewStaticObjectInfo(path.Base(key), time.Now(), 0, true, nil, f)
	_, err = f.Put(bytes.NewReader(value), obj)
	return err
}

func (rc *RClone) Del(key string, ns ...string) error {
	namespace := resolveNamespace(ns)
	namespace = path.Join(rc.ns, namespace)
	key = path.Join(namespace, key)
	f, err := fs.NewFs(path.Dir(key))
	if err != nil {
		return err
	}
	obj, err := f.NewObject(path.Base(key))
	if err == fs.ErrorObjectNotFound {
		return nil
	}
	if err != nil {
		return nil
	}
	return obj.Remove()
}

func (rc *RClone) Close() error {
	return nil
}

func (rc *RClone) List(ns []string, limits ...string) ([]string, error) {
	namespace := path.Join(resolveNamespace(ns))
	namespace = path.Join(rc.ns, namespace)
	limits = resolveLimits(limits)

	f, err := fs.NewFs(namespace)
	if err != nil {
		return nil, err
	}
	entries, err := f.List("")
	if err != nil {
		return nil, err
	}
	names := make([]string, 0)
	for _, entry := range entries {
		name := entry.Remote()
		if rc.inRange(name, limits[0], limits[1]) {
			names = append(names, name)
		}
	}
	return names, nil
}

func (rc *RClone) inRange(k, start, stop string) bool {
	return k != "" && k >= start && k <= stop
}
