package auth

import (
	"context"
	"errors"
	"fmt"
	"time"

	"git.i2edu.net/i2/go-zero/core/logx"
	"git.i2edu.net/i2/go-zero/core/stores/sqlc"
	"git.i2edu.net/i2/go-zero/core/stores/sqlx"
	"git.i2edu.net/i2/i2-bill-api/internal/svc"
	"git.i2edu.net/i2/i2-bill-api/internal/types"
	"git.i2edu.net/i2/i2-bill-api/internal/utils"
	"git.i2edu.net/i2/i2-bill-api/model"
	"github.com/dgrijalva/jwt-go"
)

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

func NewLoginByWeixinLogic(ctx context.Context, svcCtx *svc.ServiceContext) LoginByWeixinLogic {
	return LoginByWeixinLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *LoginByWeixinLogic) LoginByWeixin(req types.LoginByWeixinRequest) (*types.LoginByWeixinResponse, error) {
	alb, rtnInfo := req, types.LoginByWeixinResponse{}
	userInfo, err := l.Login(alb.Code)
	if err != nil {
		logx.Error(err)
		return nil, err
	}
	err = l.svcCtx.SqlConn.Transact(func(session sqlx.Session) error {
		var user model.User
		err := session.QueryRowPartial(&user, fmt.Sprintf("select %s from i2bill_user where `weixin_openid` = ? limit 1", model.UserRows), userInfo.OpenID)
		if err == sqlc.ErrNotFound {
			user.Username = utils.GetUUID()
			user.Password = ""
			user.RegisterTime = utils.GetTimestamp()
			user.RegisterIp = ""
			user.Mobile = ""
			user.WeixinOpenid = userInfo.OpenID
			user.Avatar = userInfo.AvatarUrl
			user.Gender = userInfo.Gender
			user.Nickname = userInfo.NickName
			_, err = session.Exec(`insert into i2bill_user  (
				mobile, avatar, weixin_openid, password, birthday, register_time, last_login_time, nickname, 
				erp_id, username, gender, user_level_id, register_ip, last_login_ip
			) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?) `,
				user.Mobile, user.Avatar, user.WeixinOpenid, user.Password, user.Birthday, user.RegisterTime, user.LastLoginTime, user.Nickname, user.ErpId, user.Username, user.Gender, user.UserLevelId, user.RegisterIp, user.LastLoginIp)
			if err != nil {
				logx.Error(err)
				return err
			}
			err = session.QueryRowPartial(&user, fmt.Sprintf("select %s from i2bill_user where `weixin_openid` = ? limit 1", model.UserRows), userInfo.OpenID)
			if err != nil {
				logx.Error(err)
				return err
			}
		}

		rtnInfo.UserInfo.ID = user.Id
		rtnInfo.UserInfo.UserName = user.Username
		rtnInfo.UserInfo.NickName = user.Nickname
		rtnInfo.UserInfo.Mobile = user.Mobile
		rtnInfo.UserInfo.Gender = user.Gender
		rtnInfo.UserInfo.Avatar = user.Avatar
		rtnInfo.UserInfo.Birthday = user.Birthday

		user.LastLoginIp = ""
		user.LastLoginTime = utils.GetTimestamp()
		//_, err = session.Exec(`update i2bill_user set
		//mobile = ?, avatar = ?, weixin_openid = ?, password = ?, birthday = ?, register_time = ?, last_login_time = ?,
		//nickname = ?, erp_id = ?, username = ?, gender = ?, user_level_id = ?, register_ip = ?, last_login_ip = ? where id = ?`, user.Mobile, user.Avatar, user.WeixinOpenid, user.Password, user.Birthday, user.RegisterTime, user.LastLoginTime, user.Nickname, user.ErpId, user.Username, user.Gender, user.UserLevelId, user.RegisterIp, user.LastLoginIp, user.Id)
		//if err != nil {
		//	logx.Error(err)
		//	return err
		//}
		return nil
	})
	if err != nil {
		return nil, err
	}
	var accessExpire = l.svcCtx.Config.JwtAuth.AccessExpire
	now := time.Now().Unix()
	payloads := map[string]interface{}{
		"userId":     rtnInfo.UserInfo.ID,
		"sessionKey": userInfo.SessionKey,
	}
	accessToken, err := l.CreateJWT(now, l.svcCtx.Config.JwtAuth.AccessSecret, payloads, accessExpire)
	if err != nil {
		logx.Error(err)
		return nil, err
	}
	//关联兼职
	partUser, err := model.GetPartTimeXormByUserId(rtnInfo.UserInfo.ID, l.svcCtx.DB)
	if err != nil {
		logx.Error(err)
		return nil, err
	}
	if rtnInfo.UserInfo.Mobile != "" && (partUser == nil || partUser.UserId == 0) {
		partTime := new(model.I2billMktPartTimeXorm)
		_, err = l.svcCtx.DB.Where("ipone_number = ? and del_flag = 0 and user_id = 0", rtnInfo.UserInfo.Mobile).Get(partTime)
		if err != nil {
			logx.Error(err)
			return nil, err
		}
		partTime.UserId = rtnInfo.UserInfo.ID
		if partTime.Id != 0 {
			_, err := l.svcCtx.DB.ID(partTime.Id).Update(partTime)
			if err != nil {
				logx.Error(err)
				return nil, err
			}
		}
	}

	rtnInfo.Token = accessToken
	return &rtnInfo, nil
}

func (l *LoginByWeixinLogic) CreateJWT(iat int64, secretKey string, payloads map[string]interface{}, seconds int64) (string, error) {
	claims := make(jwt.MapClaims)
	claims["exp"] = iat + seconds
	claims["iat"] = iat
	for k, v := range payloads {
		claims[k] = v
	}
	token := jwt.New(jwt.SigningMethodHS256)
	token.Claims = claims
	return token.SignedString([]byte(secretKey))
}

func (l *LoginByWeixinLogic) Login(code string) (*types.WXUserInfo, error) {
	secret := l.svcCtx.Config.Weixin.Secret
	appid := l.svcCtx.Config.Weixin.Appid
	req := utils.Get("https://api.weixin.qq.com/sns/jscode2session")
	req.Param("grant_type", "authorization_code")
	req.Param("js_code", code)
	req.Param("secret", secret)
	req.Param("appid", appid)

	var res types.WXLoginResponse
	req.ToJSON(&res)
	if res.ErrCode != 0 {
		return nil, errors.New(res.ErrMsg)
	}
	userinfo, err := l.DecryptUserInfoData(res.SessionKey)
	userinfo.OpenID = res.OpenID
	userinfo.SessionKey = res.SessionKey
	return userinfo, err
}

func (l *LoginByWeixinLogic) DecryptUserInfoData(sessionKey string) (*types.WXUserInfo, error) {
	var wxuserinfo types.WXUserInfo
	return &wxuserinfo, nil
}
