package controller

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt"
	"github.com/mojocn/base64Captcha"
	"github.com/satori/go.uuid"
	"strconv"
	"time"

	"iot_manager_service/app/system/model"
	"iot_manager_service/config"
	"iot_manager_service/util"
	"net/http"
	"strings"
)

var Auth = new(auth)

type auth struct{}

var driver = &base64Captcha.DriverString{
	Height:          48,
	Width:           130,
	NoiseCount:      100,
	ShowLineOptions: 2,
	Length:          5,
	BgColor:         nil,
}

func (c *auth) Token(ctx *gin.Context) {
	tenantId := ctx.Query("tenantId")
	userName := ctx.Query("username")
	password := ctx.Query("password")
	grantType := ctx.Query("grant_type")
	refreshToken := ctx.Query("refresh_token")

	// 校验连续登录失败次数
	checkLock()

	userType := ctx.GetHeader(model.UserTypeHeaderKey)
	token := model.Token{
		TenantId:     tenantId,
		UserName:     userName,
		Password:     password,
		GrantType:    grantType,
		RefreshToken: refreshToken,
		UserType:     userType,
	}
	userInfo, err := grant(token, ctx)
	if err != nil {
		ctx.JSON(http.StatusOK, err)
		return
	}
	if userInfo == nil || userInfo.ID == 0 {
		ctx.JSON(http.StatusOK, util.NormalResponse(http.StatusBadRequest, model.UserNotFound, nil))
		return
	}
	if len(userInfo.Roles) == 0 {
		ctx.JSON(http.StatusOK, util.NormalResponse(http.StatusBadRequest, model.UserHasNoRole, nil))
		return
	}

	// access token过期时间2小时
	random := util.RandomString(8)
	jwtToken, e := getAccessToken(*userInfo, random)
	if e != nil {
		ctx.JSON(http.StatusOK, util.NormalResponse(http.StatusBadRequest, e.Error(), nil))
		return
	}

	// redis记录缓存2小时
	util.Redis.Set(getAccessTokenKey(userInfo.TenantId, userInfo.ID, random), jwtToken, 2*time.Hour)
	ctx.JSON(http.StatusOK, model.RspToken{
		TenantId:     userInfo.TenantId,
		UserId:       strconv.FormatInt(userInfo.ID, 10),
		DeptId:       userInfo.DeptId,
		PostId:       userInfo.PostId,
		RoleId:       userInfo.RoleId,
		OauthId:      userInfo.OauthId,
		Account:      userInfo.Account,
		UserName:     userInfo.Name,
		NickName:     userInfo.RealName,
		RoleName:     userInfo.Roles[0],
		Avatar:       userInfo.Avatar,
		AccessToken:  jwtToken,
		RefreshToken: getRefreshToken(*userInfo),
		TokenType:    model.BEARER,
		ExpiresIn:    7200,
		License:      "",
	})
}

func (c *auth) Logout(ctx *gin.Context) {
	emptyKeyFunc := func(t *jwt.Token) (interface{}, error) { return []byte(config.Instance().Server.TokenSign), nil }
	authorization := ctx.GetHeader("Authorization")
	token, err := jwt.ParseWithClaims(authorization, &model.JwtToken{}, emptyKeyFunc)
	if err != nil {
		ctx.JSON(http.StatusUnauthorized, util.NormalResponse(http.StatusUnauthorized, err.Error(), nil))
		return
	}
	jwtToken := token.Claims.(*model.JwtToken)
	err = util.Redis.Del(getAccessTokenKey(jwtToken.TenantId, jwtToken.UserId, jwtToken.Random)).Err()
	//todo 操作记录
	ctx.JSON(http.StatusOK, util.SuccessResponse("", nil))
}

func (c *auth) Captcha(ctx *gin.Context) {
	id := uuid.NewV1().String()
	code := util.RandomString2(5)
	gotItem, _ := driver.DrawCaptcha(code)
	image := gotItem.EncodeB64string()
	rsp := model.RspCaptcha{
		Key:   id,
		Image: image,
	}
	util.Redis.Set(getCaptchaKey(id), code, 5*time.Minute)
	ctx.JSON(http.StatusOK, rsp)
}

//checkLock 校验用户登录失败次数
func checkLock() {

}

func getAccessToken(info model.UserInfo, random string) (string, error) {
	jwtToken := model.JwtToken{StandardClaims: jwt.StandardClaims{
		Audience:  "audience",
		ExpiresAt: time.Now().Add(2 * time.Hour).Unix(),
		Issuer:    "issuser",
		NotBefore: time.Now().Unix(),
	},
		UserId:    info.ID,
		TenantId:  info.TenantId,
		TokenType: "access_token",
		ClientId:  "saber",
		RoleId:    info.RoleId,
		RoleName:  info.Roles[0],
		DeptId:    info.DeptId,
		PostId:    info.PostId,
		OauthId:   info.OauthId,
		Account:   info.Account,
		UserName:  info.Account,
		NickName:  info.RealName,
		Random:    random,
	}

	claims := jwt.NewWithClaims(jwt.SigningMethodHS512, jwtToken)
	return claims.SignedString([]byte(config.Instance().Server.TokenSign))
}

func getRefreshToken(info model.UserInfo) string {
	claims := jwt.NewWithClaims(jwt.SigningMethodHS512, jwt.MapClaims{
		model.Iss:       "issuser",
		model.Aud:       "audience",
		model.ClientId:  "saber",
		model.TokenType: "refresh_token",
		model.UserId:    info.ID,
		model.Exp:       time.Now().Add(7 * 24 * time.Hour).Unix(),
		model.Nbf:       time.Now().Unix(),
	})
	token, _ := claims.SignedString([]byte(config.Instance().Server.TokenSign))
	return token
}

func grant(token model.Token, ctx *gin.Context) (*model.UserInfo, *util.Errors) {
	info := &model.UserInfo{}
	key := ctx.GetHeader(model.CaptchaHeaderKey)
	code := ctx.GetHeader(model.CaptchaHeaderCode)
	// 获取验证码
	result, err := util.Redis.Get(getCaptchaKey(key)).Result()
	if err != nil {
		return nil, util.NormalResponse(http.StatusBadRequest, model.CaptchaNotCorrect, nil)
	}
	redisCode := result
	// 判断验证码
	if config.Instance().Server.CodeEnabled && (key == "" || code == "" || !strings.EqualFold(redisCode, code)) {
		return nil, util.NormalResponse(http.StatusBadRequest, model.CaptchaNotCorrect, nil)
	}

	if token.UserName != "" && token.Password != "" {
		// 获取租户信息
		//Tenant tenant = tenantService.getByTenantId(tenantId);
		//if (TokenUtil.judgeTenant(tenant)) {
		//    throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
		//}
		// 获取用户类型
		// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
		// info.Auth = userService.GetUser(auth.tenantId, auth.UserName, auth.password)
	}

	//测试代码start

	info.TenantId = "000000"
	info.ID = 11112222
	info.Roles = []string{"admin"}
	// 测试代码end
	//todo 操作记录
	return info, nil
}

func getAccessTokenKey(tenantId string, uId int64, random string) string {
	return fmt.Sprintf("access_token_%s_%d_%s", tenantId, uId, random)
}

func getCaptchaKey(uId string) string {
	return fmt.Sprintf("auth:captcha:%s", uId)
}