package logic

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"errors"
	"fmt"
	"juwan-backend/app/snowflake/rpc/snowflake"
	"juwan-backend/app/users/rpc/internal/svc"
	"juwan-backend/app/users/rpc/internal/utils"
	"juwan-backend/app/users/rpc/pb"
	"juwan-backend/common/redisx"
	"strings"

	"github.com/zeromicro/go-zero/core/logx"
)

type RegisterLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewRegisterLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterLogic {
	return &RegisterLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func mustNewRandomNickname() string {
	bytes := make([]byte, 5)
	_, err := rand.Read(bytes)
	if err != nil {
		return "NewUser"
	}
	nickname := strings.Builder{}
	nickname.WriteString("user_")
	nickname.WriteString(hex.EncodeToString(bytes))
	return nickname.String()
}

func (l *RegisterLogic) Register(in *pb.RegisterReq) (*pb.RegisterResp, error) {
	if in.Username == "" || in.Passwd == "" {
		logx.Error("invalid input")
		return nil, errors.New("invalid input")
	}

	redisKey := fmt.Sprintf(redisx.VCODE_KEY_PREFIX, in.RequestId, redisx.SCENE_REG, in.Email)
	logx.Infof("redisKey: %s", redisKey)
	vcode, err := l.svcCtx.RedisCluster.Get(l.ctx, redisKey).Result()
	logx.Infof("vcode:%s, err:%v", vcode, err)
	if err != nil {
		logx.Error("invalid verification code")
		return nil, errors.New("invalid verification code")
	}

	if vcode != in.Vcode {
		logx.Error("invalid verification code")
		return nil, errors.New("invalid verification code")
	}

	resp, err := l.svcCtx.Snowflake.NextId(l.ctx, &snowflake.NextIdReq{})
	if err != nil {
		return nil, errors.New("generate user ID failed")
	}

	_, err = l.svcCtx.UsersModelRW.Users.Create().
		SetID(resp.Id).
		SetUsername(in.Username).
		SetPasswordHash(in.Passwd).
		SetPhone(in.Phone).
		SetEmail(in.Email).
		SetBio(in.Email).
		SetAvatar("").
		SetCurrentRole("consumer").
		SetNickname(mustNewRandomNickname()).
		Save(l.ctx)
	if err != nil {
		logx.Errorf("create user err:%v", err)
		return nil, err
	}

	token, err := l.svcCtx.JwtManager.New(l.ctx, &utils.TokenPayload{
		UserId:  resp.Id,
		IsAdmin: false,
	})
	if err != nil {
		logx.Errorf("generate token failed, err:%v", err)
		return nil, errors.New("generate token failed, but user registered successfully")
	}

	return &pb.RegisterResp{
		Res: token,
	}, nil
}
