package upyun

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"
	"time"
)

var (
	bucket   = "pychr"
	op       = "root"
	password = "MRnhPT4VlGXAuDEWe4l7JTxrdxbXjZFE"
	token    = "2cb96a90-b787-43a4-91ec-54d796c069da"
)

const (
	effectiveDuration = time.Minute * 60
)

type RestAPIToken struct {
	Authorization   string
	XUpYunUriPrefix string
	XUpYunExpire    int64
}

func GetUpYunRestAPIUploadToken(method string) (r RestAPIToken) {
	now := time.Now()
	expire := now.Add(effectiveDuration).Unix()

	elem := FilterNoneString([]string{"PUT", "/" + bucket, strconv.FormatInt(expire, 10)})
	p := strings.Join(elem, "&")
	key := []byte(StringMD5(password))
	mac := hmac.New(sha1.New, key)
	mac.Write([]byte(p))
	hash := mac.Sum(nil)
	sign := base64.StdEncoding.EncodeToString(hash)
	authorization := "UPYUN " + op + ":" + sign

	r.XUpYunExpire = expire
	r.Authorization = authorization
	r.XUpYunUriPrefix = "/" + bucket
	return
}

type FormAPIToken struct {
	Bucket        string
	Method        string
	Authorization string
	XUpYunExpire  int64
	Policy        string
}

func GetUpYunFormAPIToken(method, saveKey string) (r FormAPIToken, err error) {
	method = strings.ToUpper(method)
	now := time.Now()
	expiration := now.Add(effectiveDuration).Unix()

	paramsFormat := `{"bucket": "%v", "expiration": %v, "save-key": "%v"}`
	params := fmt.Sprintf(paramsFormat, bucket, expiration, saveKey)

	policy := StringBase64(params)

	elem := FilterNoneString([]string{method, "/" + bucket, policy})
	p := strings.Join(elem, "&")
	key := []byte(StringMD5(password))
	mac := hmac.New(sha1.New, key)
	mac.Write([]byte(p))
	hash := mac.Sum(nil)
	sign := base64.StdEncoding.EncodeToString(hash)
	authorization := "UPYUN " + op + ":" + sign

	r.Bucket = bucket
	r.Method = method
	r.XUpYunExpire = expiration
	r.Authorization = authorization
	r.Policy = policy
	return
}

func FilterNoneString(list []string) (r []string) {
	for _, t := range list {
		if t != "" {
			r = append(r, t)
		}
	}
	return
}

// 生成字符串MD5
func StringMD5(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}

func StringBase64(s string) (r string) {
	return base64.StdEncoding.EncodeToString([]byte(s))
}
