package auth

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"io"
	"strconv"
	"strings"
	"time"
)

// HMACAuth HMAC算法鉴权
type HMACAuth struct {
	SecretKey []byte
}

// Sign 对给定Body生成expires后失效的签名,expires为过期时间戳,
// 填写为0表示不限制有效期
func (auth HMACAuth) Sign(body string, expires int64) string {
	h := hmac.New(sha256.New, auth.SecretKey)
	expireTimeStamp := strconv.FormatInt(expires, 10)
	_, err := io.WriteString(h, body+":"+expireTimeStamp)
	if err != nil {
		return ""
	}

	return base64.URLEncoding.EncodeToString(h.Sum(nil)) + ":" + expireTimeStamp
}

// Check 对给定Body和Sign进行鉴权,包括对expires的检查
func (auth HMACAuth) Check(body string, sign string) error {
	signSlice := strings.Split(sign, ":")
	// 如果未携带expires字段
	if signSlice[len(signSlice)-1] == "" {
		return ErrAuthFailed
	}

	// 验证是否过期
	expires, err := strconv.ParseInt(signSlice[len(signSlice)-1], 10, 64)
	if err != nil {
		return ErrAuthFailed.WithError(err)
	}
	// 如果签名过期
	if expires < time.Now().Unix() && expires != 0 {
		return ErrExpired
	}

	// 验证签名
	if auth.Sign(body, expires) != sign {
		return ErrAuthFailed
	}
	return nil
}