package internal

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/os/glog"
	rand "math/rand"
	"strings"
	"sync"
	"time"
	"yx-dataset-server/app/errors"
	"yx-dataset-server/app/model"
	"yx-dataset-server/app/schema"
	"yx-dataset-server/library/logger"
	"yx-dataset-server/library/redis"
	"yx-dataset-server/library/sms"
	"yx-dataset-server/library/utils"
)

// NewVerifyCode 创建VerifyCode
func NewVerifyCode(
	mUser model.IUser,
) *VerifyCode {
	return &VerifyCode{
		userModel: mUser,
	}
}

// VerifyCode 创建VerifyCode对象
type VerifyCode struct {
	sync.RWMutex
	userModel model.IUser
}

func (a *VerifyCode) getKey(businessType int, tel, storeType string) string {
	return utils.Base64Encode(fmt.Sprintf("%d%s%s", businessType, tel, storeType))
}

// Check 检查手机号及验证码的有效性
func (a *VerifyCode) Check(ctx context.Context, req schema.VerifyCodeCheck) (bool, error) {
	if req.Tel == "" || len(req.Tel) < 11 {
		return false, errors.New("请输入合法的手机号")
	}

	var businessType string
	switch req.BusinessType {
	case 1:
		businessType = "login"
	case 2:
		businessType = "changePass"
	default:
		return false, errors.New("业务类型参数不正确")
	}

	if req.Token == "" {
		req.Token = fmt.Sprintf("%s:%s:%s", businessType, "code", req.Tel)
	}

	val, err := redis.GetClient().Get(ctx, req.Token)
	if err != nil {
		return false, errors.ErrBadRequest
	} else if val == "" {
		return false, errors.New("验证码已失效，请重新获取验证码")
	}

	var item schema.VerifyCodeItem

	if err := json.Unmarshal([]byte(val), &item); err != nil {
		return false, err
	}

	return item.Code == req.Code && item.BusinessType == req.BusinessType, nil
}

// SendCode 发送短信验证码
func (a *VerifyCode) ALiYunSendSms(ctx context.Context, req schema.VerifyCodeRequest) (*schema.VerifyCodeResponse, error) {
	a.Lock()
	defer a.Unlock()

	switch req.Bzty {
	case 1:
		req.TemplateId = utils.GetConfig("aliyun_sms.loginTemplateCode").String()
		req.BusinessTypeStr = "login"
	case 2:
		req.TemplateId = utils.GetConfig("aliyun_sms.passwordTemplateCode").String()
		req.BusinessTypeStr = "changePass"
	}

	telStoreKey := fmt.Sprintf("%s:%s", "telCount", req.Tel)
	telStoreData, err := redis.GetClient().Get(ctx, telStoreKey)
	if err != nil {
		return nil, errors.ErrBadRequest
	}
	if telStoreData != "" {
		var telStoreItem schema.StoreCountItem
		_ = json.NewDecoder(strings.NewReader(telStoreData)).Decode(&telStoreItem)
		if telStoreItem.Count >= 50 {
			return nil, errors.New("该手机号已被限制获取验证码，请于次日重试")
		}
		req.TelCount = telStoreItem.Count
	}

	codeKey := fmt.Sprintf("%s:%s:%s", req.BusinessTypeStr, "code", req.Tel)

	err = a.aliyunSend(ctx, req)
	if err != nil {
		return nil, err
	}

	logger.Printf(ctx, "短信发送成功:[手机号:%s]", req.Tel[7:11])
	return &schema.VerifyCodeResponse{
		Token: codeKey,
	}, nil

}

func (a *VerifyCode) aliyunSend(ctx context.Context, req schema.VerifyCodeRequest) error {
	if req.Batch {
		return sms.SendBatchSms(sms.ALiYunSendBatchSmsArgs{
			TemplateCode:  req.TemplateId,
			TemplateParam: req.ParamsJson,
			PhoneNumbers:  req.Tel,
		})
	}

	// 随机生成6位验证码
	code := fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
	paramJson := gjson.New("")
	_ = paramJson.Set("code", code)
	glog.Infof(ctx, "验证码发送成功[手机号%s，验证码：%s]", req.Tel, code)
	//发送短信验证码
	err := sms.SendSms(sms.ALiYunSendSmsArgs{
		TemplateCode:  req.TemplateId,
		TemplateParam: paramJson.MustToJsonString(),
		PhoneNumbers:  req.Tel,
	})
	if err != nil {
		return err
	}
	expired := time.Minute * 5
	resendLimit := time.Second * 60
	// 存储重发限制
	resendLimitKey := fmt.Sprintf("%s:%s:%s", req.BusinessTypeStr, "checkResend", req.Tel)
	err = redis.GetClient().Set(ctx, resendLimitKey, 1, resendLimit)
	if err != nil {
		return errors.ErrBadRequest
	}

	// 存储验证码
	codeKey := fmt.Sprintf("%s:%s:%s", req.BusinessTypeStr, "code", req.Tel)
	codeData := schema.VerifyCodeItem{
		Code:         code,
		BusinessType: req.Bzty,
	}
	err = redis.GetClient().Set(ctx, codeKey, codeData, expired)
	if err != nil {
		return errors.ErrBadRequest
	}

	return nil
}
