package controllers

import (
	"WorldEpcho/src/config"
	database "WorldEpcho/src/datasource"
	"WorldEpcho/src/models"
	"WorldEpcho/src/service"
	"WorldEpcho/src/utils"
	"bytes" // 引入bytes包用于处理字节切片
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"io"
	"log"
	"net/http"
	"time"
)

var (
	WxUserAppCache = make(map[string]*models.WxUser)
	//code状态
	initial = "1" //初始
	//scaned    = "2" //已扫描
	confirmed = "2" //已确认
	expired   = "3" //已过期
)

// WXLoginResp 定义微信接口code2Session返回值的数据结构
type WXLoginResp struct {
	OpenId     string `json:"openid"`      // 用户唯一标识
	SessionKey string `json:"session_key"` // 会话密钥
	UnionId    string `json:"unionid"`     // 用户在开放平台的唯一标识符
	ErrCode    int    `json:"errcode"`     // 错误码
	ErrMsg     string `json:"errmsg"`      // 错误信息
}

// WXLogin 用一次性的登录凭证code作为输入, 返回调用微信接口得到的WXLoginResp对象指针和可能的错误信息
func GetOpenIdAndSessionKey(code string) (*WXLoginResp, error) {
	// 微信小程序appId
	wx_appId := config.Conf.WechatAppId
	// 微信小程序appSecret
	wx_appSecret := config.Conf.WechatAppSecret
	url := config.Conf.WechatUrl
	var wxResp WXLoginResp
	// 格式化URL
	formattedURL := fmt.Sprintf(url, wx_appId, wx_appSecret, code)

	// 初始化HTTP客户端
	client := &http.Client{Timeout: 5 * time.Second}
	response, err := client.Get(formattedURL)
	if err != nil {
		log.Printf("error sending GET request, url: %s, %q", formattedURL, err)
		return nil, errors.New("请求微信服务器失败")
	}
	defer response.Body.Close()

	// 检查HTTP响应状态码
	if response.StatusCode != 200 {
		log.Printf("获取sessionKey失败,HTTP CODE:%d", response.StatusCode)
		return nil, errors.New("获取sessionKey失败")
	}
	// 读取响应体
	var responseBody bytes.Buffer
	_, err = io.Copy(&responseBody, response.Body)
	if err != nil {
		log.Printf("error reading response body, %q", err)
		return nil, errors.New("读取响应体失败")
	}

	// 解析响应体
	err = json.Unmarshal(responseBody.Bytes(), &wxResp)
	if err != nil { // 如果解析JSON失败
		log.Println("json解析失败", err)
		return nil, errors.New("json解析失败")
	}

	return &wxResp, nil // 返回结果
}

// 微信小程序登录
func AppletWeChatRegisterOrLogin(c *gin.Context) {
	//获取表单信息
	username := c.PostForm("username")
	code := c.PostForm("code") //  获取code
	if code == "" {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "wxCode不能为空"})
		return
	}
	fmt.Println(config.ColorYellow, "参数: username ==> ", username, "参数: code ==> ", code, config.ColorReset)
	//开启事务
	session := database.Engine.NewSession()
	defer session.Close() //延迟关闭session
	err := session.Begin()
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "开启事务失败!"})
		fmt.Println("开启事务失败")
		return
	}

	// 根据code获取 openID 和 session_key
	wxLoginResp, err := GetOpenIdAndSessionKey(code)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"code": 0, "message": "登录失败"})
		return
	}
	OpenId := wxLoginResp.OpenId
	// 判断OpenId 是否为空，如果为空，抛出错误
	if OpenId == "" {
		c.JSON(http.StatusBadRequest, gin.H{"code": 0, "message": "获取微信用户的openid为空"})
		fmt.Println("获取微信用户的openid为空")
		return
	}
	// 拿到微信用户的openId 存缓存
	if OpenId != "" {
		WxUserAppCache[OpenId] = &models.WxUser{
			WxCode:       code,
			WxCodeStatus: initial,
			OpenId:       OpenId,
			SessionKey:   wxLoginResp.SessionKey,
		}
		nowTime := time.Now().Unix()
		ExpiresIn := 300
		wxCodeExpiresTime := time.Now().Add(time.Second * time.Duration(ExpiresIn)).Unix()
		if nowTime > wxCodeExpiresTime {
			log.Printf("微信code过期")
			c.JSON(http.StatusBadRequest, gin.H{"code": 0, "message": "微信登录code令牌过期"})
			WxUserAppCache[OpenId].WxCodeStatus = expired
			return
		}
	}

	fmt.Println(" OpenId ===> ", OpenId)

	/*
	  使用微信用户的openid注册账号，注册之前先查询该用户是否注册过
	*/
	WxUser_openId, exist, err := models.GetWxUserByOpenId(session, OpenId)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据微信手机号查询用户是否存在出错!"})
		fmt.Println("根据微信手机号查询用户是否存在出错")
		return
	}

	// 根据openId查询用户是否存在，如果不存在则注册并模拟登录
	if !exist {
		/*
		   调用工具类的MD5加盐的函数
		*/
		pwd := "88888888"
		salt := config.Conf.Md5Salt
		md5String, err := utils.MD5BySalf(pwd, salt)
		if err != nil {
			log.Println("密码加盐失败！")
		}
		fmt.Println("用户密码加盐后：", md5String)

		NewUser := models.User{
			Username:   OpenId,
			Phone:      OpenId,
			Password:   md5String,
			Status:     0,
			Createtime: time.Now().Unix(),
			Level:      9,
			WxOpenid:   OpenId,
		}
		//添加用户
		rows, userId, err := models.AddUser(session, &NewUser)
		if err != nil {
			c.JSON(http.StatusOK, gin.H{"code": 0, "message": "注册失败"})
			log.Println(err.Error())
			return
		}
		if rows <= 0 {
			c.JSON(http.StatusOK, gin.H{"code": 0, "message": "注册失败"})
			fmt.Println("用户注册失败！")
			return

		} else {
			/*
				获取用户头像
			*/
			file, err := c.FormFile("avatar")
			if err != nil {
				fmt.Println("用户头像从前端获取失败:", err)
				log.Println(err.Error())
				return
			}
			// file保存到本地
			fileName := "./static/user/" + "avatar" + utils.Strval(userId) + ".png"

			err = c.SaveUploadedFile(file, fileName)
			if err != nil {
				c.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户头像添加失败"})
				return
			}

			/*
			   根据openid查询用户详情信息表中是否保存了用户详情信息
			*/
			_, userInfo_exist, err := models.GetUserInfoByPhone(session, OpenId)
			if err != nil {
				fmt.Println("根据手机号查询用户是否存在，查询失败")
				c.JSON(http.StatusInternalServerError, gin.H{"code": 0, "message": "根据手机查询用户详情信息是否存在出错"})
				return
			}
			//创建用户详情信息
			if username == "" {
				username = "wx_" + "微信用户" + "_" + utils.Strval(time.Now().Unix())
			}
			userinfo := &models.UserInfo{
				UserName:  username,
				Phone:     OpenId,
				CreatedAt: time.Now().Unix(),
				UpdatedAt: time.Now().Unix(),
			}
			userinfo.UserId = userId
			if !userInfo_exist {
				//用户详情信息表操作
				err = models.FindOrCreateOrUpdateUserInfoByPhone(session, userinfo)
				if err != nil {
					fmt.Printf("向用户信息表添加用户数据失败")
					c.JSON(http.StatusInternalServerError, gin.H{"code": 0, "message": "向用户详情信息表添加用户数据失败"})
					return
				}
			}
			/*
			  模拟用户登录,用户免密登录
			*/
			session := sessions.Default(c)
			//在这里修改,3表示有效时间为1800秒
			session.Options(sessions.Options{MaxAge: 1800})
			// 设置用户ID到会话中
			session.Set("UserID", utils.Strval(userId))
			if err := session.Save(); err != nil {
				// 如果保存Session失败，处理错误
				c.JSON(http.StatusInternalServerError, gin.H{"code": 0, "message": "模拟登录失败"})
				return
			}
			var userId = session.Get("UserID").(string)
			fmt.Println(config.ColorPurple, "从session中获取userId: ", userId, config.ColorReset)
			config.WorldLoginCacheCode.Set(userId, true, time.Hour)
			// 微信用户注册成功，模拟登录成功保存缓存
			if WxUserAppCache[OpenId] != nil {
				WxUserAppCache[OpenId].UserID = utils.Strval(userId)
				WxUserAppCache[OpenId].WxCodeStatus = confirmed
			}
			c.JSON(http.StatusOK, gin.H{
				"code":    1,
				"wxUid":   userId,
				"openId":  OpenId,
				"message": "微信用户注册,模拟登录成功!",
			})
		}
		//提交事务
		err = session.Commit()
		if err != nil {
			c.JSON(http.StatusOK, gin.H{"code": 0, "message": "微信用户模拟登录事务提交失败"})
			return
		}

	} else {

		// 微信用户已经注册过
		fmt.Println(config.ColorBlue, "绑定openId的用户已经存在，该用户id为: ", WxUser_openId.Id, config.ColorReset)
		/*
		  模拟用户登录,用户免密登录
		*/
		//用户已经存在，模拟用户登录
		session := sessions.Default(c)
		//在这里修改,3表示有效时间为1800秒
		session.Options(sessions.Options{MaxAge: 1800})
		// 设置用户ID到会话中
		session.Set("UserID", utils.Strval(WxUser_openId.Id))
		if err := session.Save(); err != nil {
			// 如果保存Session失败，处理错误
			c.JSON(http.StatusInternalServerError, gin.H{"code": 0, "message": "模拟登录失败"})
			return
		}
		var userId = session.Get("UserID").(string)
		fmt.Println(config.ColorPurple, "从session中获取userId: ", userId, config.ColorReset)
		config.WorldLoginCacheCode.Set(userId, true, time.Hour)
		// 微信用户注册成功，模拟登录成功保存缓存
		if WxUserAppCache[OpenId] != nil {
			WxUserAppCache[OpenId].UserID = utils.Strval(userId)
			WxUserAppCache[OpenId].WxCodeStatus = confirmed
		}
		response := gin.H{
			"code":    1,
			"wxUid":   userId,
			"openId":  OpenId,
			"message": "微信用户注册,模拟登录成功!",
		}
		// 序列化响应数据为 JSON 字符串
		jsonData, err := json.Marshal(response)
		if err != nil {
			// 处理序列化错误
			fmt.Println("序列化 JSON 数据出错:", err)
			return
		}
		fmt.Println(config.ColorPurple, " response: ===> ", string(jsonData), config.ColorReset)
		c.JSON(http.StatusOK, response)
	}
	//提交事务
	err = session.Commit()
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "微信用户模拟登录事务提交失败"})
		return
	}

}

/*
   校验用户是否已经注册，是否已经绑定手机号
*/
func CheckWxUserIsRegisterAndBindPhone(c *gin.Context) {

	code := c.PostForm("code") //  获取code
	if code == "" {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "wxCode不能为空"})
		return
	}

	//开启事务
	session := database.Engine.NewSession()
	defer session.Close() //延迟关闭session
	err := session.Begin()
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "开启事务失败!"})
		fmt.Println("开启事务失败")
		return
	}
	// 根据code获取 openID 和 session_key
	wxLoginResp, err := GetOpenIdAndSessionKey(code)
	if err != nil {
		//c.JSON(400, util.Fail(err.Error()))
		c.JSON(http.StatusBadRequest, gin.H{"code": 0, "message": "获取微信用户的openid出错!"})
		return
	}
	// 拿到微信用户的openId，判断缓存中是否存在该用户信息，如果不存在保存
	OpenId := wxLoginResp.OpenId
	// 判断OpenId 是否为空，如果为空，抛出错误
	if OpenId == "" {
		c.JSON(http.StatusBadRequest, gin.H{"code": 0, "message": "获取微信用户的openid为空"})
		fmt.Println("获取微信用户的openid为空")
		return
	}
	fmt.Println(config.ColorPurple, "openId: ", OpenId, config.ColorReset)
	if WxUserAppCache[OpenId] == nil {
		// 保存用户信息到缓存中
		WxUserAppCache[OpenId] = &models.WxUser{
			WxCode:       code,
			WxCodeStatus: initial,
			OpenId:       OpenId,
			SessionKey:   wxLoginResp.SessionKey,
		}

		nowTime := time.Now().Unix()
		ExpiresIn := 300
		wxCodeExpiresTime := time.Now().Add(time.Second * time.Duration(ExpiresIn)).Unix()
		if nowTime > wxCodeExpiresTime {
			log.Printf("微信code过期")
			c.JSON(http.StatusBadRequest, gin.H{"code": 0, "message": "微信登录code令牌过期"})
			WxUserAppCache[OpenId].WxCodeStatus = expired
			return
		}

	}

	// 定义一个变量保存用户是否绑定手机号
	var isBindPhone bool

	WxUser_openId, exist, err := models.GetWxUserByOpenId(session, OpenId)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据微信手机号查询用户是否存在出错!"})
		fmt.Println("根据微信手机号查询用户是否存在出错")
		return
	}

	if !exist {
		isBindPhone = false
		c.JSON(http.StatusOK, gin.H{
			"code":        0,
			"message":     "用户未注册!",
			"isBindPhone": isBindPhone,
		})
		fmt.Println("用户未注册")
		return
	} else {

		//判断用户是否已经被注销
		if WxUser_openId.Status == -1 {
			c.JSON(http.StatusOK, gin.H{"code": 0, "message": "该用户已经注销!"})
			return
		}

		// 查询用户详细信息
		userInfo, _, err := models.GetUserInfoById(session, WxUser_openId.Id)
		if err != nil {
			c.JSON(http.StatusOK, gin.H{"code": 0, "message": "查询用户详细信息出错"})
			fmt.Println("查询用户详细信息出错")
			return
		}

		//该微信用户的openid已经注册，模拟登录
		/*
		  模拟用户登录,用户免密登录
		*/
		//用户已经存在，模拟用户登录
		session := sessions.Default(c)
		//在这里修改,3表示有效时间为1800秒
		session.Options(sessions.Options{MaxAge: 1800})
		// 设置用户ID到会话中
		session.Set("UserID", utils.Strval(WxUser_openId.Id))
		if err := session.Save(); err != nil {
			// 如果保存Session失败，处理错误
			c.JSON(http.StatusInternalServerError, gin.H{"code": 0, "message": "模拟登录失败"})
			return
		}
		var userId = session.Get("UserID").(string)
		fmt.Println(config.ColorPurple, "从session中获取userId: ", userId, config.ColorReset)
		config.WorldLoginCacheCode.Set(userId, true, time.Hour)
		// 微信用户注册成功，模拟登录成功保存缓存
		if WxUserAppCache[OpenId] != nil {
			WxUserAppCache[OpenId].UserID = utils.Strval(userId)
			WxUserAppCache[OpenId].WxCodeStatus = confirmed
		}

		if len(WxUser_openId.Phone) > 11 {
			isBindPhone = false
		} else {
			isBindPhone = true
		}

		response := gin.H{
			"code":        1,
			"wxUid":       userId,
			"userName":    userInfo.UserName,
			"isBindPhone": isBindPhone,
			"message":     "微信用户已注册，模拟登录成功!",
		}
		// 序列化响应数据为 JSON 字符串
		jsonData, err := json.Marshal(response)
		if err != nil {
			// 处理序列化错误
			fmt.Println("序列化 JSON 数据出错:", err)
			return
		}
		fmt.Println(config.ColorPurple, " response: ===> ", string(jsonData), config.ColorReset)
		c.JSON(http.StatusOK, response)

	}

}

// 将一个字符串进行MD5加密后返回加密后的字符串
func GetMD5Encode(data string) string {
	h := md5.New()
	h.Write([]byte(data))
	return hex.EncodeToString(h.Sum(nil))
}

// 校验微信返回的用户数据
func ValidateUserInfo(rawData, sessionKey, signature string) bool {
	signature2 := GetSha1(rawData + sessionKey)
	return signature == signature2
}

// SHA-1 加密
func GetSha1(str string) string {
	data := []byte(str)
	has := sha1.Sum(data)
	res := fmt.Sprintf("%x", has) //将[]byte转成16进制
	return res
}

// 获取微信用户手机号
func LoginOrBindWXUserPhone(c *gin.Context) {

	//获取表单信息
	OpenId := c.PostForm("openid")
	phoneCode := c.PostForm("phone_code")
	//rejectBind := c.PostForm("reject_binding")

	fmt.Println(config.ColorPurple, " ==== phoneCode: ==== ", phoneCode)
	//开启事务
	session := database.Engine.NewSession()
	defer session.Close() //延迟关闭session
	err := session.Begin()
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "开启事务失败!"})
		fmt.Println("开启事务失败")
		return
	}
	/*
	  用户拒绝绑定手机号，使用openid注册临时账号
	*/
	WxUser_openId, exist, err := models.GetWxUserByOpenId(session, OpenId)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据微信手机号查询用户是否存在出错!"})
		fmt.Println("根据微信手机号查询用户是否存在出错")
		return
	}
	// 根据openId查询用户是否存在
	if !exist {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户未注册!"})
		return
	}
	// 微信手机code码为空，用户拒绝绑定手机操作

	if phoneCode != "" {
		/*
			微信手机code码不为空，用户允许绑定手机号操作
		*/
		// 调用获取微信用户手机号的方法
		PhoneInfo, err := service.GetWechatUserPhoneNumber(phoneCode)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"code": 0, "message": "微信获取用户手机出错!"})
			return
		}
		fmt.Println(config.ColorYellow, "微信获取用户手机:", PhoneInfo.PurePhoneNumber, config.ColorReset)
		if PhoneInfo.PurePhoneNumber == "" {
			c.JSON(http.StatusBadRequest, gin.H{"code": 0, "message": "微信获取用户手机出错，获取手机号为空!"})
			return
		}
		phone := PhoneInfo.PurePhoneNumber
		// 根据微信手机号查询用户是否存在觉卿梦系统，如果不存在则创建并免密登录，如果存在则直接模拟免密登录
		_, isExist, err := models.GetUserByPhone_Tx(session, phone)
		if err != nil {
			c.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据微信手机号查询用户是否存在出错!"})
			fmt.Println("根据微信手机号查询用户是否存在出错")
			return
		}
		/*
			if isExist {
				c.JSON(http.StatusOK, gin.H{"code": 0, "message": "该手机号已经被注册!"})
				fmt.Println(" 该手机号已经被注册! ")
				return
			}
		*/

		if !isExist {
			// 修改用户手机
			WxUser_openId.Phone = phone
			// 更新数据库中
			_, err = models.UpdateUser_Tx(session, WxUser_openId)
			if err != nil {
				c.JSON(http.StatusOK, gin.H{"code": 0, "message": "绑定微信用户手机信息出错!"})
				fmt.Println("修改微信用户手机信息出错!")
			} else {
				c.JSON(http.StatusOK, gin.H{"code": 1, "message": "绑定微信用户手机信息成功!"})
			}
		}
	}
	//提交事务
	err = session.Commit()
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "绑定微信用户手机事务提交失败"})
		return
	}

}
