package http_handler

import (
	"common/db/redis"
	"common/log"
	"common/net/grpc/service"
	"common/net/http/http_resp"
	"common/proto/ss/grpc_pb"
	"common/utils"
	"context"
	"fmt"
	"gateway/config"
	"github.com/gin-gonic/gin"
	"time"
)

// 这个模块处理用户登录

type LoginReq struct {
	Phone string `json:"phone" binding:"required,min=1"`
	Code  string `json:"code" binding:"required,min=1"`
}

type LoginResp struct {
	USN          int64  `json:"usn"`
	Name         string `json:"name"`
	AccessToken  string `json:"accessToken"`
	RefreshToken string `json:"refreshToken"`
}

func Login(c *gin.Context) {
	req := &LoginReq{}
	if err := c.ShouldBindJSON(req); err != nil {
		http_resp.JsonBadRequest(c)
		return
	}
	client, err := service.UserNewClientLB()
	if err != nil {
		log.Errorf("Login UserNewClientLB error: %v", err)
		http_resp.JsonOK(c, http_resp.Error(http_resp.Failed))
		return
	}
	login, err := client.Login(c, &grpc_pb.LoginReq{
		Phone: req.Phone,
		Code:  req.Code,
	})
	if err != nil {
		log.Errorf("Login Login error: %v", err)
		http_resp.JsonOK(c, http_resp.Error(http_resp.Failed))
		return
	}

	at, rt, err := genToken(c, login.USN)
	http_resp.JsonOK(c, http_resp.Success(&LoginResp{
		USN:          login.USN,
		Name:         login.Name,
		AccessToken:  at,
		RefreshToken: rt,
	}))
}

type RefreshTokenReq struct {
	RefreshToken string `json:"refreshToken" binding:"required,min=1"`
}

type RefreshTokenResp struct {
	AccessToken  string `json:"accessToken"`
	RefreshToken string `json:"refreshToken"`
}

func RefreshToken(c *gin.Context) {
	req := &RefreshTokenReq{}
	if err := c.ShouldBindJSON(req); err != nil {
		http_resp.JsonBadRequest(c)
		return
	}
	claims, err := utils.ParseToken(req.RefreshToken, config.Get().Auth.Secret)
	if err != nil {
		http_resp.JsonOK(c, http_resp.Error(http_resp.TokenInvalid))
		return
	}
	if redis.GetClient().Get(c, fmt.Sprintf(config.KeyUserRefreshToken, claims.USN)).String() != req.RefreshToken {
		http_resp.JsonOK(c, http_resp.Error(http_resp.TokenInvalid))
		return
	}
	at, rt, err := genToken(c, claims.USN)
	if err != nil {
		log.Errorf("RefreshToken genToken error: %v, usn: %v", err, claims.USN)
		http_resp.JsonOK(c, http_resp.Error(http_resp.Failed))
		return
	}

	http_resp.JsonOK(c, http_resp.Success(&RefreshTokenResp{
		AccessToken:  at,
		RefreshToken: rt,
	}))
}

func genToken(ctx context.Context, usn int64) (string, string, error) {
	at, err := genTokenOne(ctx, config.KeyUserAccessToken, usn, 2*time.Hour)
	if err != nil {
		return "", "", err
	}
	rt, err := genTokenOne(ctx, config.KeyUserRefreshToken, usn, 3*24*time.Hour)
	if err != nil {
		return "", "", err
	}
	return at, rt, nil
}

func genTokenOne(ctx context.Context, key string, usn int64, ttl time.Duration) (string, error) {
	token, err := utils.GenToken(usn, config.Get().Auth.Secret, time.Duration(config.Get().Auth.Expire)*time.Second)
	if err != nil {
		return "", err
	}
	redis.GetClient().Set(ctx, fmt.Sprintf(key, usn), token, ttl)
	return token, err
}
