package middleware

import (
	"encoding/json"
	"fmt"
	"hpds-iot-web/model"
	e "hpds-iot-web/pkg/err"
	"net/http"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

var secretKey = []byte("17715d3df8712f0a3b31cfed384f668e95822de4e4a371e4ceaa6f1b279e482a0af32b4615d39f8857d0a1d99d2787f773147a9ed7587b243e0fe1b04076e307")

// 验签使用
var AuthKey = "auth-key"
var AuthSignature = "auth-signature"

// Claims 自定义声明
type Claims struct {
	Avatar   string             `json:"avatar"`
	Desc     string             `json:"desc"`
	HomePath string             `json:"homePath"`
	RealName string             `json:"realName"`
	Roles    []model.SystemRole `json:"roles"`
	UserId   int64              `json:"userId"`
	Token    string             `json:"token,omitempty"`
	Phone    string             `json:"phone"`
	jwt.StandardClaims
}

// Sign 生成token
func Sign(user *model.SystemUser, expires int) (string, error) {
	// 过期时间为秒
	expAt := time.Now().Add(time.Duration(expires) * time.Second).Unix()
	// 创建声明
	claims := Claims{
		Avatar:   user.Avatar,
		Desc:     user.Desc,
		HomePath: user.HomePath,
		RealName: user.RealName,
		Roles:    model.GetRolesByUserId(user.UserId),
		UserId:   user.UserId,
		Phone:    user.Phone,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expAt,
			Issuer:    "交科院",
		},
	}
	var err error
	//创建token,指定加密算法为HS256
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	claims.Token, err = token.SignedString(secretKey)
	//生成token
	return claims.Token, err
}

// JwtAuthMiddleware JWT处理中间件
func JwtAuthMiddleware(logger *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		var (
			err      error
			Cookie   *http.Cookie
			usClaims *Claims
			user     *model.SystemUser
		)
		token := c.GetHeader("Authorization")
		// 这里可以过滤不需要进行验证的接口
		if path == "/api/user/login" || path == "/api/health" {
			goto Return
		}
		if len(token) == 0 {
			Cookie, err = c.Request.Cookie("token")
			if err != nil {
				logger.With(
					zap.String("method", c.Request.Method),
					zap.String("path", path),
					zap.Any("request", c.Params),
				).Error(err.Error())
				goto Return
			}
			token = Cookie.Value
		}
		if len(token) <= 0 {
			logger.With(
				zap.String("method", c.Request.Method),
				zap.String("path", path),
				zap.Any("request", c.Params),
			).Error("缺少token")
			goto Return
		}
		usClaims, err = GetUserInfoByToken(c, token, logger)
		if err != nil {
			c.JSON(e.NoAuth, gin.H{"code": e.NoAuth, "msg": "未登录的用户请求"})
			c.Abort()
			return
		}
		if err != nil {
			logger.With(
				zap.String("method", c.Request.Method),
				zap.String("path", path),
				zap.Any("request", c.Params),
			).Error(err.Error())
			goto Return
		}
		user, err = model.GetUserById(usClaims.UserId)
		if err != nil {
			logger.With(
				zap.String("method", c.Request.Method),
				zap.String("path", path),
				zap.Any("request", c.Params),
			).Error(err.Error())
			goto Return
		}
		c.Set("operatorUser", user)
	Return:
		if err != nil {
			c.Header("Content-Type", "application/json")
			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
				"code":    http.StatusBadRequest,
				"status":  http.StatusText(http.StatusBadRequest),
				"path":    path,
				"message": "失败",
				"error":   "token过期/失效,请登录后重试",
				"data":    nil,
			})
			return
		}
		c.Next()
	}
}

func GetUserInfoByToken(ctx *gin.Context, token string, logger *zap.Logger) (data *Claims, err error) {
	nosql := NewNoSql(logger)
	su, err := nosql.Get(ctx, token)
	if err != nil {
		return nil, err
	}
	data = new(Claims)
	err = json.Unmarshal([]byte(fmt.Sprintf("%s", su)), data)
	if err != nil {
		return nil, err
	}
	return data, nil
}