package jwthelper

import (
	"go-admin/common/const/rediskey"
	"go-admin/common/helper"
	"go-admin/pkg/utility"

	"github.com/golang-jwt/jwt/v5"
	"go.uber.org/zap"

	"fmt"
	"strings"
	"time"

	log "github.com/go-admin-team/go-admin-core/logger"
)

// LoginUserJwt 用户登入信息
type LoginUserJwt struct {
	UserID   int
	NickName string
	Phone    string
	Email    string
	OsType   int
}

// LoginClaims 自定义声明结构体并内嵌jwt.StandardClaims
// jwt包自带的jwt.StandardClaims只包含了官方字段
// 我们这里需要额外记录一个nickname字段，所以要自定义结构体
// 如果想要保存更多信息，都可以添加到这个结构体中
type LoginClaims struct {
	UserID   int    `json:"userid"`
	NickName string `json:"nickname"`
	Phone    string `json:"phone"`
	Email    string `json:"email"`
	jwt.RegisteredClaims
}

// JwtSigningKey Jwt signing key
var JwtSigningKey = []byte("tokexapp.com")

var LoginTokenValidTime = 48 * time.Hour // 登录验证token有效期
var LoginAdminTokenValidTime = 24 * time.Hour

var TokenNotValid = -1 // Token无效
var TokenExpire = -2   // Token过期
var TokenSuccess = 1   // Token正常

// CreateJwtToken 创建用户jwt token
func CreateJwtToken(userJwt LoginUserJwt, minute int) (string, string) {
	// 创建一个我们自己的声明
	expire := time.Now().Add(time.Minute * time.Duration(minute)) //.Unix()
	claims := LoginClaims{
		UserID:   userJwt.UserID,
		Phone:    userJwt.Phone,
		NickName: userJwt.NickName,
		Email:    userJwt.Email,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expire),
			Issuer:    "Tokex", // 签发人
		},
	}

	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenStr, err := token.SignedString(JwtSigningKey)
	if err != nil {
		log.Error("获取jwt token失败:", zap.Error(err))
		return "", ""
	}
	return tokenStr, fmt.Sprintf("%v", expire.Unix())
}

// MidValidToken 检验token是否有效,返回token解密后的string
func MidValidToken(tokenStr string, source int) (int, string) {
	// 解析token
	token, err := jwt.ParseWithClaims(tokenStr, &LoginClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return JwtSigningKey, nil
	})
	if err != nil {
		if strings.Contains(err.Error(), "token is expired") {
			return TokenExpire, ""
		}
		// loghelper.Error("MidValidToken解析token失败", zap.Error(err))
		return TokenNotValid, ""
	}
	if !token.Valid {
		log.Error("MidValidToken解析token无效", zap.Error(err))
		return TokenNotValid, ""
	}
	claims, ok := token.Claims.(*LoginClaims)
	if !ok {
		return TokenNotValid, ""
	}

	// 校验token
	key := fmt.Sprintf(rediskey.AppLoginUserToken, claims.UserID)
	if source == 3 {
		key = fmt.Sprintf(rediskey.PCLoginUserToken, claims.UserID)
	}
	appToken, _ := helper.DefaultRedis.GetString(key)
	if len(appToken) == 0 {
		//说明未登入或者过期
		return TokenExpire, ""
	}
	if appToken != tokenStr {
		//说明是被t
		return TokenExpire, ""
	}

	return TokenSuccess, strings.Join([]string{utility.IntToString(claims.UserID),
		claims.NickName, claims.Phone, claims.Email}, ",")
}

// GetLoginUserJwt 解析用户登入信息
func GetLoginUserJwt(tokenStr string) LoginUserJwt {
	if len(tokenStr) == 0 {
		return LoginUserJwt{}
	}
	tokenArr := strings.Split(tokenStr, ",")
	arrLen := len(tokenArr)
	item := LoginUserJwt{
		UserID: utility.StringAsInteger(tokenArr[0]),
	}
	if arrLen > 1 {
		item.NickName = tokenArr[1]
	}
	if arrLen > 2 {
		item.Phone = tokenArr[2]
	}
	if arrLen > 3 {
		item.Email = tokenArr[3]
	}
	return item
}

// AgentLoginJwt ==============Agent 代理商 JWT========================//
type AgentLoginJwt struct {
	AgentId int    `json:"agent_id"` // 代理商ID
	UserId  int    `json:"user_id"`  // 代理商关联的用户ID
	Name    string `json:"name"`     // 代理商姓名
	Email   string `json:"email"`    // 代理商邮件
	IP      string `json:"ip"`       // 代理商IP
}
type AgentLoginClaims struct {
	AgentId              int    `json:"agent_id"` // 代理商ID
	UserId               int    `json:"user_id"`  // 代理商关联的用户ID
	Name                 string `json:"name"`     // 代理商姓名
	Email                string `json:"email"`    // 代理商邮件
	IP                   string `json:"ip"`       // 代理商IP
	jwt.RegisteredClaims        //StandardClaims
}

var JwtAgentSigningKey = []byte("tokexagent.com") //Jwt signing key

// CreateAgentJwtToken 代理商产生的token
func CreateAgentJwtToken(userJwt AgentLoginJwt, minute int) string {
	// 创建一个我们自己的声明
	exp := time.Now().Add(time.Minute * time.Duration(minute))
	c := AgentLoginClaims{
		userJwt.AgentId,
		userJwt.UserId, // 自定义字段
		userJwt.Name,
		userJwt.Email,
		userJwt.IP,
		jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(exp), // 过期时间
			Issuer:    "Tokex-Agent",           // 签发人
		},
	}
	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	tokenStr, err := token.SignedString(JwtAgentSigningKey)
	if err != nil {
		log.Error("获取jwt token失败:", zap.Error(err))
		return ""
	}
	return tokenStr
}

// MidValidAgentToken 检验token是否有效,返回token解密后的string
func MidValidAgentToken(tokenStr string) (int, string) {
	// 解析token
	token, err := jwt.ParseWithClaims(tokenStr, &AgentLoginClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return JwtAgentSigningKey, nil
	})
	if err != nil {
		return TokenNotValid, ""
	}
	if !token.Valid {
		log.Error("MidValidAgentToken解析token无效", zap.Error(err))
		return TokenNotValid, ""
	}
	claims, ok := token.Claims.(*AgentLoginClaims)
	if !ok {
		return TokenNotValid, ""
	}

	// 校验token
	key := fmt.Sprintf(rediskey.AgentLoginUserToken, claims.AgentId)
	appToken, _ := helper.DefaultRedis.GetString(key)
	if len(appToken) == 0 {
		return TokenExpire, ""
	}
	if appToken != tokenStr {
		return TokenExpire, ""
	}

	return TokenSuccess, strings.Join([]string{utility.IntToString(claims.AgentId), utility.IntToString(claims.UserId),
		claims.Name, claims.Email, claims.IP}, ",")
}

// GetLoginAgentJwt 解析代理商登入信息
func GetLoginAgentJwt(tokenStr string) AgentLoginJwt {
	if len(tokenStr) == 0 {
		return AgentLoginJwt{}
	}
	tokenArr := strings.Split(tokenStr, ",")
	arrLen := len(tokenArr)
	item := AgentLoginJwt{
		AgentId: utility.StringAsInteger(tokenArr[0]),
	}
	if arrLen > 1 {
		item.UserId = utility.StringAsInteger(tokenArr[1])
	}
	if arrLen > 2 {
		item.Name = tokenArr[2]
	}
	if arrLen > 3 {
		item.Email = tokenArr[3]
	}
	if arrLen > 4 {
		item.IP = tokenArr[4]
	}
	return item
}

// ========== Admin 专用 JWT ========== //

type AdminLogin struct {
	UserID   int    `json:"user_id"`
	Account  string `json:"account"`
	NickName string `json:"nickname"`
	Phone    string `json:"phone"`
	Email    string `json:"email"`
	IP       string `json:"ip"`
}

type AdminLoginClaims struct {
	AdminLogin
	jwt.RegisteredClaims //StandardClaims
}

var AdminSignedKey = []byte("adm23inTOKexKey:237") // Admin 签名 Key

const AdminTokenIssue = "tokexAdmin" // Admin 签发人

// GenerateAdminLoginToken 生成管理员登录 Token
func GenerateAdminLoginToken(admin AdminLogin, expires time.Duration) string {
	exp := time.Now().Add(expires)
	claims := AdminLoginClaims{
		admin,
		jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(exp), // 过期时间
			Issuer:    AdminTokenIssue,         // 签发人
		},
	}

	// 使用指定的签名方法创建签名对象
	jwtToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(AdminSignedKey)
	if err != nil {
		log.Error("获取 jwtToken 失败：", zap.Error(err))
	}

	return jwtToken
}

// ParseAdminLoginToken 解析管理员登录 Token
func ParseAdminLoginToken(jwtToken string) (pass bool, adminLogin AdminLoginClaims) {
	// 解析 jwtToken
	token, err := jwt.ParseWithClaims(jwtToken, &AdminLoginClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return AdminSignedKey, nil
	})
	if err != nil {
		log.Error("解析 jwtToken 失败：", zap.Error(err))
		return false, adminLogin
	}

	// 判断是否验证通过，并将 Claims => AdminLoginClaims
	if claims, pass := token.Claims.(*AdminLoginClaims); pass && token.Valid {
		return pass, *claims
	}

	return false, adminLogin
}
