package ufsdk

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strings"
)

//Auth 构造签名的工具，使用本 SDK 你不需要关心 Auth 这一整个模块，以及它暴露出来的签名算法。
//如果你希望自己封装 API 可以使用这里面的暴露出来的接口来填充 http authorization。
type Auth struct {
	publicKey  string
	privateKey string
}

//NewAuth 构造一个新的签名工具，传入你的公钥匙和私钥。
func NewAuth(publicKey, privateKey string) Auth {
	return Auth{
		publicKey:  publicKey,
		privateKey: privateKey,
	}
}

//Authorization 构造一个主要用于上传文件的签名，返回 HMAC-Sh1 的签名字符串，可以直接填充到 HTTP authorization header 里面。
//key 是传到 ufile 所使用的文件名，bucekt 是文件上传后存放的 bucket。
//method 就是你当前这个 HTTP 请求的 Method。
//header 就是你当前这个 HTTP 的 header。
func (A Auth) Authorization(method, bucket, key string, header http.Header) string {
	var sigData string
	method = strings.ToUpper(method)

	md5 := header.Get("Content-MD5")
	contentType := header.Get("Content-Type")
	date := header.Get("Date")

	sigData = method + "\n" + md5 + "\n" + contentType + "\n" + date + "\n"
	sigData += A.CanonicalizedUcloudHeaders(header)
	resource := "/" + bucket + "/" + key
	sigData += resource

	signature := A.signature(sigData)

	return "UCloud " + A.publicKey + ":" + signature
}

//AuthorizationPrivateURL 构造私有空间文件下载链接的签名，其中 expires 是当前的时间加上一个过期的时间，再转为字符串。格式是 unix time second.
//其他的参数含义和 Authoriazation 函数一样。
//有时我们需要把签名后的 URL 直接拿来用，header 参数可以直接构造一个空的 http.Header{} 传入即可。
func (A Auth) AuthorizationPrivateURL(method, bucket, key, expires string, header http.Header) (string, string) {
	var sigData string
	method = strings.ToUpper(method)
	md5 := header.Get("Content-MD5")
	contentType := header.Get("Content-Type")

	sigData = method + "\n" + md5 + "\n" + contentType + "\n" + expires + "\n"
	resource := "/" + bucket + "/" + key
	sigData += resource

	signature := A.signature(sigData)

	return signature, A.publicKey
}

//AuthorizationPolicy 构造支持回调策略的签名，policy 是经过 base64 编码后的 json string。
//本签名函数就是多了一个 policy 字段，其他的参数和 Authoriazation 一样。
func (A Auth) AuthorizationPolicy(method, bucket, key, policy string, header http.Header) string {
	var sigData string
	method = strings.ToUpper(method)

	md5 := header.Get("Content-MD5")
	contentType := header.Get("Content-Type")
	date := header.Get("Date")

	sigData = method + "\n" + md5 + "\n" + contentType + "\n" + date + "\n"
	resource := "/" + bucket + "/" + key
	sigData += resource
	sigData += policy

	signature := A.signature(sigData)

	return "UCloud " + A.publicKey + ":" + signature + ":" + policy
}

func (A Auth) signature(data string) string {
	mac := hmac.New(sha1.New, []byte(A.privateKey))
	mac.Write([]byte(data))

	return base64.StdEncoding.EncodeToString(mac.Sum(nil))
}

//AuthorizationBucketMgr 生成用于管理 bucket 的签名。
func (A Auth) AuthorizationBucketMgr(query url.Values) string {
	query.Add("PublicKey", A.publicKey)

	var signstring string
	var keys []string
	for k := range query {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		signstring += k + query.Get(k) //Get first value
	}
	signstring += A.privateKey

	h := sha1.New()
	io.WriteString(h, signstring)

	query.Add("Signature", fmt.Sprintf("%x", h.Sum(nil)))
	return query.Encode()
}

//CanonicalizedUcloudHeaders 用于将自定义请求报头规范化为string
//ucloudHeader 支持自定义请求头
func (A Auth) CanonicalizedUcloudHeaders(ucloudHeader http.Header) string {
	keys := make([]string, 0)
	headers := make(map[string]string, 0)
	for k := range ucloudHeader {
		newkey := strings.ToLower(k)
		if strings.HasPrefix(newkey, "x-ufile-") || strings.HasPrefix(newkey, "x-ucloud-") {
			headers[newkey] = strings.TrimSpace(ucloudHeader[k][0])
			keys = append(keys, newkey)
		}
	}
	sort.Strings(keys)

	s := ""
	for _, k := range keys {
		v := headers[k]
		s += k + ":" + v + "\n"
	}
	return s
}
