package svc

import (
	"applet/app/db"
	"applet/app/db/model"
	"applet/app/e"
	"applet/app/lib/mob"
	"applet/app/lib/sms"
	"applet/app/lib/zhimeng"
	"applet/app/md"
	"applet/app/svc"
	"applet/app/utils"
	"applet/app/utils/cache"
	"applet/app/utils/logx"
	sms2 "code.fnuoos.com/go_rely_warehouse/zyos_go_third_party_api.git/sms"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/syyongx/php2go"
	"github.com/tidwall/gjson"
	"math/rand"
	"time"
	"xorm.io/xorm"
)

func Login(c *gin.Context) {
	r := new(md.LoginResponse)
	requestBody := new(md.FastLoginRequestBody)
	if err := c.ShouldBindJSON(&requestBody); err != nil {
		e.OutErr(c, e.ERR_INVALID_ARGS, err)
		return
	}
	user, _ := db.UserFindByMobileAll(svc.MasterDb(c), requestBody.Mobile)
	if user == nil {
		e.OutErr(c, 400, e.NewErr(400, "账号不存在"))
		return
	}
	if user.Password != utils.Md5(requestBody.Password) {
		e.OutErr(c, 400, e.NewErr(400, "密码不正确"))
		return
	}
	if err := svc.FastLoginUserExist(c, r, requestBody); err != nil {
		if e.ErrorIsAccountBan(err) {
			e.OutErr(c, e.ERR_USER_IS_BAN, err)
			return
		}
		e.OutErr(c, 400, err)
		return
	}
	e.OutSuc(c, r, nil)
	return
}
func LoginFastIn(c *gin.Context) {
	var req map[string]string
	if err := c.ShouldBindJSON(&req); err != nil {
		e.OutErr(c, e.ERR_INVALID_ARGS, err)
		return
	}
	if req["phone"] == "" {
		e.OutErr(c, 400, e.NewErr(400, "手机号不能为空"))
		return
	}
	if req["captcha"] == "" {
		e.OutErr(c, 400, e.NewErr(400, "验证码不能为空"))
		return
	}
	mob1, errr := mob.GetMobSDK(c.GetString("mid"))
	if errr != nil {
		e.OutErr(c, e.ERR_MOB_CONFIG, errr)
		return
	}
	if req["zone"] == "" {
		req["zone"] = "86"
	}
	send := map[string]interface{}{
		"phone": req["phone"],
		"zone":  req["zone"],
		"code":  req["captcha"],
	}

	var ok bool
	var err error
	// h5(wap) 登录
	smsPlatform := sms.GetSmsPlatform(c)
	key := fmt.Sprintf("%s_SMS_FastLogin_%s", db.SysCfgGet(c, "app_name"), req["phone"])
	if smsPlatform == "ljioe" {
		b, err := cache.GetBytes(key)
		if err != nil {
			e.OutErr(c, e.ERR_MOB_SMS_NO_EXISTS, err)
			return
		}
		if req["captcha"] != gjson.GetBytes(b, "data.captcha").String() {
			e.OutErr(c, e.ERR_MOB_SMS_NO_SAME, err)
			return
		}
		ok = true
	} else {
		c.Set("not_deduction_doing", "1")
		ok, err = mob1.MobSMS(c, send)
		if err != nil {
			e.OutErr(c, 400, err.Error())
			return
		}
	}

	r := new(md.LoginResponse)
	requestBody := &md.FastLoginRequestBody{
		Mobile: req["phone"],
	}
	if ok {
		// 短信验证通过
		user, isExist, err := db.UserGetByMobileIgnoreDelete(db.DBs[c.GetString("mid")], req["phone"], req["zone"])
		if err != nil {
			e.OutErr(c, e.ERR, err)
			return
		}
		//用户已注册过，获取用户信息并返回
		if isExist {
			if user.DeleteAt != 0 {
				e.OutErr(c, e.ERR_USER_IS_BAN)
				return
			}
			if err := svc.FastLoginUserExist(c, r, requestBody); err != nil {
				if e.ErrorIsAccountBan(err) {
					e.OutErr(c, e.ERR_USER_IS_BAN, err)
					return
				}
				e.OutErr(c, 400, err)
				return
			}
			e.OutSuc(c, r, nil)
			return
		}

		if err := FastLoginUserNoExist(c, r, requestBody); err != nil {
			// 创建失败时清空用户
			if r.UserID != "" {
				UserDeleteOne(c, r.UserID)
			}
			e.OutErr(c, e.ERR, err)
			return
		}
		cache.Del(key)
		// 返回登陆响应
		e.OutSuc(c, r, nil)
		return
	}
	// 验证码无效或者过期，验证码错误
	e.OutErr(c, e.ERR_SMS_AUTH, err)
	return
}
func FastLoginUserNoExist(c *gin.Context, r *md.LoginResponse, requestBody *md.FastLoginRequestBody) error {
	user := new(model.User)
	user.Username = requestBody.Mobile
	user.Password = utils.Md5(requestBody.Mobile)
	if requestBody.Password != "" {
		user.Password = utils.Md5(requestBody.Password)
	}
	user.Platform = c.GetHeader("platform")
	user.Phone = requestBody.Mobile
	appNameCn := db.SysCfgGet(c, "app_name_cn")
	user.Nickname = appNameCn + requestBody.Mobile[len(requestBody.Mobile)-4:len(requestBody.Mobile)]
	user.Zone = "86"
	lvs, err := db.UserLevlAll(c, db.DBs[c.GetString("mid")])
	if err != nil || len(lvs) == 0 {
		return errors.New("会员等级不存在")
	}

	user.Level = lvs[0].Id
	user.State = 1
	user.CreateAt = time.Now()
	user.UpdateAt = time.Now()
	// ---- 插入用户-----！
	exist, err := userCheck(c, user, db.UserInsert)
	if err != nil {
		return err
	}
	if exist {
		return errors.New("Register user is exist")
	}
	r.UserID = utils.IntToStr(user.Uid)
	// 生成jwt
	appName := db.SysCfgGet(c, "app_name")
	token, err := utils.GenToken(user.Uid, user.Username, user.Phone, appName, "", "")
	if err != nil {
		return logx.Warn(err)
	}
	// Insert user profile
	userProfile := new(model.UserProfile)
	userProfile.ArkidToken = token
	userProfile.Uid = user.Uid
	if userProfile.AvatarUrl == "" {
		userProfile.AvatarUrl = db.SysCfgGet(c, "app_user_default_avatar")
	}
	userProfile.IsNew = 1
	exist, err = userProfileCheck(c, userProfile, db.UserProfileInsert)
	if err != nil {
		return err
	}
	if exist {
		return errors.New("user Profile is exist")
	}
	//debug
	//生成邀请码 防止卡死
	GetInviteCode(c, user, userProfile)
	// 异步处理 有效会员和新会员
	// 写入缓存
	key := fmt.Sprintf("%s:token:%s", c.GetString("mid"), user.Username)
	_, err = cache.SetEx(key, token, 39528000) // 半年
	if err != nil {
		return logx.Warn(err)
	}
	r.Token = token
	r.UserID = utils.IntToStr(user.Uid)
	return nil
}
func GetInviteCode(c *gin.Context, user *model.User, userProfile *model.UserProfile) {
	if userProfile.InviteCode != "" {
		return
	}
	cc := c.Copy() // 传递上下文给 goroutine 的话 一定要使用副本，这样才能保证上下文的输出安全
	//生成邀请码的线程 直接访问怕慢
	go UserInviteCode(cc, &md.User{
		Info:    user,
		Profile: userProfile,
	})

	return
}

func userCheck(c *gin.Context, user *model.User, f func(*xorm.Engine, *model.User) (int64, error)) (bool, error) {
	fmt.Println("user in userCheck is ", utils.SerializeStr(user))
	isExist, err := db.UserIsExistByMobile(db.DBs[c.GetString("mid")], user.Username)
	if isExist {
		return isExist, nil
	}
	_, err = f(db.DBs[c.GetString("mid")], user)

	return false, err
}

func userProfileCheck(c *gin.Context, userProfile *model.UserProfile, f func(*xorm.Engine, *model.UserProfile) (int64, error)) (bool, error) {
	isExist, err := db.UserProfileIsExistByUserID(db.DBs[c.GetString("mid")], userProfile.Uid)
	if isExist {
		return isExist, nil
	}
	_, err = f(db.DBs[c.GetString("mid")], userProfile)

	return false, err
}
func UserDeleteOne(c *gin.Context, id interface{}) (int64, error) {

	_, err := db.UserDelete(db.DBs[c.GetString("mid")], id)
	if err != nil {
		return 0, err
	}
	// 删除用户
	_, err = db.UserProfileDelete(db.DBs[c.GetString("mid")], id)
	if err != nil {
		return 0, err
	}
	// 删除用户关系
	_, err = db.UserRelateDelete(db.DBs[c.GetString("mid")], id)
	if err != nil {
		return 0, err
	}
	// 将他的下级pid清零
	us, err := db.UserProfileByPuid(db.DBs[c.GetString("mid")], id)
	if err != nil {
		return 0, err
	}
	for _, item := range *us {
		item.ParentUid = 0
		_, err = db.UserProfileUpdate(db.DBs[c.GetString("mid")], item.Uid, &item, "parent_uid")
		if err != nil {
			continue
		}
	}

	return 1, nil
}

func LoginSendSms(c *gin.Context) {
	var req map[string]string
	if err := c.ShouldBindJSON(&req); err != nil {
		e.OutErr(c, e.ERR_INVALID_ARGS, err)
		return
	}
	if req["phone"] == "" {
		e.OutErr(c, 400, e.NewErr(400, "手机号不能为空"))
		return
	}
	isExist, err := db.UserisExistByMobile(db.DBs[c.GetString("mid")], req["phone"])
	if err != nil {
		e.OutErr(c, e.ERR, err)
		return
	}
	userdata, _ := db.UserFindByPhoneOrNickname(db.DBs[c.GetString("mid")], req["phone"])
	if userdata != nil && userdata.State == 2 {
		e.OutErr(c, 400, e.NewErr(400, "账号已冻结"))
		return
	}
	types := ""
	if !isExist {
		types = "register"
	} else if isExist {
		types = "login"
	}
	smsType := svc.SysCfgGet(c, "sms_type")
	c.Set("sms_type", smsType)
	if c.GetString("sms_type") == "1" {
		count := sms2.SmsNumGetSmsNum(db.Db, "putong", c.GetString("mid"))
		if count-1 < 0 {
			e.OutErr(c, e.ERR_MOB_SMS_NO_AVA, err)
			return
		}
	} else {
		count, _ := zhimeng.SmsNum(db.Db, c.GetString("mid"), c.GetString("sms_type"), db.SysCfgGet(c, "third_zm_sms_key"), db.SysCfgGet(c, "third_zm_sms_secret"))
		if count-1 < 0 {
			e.OutErr(c, e.ERR_MOB_SMS_NO_AVA, err)
			return
		}
	}
	appName := db.SysCfgGet(c, "sms_push_sign")
	captcha := createCaptcha()
	content := fmt.Sprintf("【%s】验证码:%s", appName, captcha)
	key := fmt.Sprintf("%s_SMS_%s", db.SysCfgGet(c, "app_name"), req["phone"])
	templateCode := ""
	switch types {
	case "login":
		content = fmt.Sprintf("【%s】快捷登录验证码:%s", appName, captcha)
		key = fmt.Sprintf("%s_SMS_FastLogin_%s", db.SysCfgGet(c, "app_name"), req["phone"])
		templateCode = "login"
	case "register":
		content = fmt.Sprintf("【%s】注册验证码:%s", appName, captcha)
		key = fmt.Sprintf("%s_SMS_FastLogin_%s", db.SysCfgGet(c, "app_name"), req["phone"])
		templateCode = "register"
	}
	marshal, _ := json.Marshal(c.Request.Header)
	waykeys := "app_" + c.ClientIP() + "_" + utils.IntToStr(utils.GetApiVersion(c)) + "_" + c.Request.RequestURI + "_" + string(marshal)
	postData := map[string]interface{}{
		"content":      content,
		"mobile":       req["phone"],
		"templateCode": templateCode,
		"way":          php2go.Base64Encode(waykeys),
	}
	requestBody := new(md.Register)
	requestBody.Mobile = req["phone"]
	requestBody.Captcha = captcha
	if requestBody.Zone == "" {
		requestBody.Zone = "86"
	}
	err = sms.GetSmsConfig(c, requestBody.Zone, postData)
	if err != nil {
		e.OutErr(c, 400, err)
		return
	}
	tmp := struct {
		Data      md.Register `json:"data"`
		CacheTime int64       `json:"cache_time"`
	}{
		Data:      *requestBody,
		CacheTime: time.Now().Unix(),
	}
	if _, err := cache.SetEx(key, utils.Serialize(tmp), 180); err != nil {
		e.OutErr(c, e.ERR, err)
		return
	}
	//存入一个缓存 用于邀请码注册时候判断
	keys := fmt.Sprintf("%s_SMSCHECK_%s", c.GetString("mid"), requestBody.Mobile)
	tmps := struct {
		Check     int   `json:"check"`
		CacheTime int64 `json:"cache_time"`
	}{
		Check:     1,
		CacheTime: time.Now().Unix(),
	}
	if _, err := cache.SetEx(keys, utils.Serialize(tmps), 3600); err != nil {
		e.OutErr(c, e.ERR, err)
		return
	}
	e.OutSuc(c, "验证码已发送,3分钟内有效", nil)
	return
}
func Sms(c *gin.Context) {
	var req map[string]string
	if err := c.ShouldBindJSON(&req); err != nil {
		e.OutErr(c, e.ERR_INVALID_ARGS, err)
		return
	}
	user := svc.GetUser(c)
	req["phone"] = user.Info.Phone
	types := req["type"]
	var err error
	smsType := svc.SysCfgGet(c, "sms_type")
	c.Set("sms_type", smsType)
	if c.GetString("sms_type") == "1" {
		count := sms2.SmsNumGetSmsNum(db.Db, "putong", c.GetString("mid"))
		if count-1 < 0 {
			e.OutErr(c, e.ERR_MOB_SMS_NO_AVA, err)
			return
		}
	} else {
		count, _ := zhimeng.SmsNum(db.Db, c.GetString("mid"), c.GetString("sms_type"), db.SysCfgGet(c, "third_zm_sms_key"), db.SysCfgGet(c, "third_zm_sms_secret"))
		if count-1 < 0 {
			e.OutErr(c, e.ERR_MOB_SMS_NO_AVA, err)
			return
		}
	}
	appName := db.SysCfgGet(c, "sms_push_sign")
	captcha := createCaptcha()
	content := fmt.Sprintf("【%s】验证码:%s", appName, captcha)
	key := fmt.Sprintf("%s_SMS_%s", db.SysCfgGet(c, "app_name"), req["phone"])
	templateCode := ""
	switch types {
	case "login":
		content = fmt.Sprintf("【%s】快捷登录验证码:%s", appName, captcha)
		key = fmt.Sprintf("%s_SMS_FastLogin_%s", db.SysCfgGet(c, "app_name"), req["phone"])
		templateCode = "login"
	case "bind_alipay":
		content = fmt.Sprintf("【%s】绑定支付宝验证码:%s", appName, captcha)
		key = fmt.Sprintf("%s_SMS_FastLogin_%s", db.SysCfgGet(c, "app_name"), req["phone"])
		templateCode = "bind_alipay"
	case "register":
		content = fmt.Sprintf("【%s】注册验证码:%s", appName, captcha)
		key = fmt.Sprintf("%s_SMS_FastLogin_%s", db.SysCfgGet(c, "app_name"), req["phone"])
		templateCode = "register"
	}
	marshal, _ := json.Marshal(c.Request.Header)
	waykeys := "app_" + c.ClientIP() + "_" + utils.IntToStr(utils.GetApiVersion(c)) + "_" + c.Request.RequestURI + "_" + string(marshal)
	postData := map[string]interface{}{
		"content":      content,
		"mobile":       req["phone"],
		"templateCode": templateCode,
		"way":          php2go.Base64Encode(waykeys),
	}
	requestBody := new(md.Register)
	requestBody.Mobile = req["phone"]
	requestBody.Captcha = captcha
	if requestBody.Zone == "" {
		requestBody.Zone = "86"
	}
	err = sms.GetSmsConfig(c, requestBody.Zone, postData)
	if err != nil {
		e.OutErr(c, 400, err)
		return
	}
	tmp := struct {
		Data      md.Register `json:"data"`
		CacheTime int64       `json:"cache_time"`
	}{
		Data:      *requestBody,
		CacheTime: time.Now().Unix(),
	}
	if _, err := cache.SetEx(key, utils.Serialize(tmp), 180); err != nil {
		e.OutErr(c, e.ERR, err)
		return
	}
	//存入一个缓存 用于邀请码注册时候判断
	keys := fmt.Sprintf("%s_SMSCHECK_%s", c.GetString("mid"), requestBody.Mobile)
	tmps := struct {
		Check     int   `json:"check"`
		CacheTime int64 `json:"cache_time"`
	}{
		Check:     1,
		CacheTime: time.Now().Unix(),
	}
	if _, err := cache.SetEx(keys, utils.Serialize(tmps), 3600); err != nil {
		e.OutErr(c, e.ERR, err)
		return
	}
	e.OutSuc(c, "验证码已发送,3分钟内有效", nil)
	return
}
func createCaptcha() string {
	return fmt.Sprintf("%05v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000))
}
