package storage

import (
	"context"
	"errors"
	"fmt"
	"github.com/qiniu/go-sdk/v7/auth/qbox"
	"github.com/qiniu/go-sdk/v7/storage"
	"os"
	"time"
)

type QiniuConfig struct {
	Bucket  string
	AK      string
	SK      string
	Timeout time.Duration
}

type Qiniu struct {
	config *QiniuConfig
}

func NewQiniu(cfg *QiniuConfig) Storage {

	return &Qiniu{
		config: cfg,
	}
}

func (s *Qiniu) Put(fileName, objectName string, onProcess func(fsize, uploaded int64)) error {
	mac := qbox.NewMac(s.config.AK, s.config.SK)
	putPolicy := storage.PutPolicy{
		Scope:   s.config.Bucket,
		Expires: uint64(time.Now().Add(12 * time.Hour).Unix()),
	}
	upToken := putPolicy.UploadToken(mac)
	cfg := storage.Config{}
	cfg.UseHTTPS = false
	cfg.UseCdnDomains = false

	// 构建表单上传的对象
	formUploader := storage.NewFormUploader(&cfg)
	ret := storage.PutRet{}

	// 可选配置
	putExtra := storage.PutExtra{
		Params:     map[string]string{},
		OnProgress: onProcess,
	}
	err := formUploader.PutFile(context.Background(), &ret, upToken, objectName, fileName, &putExtra)
	if err != nil {
		return err
	}
	return nil
}

func (s *Qiniu) PutResume(fileName, objectName string, onProcess func(fsize, uploaded int64)) error {
	mac := qbox.NewMac(s.config.AK, s.config.SK)
	putPolicy := storage.PutPolicy{
		Scope: s.config.Bucket,
	}
	upToken := putPolicy.UploadToken(mac)
	cfg := storage.Config{}
	cfg.UseHTTPS = false
	cfg.UseCdnDomains = false

	file, err := os.Open(fileName)
	if err != nil {
		return err
	}

	fileInfo, _ := file.Stat()
	fileSize := fileInfo.Size()

	resumeUploader := storage.NewResumeUploaderV2(&cfg)
	ret := storage.PutRet{}

	recorder, err := storage.NewFileRecorder(os.TempDir())
	if err != nil {
		return err
	}

	partSize := int64(1024 * 1024 * 1)
	partUploaded := int64(0)
	putExtra := storage.RputV2Extra{
		Recorder: recorder,
		Notify: func(partNumber int64, ret *storage.UploadPartsRet) {
			partUploaded += 1
			onProcess(fileSize, partUploaded*partSize)
		},
		PartSize: partSize,
	}
	err = resumeUploader.PutFile(context.Background(), &ret, upToken, objectName, fileName, &putExtra)
	if err != nil {
		return err
	}
	return nil
}

func (s *Qiniu) Del(objectName string) error {
	mac := qbox.NewMac(s.config.AK, s.config.SK)
	cfg := storage.Config{
		UseHTTPS: false,
	}
	bucketManager := storage.NewBucketManager(mac, &cfg)
	return bucketManager.Delete(s.config.Bucket, objectName)
}

func (s *Qiniu) List(objectPrefix string) ([]string, error) {
	mac := qbox.NewMac(s.config.AK, s.config.SK)
	cfg := storage.Config{
		UseHTTPS: false,
	}
	result := make([]string, 0)
	marker := ""
	bucketManager := storage.NewBucketManager(mac, &cfg)
	for {
		entries, _, nextMarker, hasNext, err := bucketManager.ListFiles(s.config.Bucket, objectPrefix, "", marker, 1000)
		if err != nil {
			return nil, err
		}
		for _, entry := range entries {
			result = append(result, entry.Key)
		}
		if hasNext {
			marker = nextMarker
		} else {
			break
		}
	}

	return result, nil
}

func (s *Qiniu) Get(objectName, fileName string) error {
	mac := qbox.NewMac(s.config.AK, s.config.SK)
	cfg := storage.Config{
		UseHTTPS: false,
	}

	bucketManager := storage.NewBucketManager(mac, &cfg)

	domains, err := bucketManager.ListBucketDomains(s.config.Bucket)
	if err != nil {
		return err
	}
	if len(domains) <= 0 {
		return errors.New("bucket no domain")
	}
	url := fmt.Sprintf("http://%s/%s", domains[0].Domain, objectName)
	return Download(url, fileName)
}

func (s *Qiniu) Url(objectName string, expire time.Duration) string {
	mac := qbox.NewMac(s.config.AK, s.config.SK)
	cfg := storage.Config{
		UseHTTPS: false,
	}

	bucketManager := storage.NewBucketManager(mac, &cfg)
	domains, err := bucketManager.ListBucketDomains(s.config.Bucket)
	if err != nil {
		return ""
	}
	if len(domains) <= 0 {
		return ""
	}

	domain := "https://" + domains[0].Domain
	if expire == 0 {
		return storage.MakePublicURLv2(domain, objectName)
	} else {
		deadline := time.Now().Add(expire).Unix()
		return storage.MakePrivateURLv2(mac, domain, objectName, deadline)
	}
}

func (s *Qiniu) Stat(objectName string) (*ObjectInfo, error) {
	mac := qbox.NewMac(s.config.AK, s.config.SK)
	cfg := storage.Config{
		UseHTTPS: false,
	}

	bucketManager := storage.NewBucketManager(mac, &cfg)
	fileInfo, err := bucketManager.Stat(s.config.Bucket, objectName)
	if err != nil {
		return nil, err
	}

	info := new(ObjectInfo)
	info.Size = fileInfo.Fsize
	info.Hash = fileInfo.Hash
	info.MimeType = fileInfo.MimeType
	info.PutTime = fileInfo.PutTime
	return info, nil
}

func (s *Qiniu) Fetch(url, objectName string) error {
	mac := qbox.NewMac(s.config.AK, s.config.SK)
	cfg := storage.Config{
		UseHTTPS: false,
	}

	bucketManager := storage.NewBucketManager(mac, &cfg)

	_, err := bucketManager.Fetch(url, s.config.Bucket, objectName)
	if err != nil {
		return err
	}

	return nil
}