package auth

import (
	"crypto/rand"
	"encoding/base64"
	"errors"
	"eta/eta_mini_ht_api/common/component/cache"
	logger "eta/eta_mini_ht_api/common/component/log"
	"eta/eta_mini_ht_api/common/component/wechat"
	"eta/eta_mini_ht_api/common/exception"
	authUtils "eta/eta_mini_ht_api/common/utils/auth"
	"eta/eta_mini_ht_api/common/utils/jwt"
	"eta/eta_mini_ht_api/common/utils/redis"
	"eta/eta_mini_ht_api/common/utils/sms"
	stringUtils "eta/eta_mini_ht_api/common/utils/string"
	smsService "eta/eta_mini_ht_api/domian/sms"
	userService "eta/eta_mini_ht_api/domian/user"
	"fmt"
	"gorm.io/gorm"
	"strings"
)

var (
	smsSender sms.SMSClient

	wechatClient *wechat.Client

	redisCache *cache.RedisCache
)

func message() sms.SMSClient {
	if smsSender == nil {
		smsSender = sms.GetInstance()
	}
	return smsSender
}

func wx() *wechat.Client {
	if wechatClient == nil {
		wechatClient = wechat.GetInstance()
	}
	return wechatClient
}
func SendSMSCode(mobile string) (err error) {
	code := rd().GetString(redis.GenerateSmsKey(mobile))
	if stringUtils.IsEmptyOrNil(code) {
		code, err = authUtils.GenerateCode(6)
		if err != nil {
			logger.Warn("生成验证码失败:%v", err)
			return exception.New(exception.SMSCodeGenerateFailed)
		}
		codeDTO := smsService.CodeDTO{
			Mobile:       mobile,
			Code:         code,
			ExpireMinute: message().GetExpireMinute(),
		}
		//消息domain层
		var smid int
		smid, err = smsService.SendSMSCode(codeDTO)
		if err != nil {
			logger.Error("发送短信失败：%v", err)
			err = exception.New(exception.SendingSMSFailed)
		}
		logger.Debug("验证码:%v", code)
		_, err = message().SendSms(mobile, code, smid)
		if err != nil {
			logger.Error("发送短信失败：%v", err)
			err = exception.New(exception.SendingSMSFailed)
		}
		return
	}
	return exception.New(exception.SMSCodeAlreadySent)
}

func CheckUser(mobile string, code string) (err error) {
	smsCode := rd().GetString(redis.GenerateSmsKey(mobile))
	if stringUtils.IsEmptyOrNil(smsCode) {
		logger.Warn("验证码已过期:%v", code)
		codeDTO := smsService.CodeDTO{
			Mobile: mobile,
			Code:   code,
		}
		_ = smsService.TryExpireCode(codeDTO)
		err = exception.New(exception.SMSCodeExpired)
		return
	}
	if smsCode != code {
		err = exception.New(exception.SMSCodeError)
		return
	}
	return nil
}

type LoginDTO struct {
	VerifyCode string
	Code       string
	Mobile     string
}

func Login(login LoginDTO) (token string, err error) {
	var user userService.UserDTO
	user, err = userService.GetUserByMobile(login.Mobile)
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			err = exception.New(exception.UnknownError)
			return
		}
		//注册用户
		var wechatInfo wechat.WxUser
		//微信请求异常
		wechatInfo, err = wx().Login(login.Code)
		//微信客户端的异常不做处理，已经是EtaError
		if err != nil {
			return
		}
		user = initUser(wechatInfo, login)
		err = userService.RegisterTemplateUser(&user)
		if err != nil {
			err = exception.New(exception.TemplateUserCreateFailed)
			return
		}
	}
	token, err = jwt.CreateToken(user.OpenId, user.Mobile)
	if err != nil {
		err = exception.New(exception.GenerateTokenFailed)
		return
	}
	err = rd().SetString(redis.GenerateTokenKey(user.Mobile), token, 90*24*60*60)
	if err != nil {
		err = exception.New(exception.GenerateTokenFailed)
		return
	}
	codeDTO := smsService.CodeDTO{
		Mobile: login.Mobile,
		Code:   login.VerifyCode,
	}
	//登录成功删除短信验证码，数据库留痕
	err = rd().Delete(redis.GenerateSmsKey(login.Mobile))
	if err != nil {
		logger.Error("清除redis 短信验证码失败,%v", err)
		_ = rd().SetString(redis.GenerateSmsKey(login.Mobile), "", 1)
		return "", err
	}
	_ = smsService.TryVerifiedCode(codeDTO)
	return
}

func BindMobile(userId int, mobile, verifyCode string) (err error) {
	err = userService.BindUserMobile(userId, mobile)
	if err != nil {
		err = exception.New(exception.BindMobileFailed)
		return
	}
	codeDTO := smsService.CodeDTO{
		Mobile: mobile,
		Code:   verifyCode,
	}
	//登录成功删除短信验证码，数据库留痕
	err = rd().Delete(redis.GenerateSmsKey(mobile))
	if err != nil {
		logger.Error("清除redis 短信验证码失败,%v", err)
		_ = rd().SetString(redis.GenerateSmsKey(mobile), "", 1)
		return err
	}
	_ = smsService.TryVerifiedCode(codeDTO)
	return
}

func RefreshToken(code string) (token string, isBindMobile int, err error) {
	//注册用户
	var wechatInfo wechat.WxUser
	//微信请求异常
	wechatInfo, err = wx().Login(code)
	//微信客户端的异常不做处理，已经是EtaError
	if err != nil {
		fmt.Println("wx.Login,err:" + err.Error())
		return
	}
	user, err := userService.GetTemplateUserByOpenId(wechatInfo.OpenId)
	var isAdd bool
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			isAdd = true
		} else {
			err = exception.New(exception.TemplateUserNotFound)
			return
		}
	}
	token, err = jwt.CreateToken(wechatInfo.OpenId, wechatInfo.OpenId)
	if err != nil {
		err = exception.New(exception.GenerateTokenFailed)
		return
	}
	err = rd().SetString(redis.GenerateTokenKey(wechatInfo.OpenId), token, 90*24*60*60)
	if err != nil {
		err = exception.New(exception.GenerateTokenFailed)
		return
	}
	if user.Mobile == "" {
		isBindMobile = 0
	} else {
		isBindMobile = 1
	}
	if isAdd {
		user.OpenId = wechatInfo.OpenId
		user.UnionId = wechatInfo.UnionId
		var isRegister bool

		//判断unionid是否存在
		wxUser, unionErr := userService.GetTemplateUserByUnionId(wechatInfo.UnionId)
		if unionErr != nil {
			if unionErr == gorm.ErrRecordNotFound { //注册用户
				isRegister = true
			} else {
				err = exception.New(exception.TemplateUserNotFound)
				return
			}
		}
		if isRegister {
			err = userService.RegisterTemplateUser(&user)
		} else { //修改微信小程序openid
			err = userService.BindUserXcxOpenId(wxUser.Id, user.OpenId)
		}
	}
	return
}

func GetAreaCodes() (list []userService.AreaCodeDTO, err error) {
	list, err = userService.GetAreaCodes()
	if err != nil {
		err = exception.New(exception.GetAreaCodesFailed)
	}
	return
}

func GetValidAreaCodes() (list []string) {
	str := rd().GetString(redis.ValidAreaCode)
	if str == "" {
		codes, err := userService.GetValidAreaCodes()
		if err != nil {
			return []string{}
		}
		var validCodes []string
		for _, code := range codes {
			validCodes = append(validCodes, code.CodeValue)
		}
		err = rd().SetString(redis.ValidAreaCode, strings.Join(validCodes, ","), 60*60)
		if err != nil {
			logger.Error("设置Redis区号失败:%v", err)
			list = validCodes
		}
		list = strings.Split(rd().GetString(redis.ValidAreaCode), ",")
	} else {
		list = strings.Split(str, ",")
	}
	return
}
func initUser(user wechat.WxUser, dto LoginDTO) userService.UserDTO {
	username := generateRandomUsername(user.OpenId)
	if stringUtils.IsBlank(username) {
		username = dto.Mobile
	}
	return userService.UserDTO{
		OpenId:   user.OpenId,
		UnionId:  user.UnionId,
		Username: username,
		Mobile:   dto.Mobile,
	}
}

// GenerateRandomUsername 生成基于UnionId的随机用户名
func generateRandomUsername(openId string) string {
	// 生成一个随机的4字节二进制数据
	randomBytes := make([]byte, 4)
	_, err := rand.Read(randomBytes)
	if err != nil {
		return ""
	}
	// 将随机字节转换为base64编码的URL安全字符串
	// 并去掉可能出现的等于号
	randomStr := strings.TrimRight(base64.URLEncoding.EncodeToString(randomBytes), "=")
	// 拼接UnionId和随机字符串
	username := fmt.Sprintf("%s_%s", openId, randomStr)
	return username
}

func rd() *cache.RedisCache {
	if redisCache == nil {
		redisCache = cache.GetInstance()
	}
	return redisCache
}

func BindXcxOpenId(userId int, openId string) (err error) {
	err = userService.BindUserXcxOpenId(userId, openId)
	return
}

// 绑定微信公众号
func BindWxGzh(code string) (isBind bool, err error) {
	//注册用户
	wxUser, err := GetWxUserInfo(code)
	if err != nil {
		logger.Error("获取微信公众号用户信息失败" + err.Error())
		err = exception.New(exception.TemplateUserNotFound)
		return
	}
	logger.Info("GetWxUserInfo:")
	if wxUser.ErrCode != 0 {
		logger.Error("获取微信公众号用户信息失败" + wxUser.ErrMsg)
		err = exception.New(exception.TemplateUserNotFound)
		return
	}

	return BindWxGzhByOpenId(wxUser.OpenId)
}

// BindWxGzhByOpenId
// @Description: 通过openid绑定微信公众号
// @author: Roc
// @datetime 2024-08-13 13:19:49
// @param openId string
// @return isBind bool
// @return err error
func BindWxGzhByOpenId(openId string) (isBind bool, err error) {
	logger.Info("openId:" + openId)
	wxUserDetail, err := GetWxUserDetail(openId)
	if err != nil {
		logger.Error("获取微信公众号用户详细信息失败" + err.Error())
		err = exception.New(exception.TemplateUserNotFound)
		return
	}

	unionId := wxUserDetail.UnionID
	followingGzh := int(wxUserDetail.Subscribe)

	logger.Info("unionId:" + unionId)
	var isAdd bool
	user, err := userService.GetTemplateUserByGzhOpenId(openId)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			isAdd = true
		} else {
			err = exception.New(exception.TemplateUserNotFound)
			return
		}
	}

	logger.Info("IsAdd", isAdd)

	if isAdd {
		user.GzhOpenId = openId
		user.UnionId = unionId
		user.FollowingGzh = followingGzh
		var isRegister bool

		//判断unionid是否存在
		wxUser, unionErr := userService.GetTemplateUserByUnionId(unionId)

		if unionErr != nil {
			if unionErr == gorm.ErrRecordNotFound { //注册用户
				isRegister = true
			} else {
				err = exception.New(exception.TemplateUserNotFound)
				return
			}
		}

		logger.Info("isRegister", isRegister)

		if isRegister {
			err = userService.RegisterTemplateUser(&user)
			if err != nil {
				logger.Info("RegisterTemplateUser,Err" + err.Error())
			}
		} else { //修改微信小程序openid
			logger.Info("wxUser.Id:%d", wxUser.Id)
			err = userService.BindUserGzhOpenId(wxUser.Id, openId, followingGzh)
			if err != nil {
				logger.Info("BindUserGzhOpenId,Err" + err.Error())
			}
		}
	} else {
		isBind = true
		err = userService.BindUserGzhOpenId(user.Id, openId, followingGzh)
		if err != nil {
			logger.Info("BindUserGzhOpenId,Err" + err.Error())
		}
	}

	return
}

// UnSubscribeWxGzhByOpenId
// @Description: 通过openid取消关注微信公众号
// @author: Roc
// @datetime 2024-08-13 13:19:49
// @param openId string
// @return isBind bool
// @return err error
func UnSubscribeWxGzhByOpenId(openId string) {
	logger.Info("openId:" + openId)
	var err error
	defer func() {
		if err != nil {
			logger.Info("通过openid取消关注微信公众号失败，openId:", openId, "；错误信息：", err.Error())
		}
	}()
	// 通过公众号openid获取用户信息
	user, err := userService.GetTemplateUserByGzhOpenId(openId)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			err = nil
			// 找不到就直接返回了
			return
		} else {
			err = exception.New(exception.TemplateUserNotFound)
			return
		}
	}

	// 解绑用户
	err = userService.BindUserGzhOpenId(user.Id, openId, 0)
	if err != nil {
		logger.Info("UnBindWxGzhByOpenId,Err" + err.Error())
	}

	return
}
