package controllers

import (
	"WorldEpcho/src/config"
	database "WorldEpcho/src/datasource"
	"WorldEpcho/src/models"
	"WorldEpcho/src/service"
	"WorldEpcho/src/utils"
	"WorldEpcho/src/utils/message"
	"fmt"
	unisms "github.com/apistd/uni-go-sdk/sms"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"

	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

//ImgSlider图片滑块
type ImgSlider struct {
	Msg        string `json:"message,omitempty"`
	ActivityId int64  `json:"code,omitempty"`
	Uuid       string `json:"uuid,omitempty"`
	Mask       string `json:"mask,omitempty"`
	Bg         string `json:"bg,omitempty"`
	ImageRandY int    `json:"imageRandY,omitempty"`
}

func LoginGet(c *gin.Context) {
	//返回html
	c.HTML(http.StatusOK, "login.html", gin.H{"title": "登录页"})
}

//用户登录控制器
func LoginPost(c *gin.Context) {
	//获取表单信息
	username := c.PostForm("username")
	password := c.PostForm("password")

	fmt.Println("username:", username, ",password:", password)
	/*
	   调用工具类的MD5加盐的函数
	*/
	salt := config.Conf.Md5Salt

	md5String, err := utils.MD5BySalf(password, salt)
	if err != nil {
		log.Println("密码加盐失败！")
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "密码加盐失败"})
	}
	//根据用户名和密码从数据库中查询
	Userid, level, status, tags, resource, err := models.QueryUserWithParam2(username, md5String)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户名或密码错误"})
		return
	}
	fmt.Println("用户", username, "加密后用户密码：", md5String)
	fmt.Println("user id ==> :", Userid, "level =>", level)
	if status == -1 {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "该用户已经注销!"})
		return
	}

	if Userid > 0 {

		/*	if level != 0 {
			//fmt.Println(" ================= 检查用户的激活码是否过期 ================== ")
			// 查询用户的激活码是否过期
			exist, Active_code, err := models.GetActivationCodeByUserID(Userid)
			if err != nil {
				fmt.Println(" == 根据使用者id查询激活码出错 ==> err:", err.Error())
				c.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据使用者id查询激活码出错"})
				return
			}
			if exist {
				fmt.Println(config.ColorCyan, "当前时间戳为: ", time.Now().Unix(), "激活码过期时间:", Active_code.ExpireTime, config.ColorReset)
			}
			//如果存在激活码，判断激活码是否过期
			if exist && Active_code.ExpireTime < time.Now().Unix() {
				//如果激活码的过期时间大于当前的时间戳，表示激活码过期，则直接返回，不允许该用户登录

				response := gin.H{
					"code":    0,
					"message": "用户激活码已经过期，无法登录，请联系你的代理商! ",
				}
				c.JSON(http.StatusOK, response)
				// 序列化响应数据为 JSON 字符串
				jsonData, err := json.Marshal(response)
				if err != nil {
					// 处理序列化错误
					fmt.Println("序列化 JSON 数据出错:", err)
					return
				}
				fmt.Println(config.ColorPurple, string(jsonData), config.ColorReset)

				return
			}
		}*/

		/*
			设置了session后会将数据处理设置到cookie，然后再浏览器进行网络请求的时候会自动带上cookie
			因为我们可以通过获取这个cookie来判断用户是谁，这里我们使用的是session的方式进行设置。
		*/
		session := sessions.Default(c)

		//在这里修改,3表示有效时间为1800秒
		session.Options(sessions.Options{MaxAge: 3600 * 3})
		// 设置用户ID到会话中
		session.Set("WorldUserID", utils.Strval(Userid))
		//参考
		//session.Set("user-id", "unique-user-id") // 设置用户ID到会话中
		err2 := session.Save()
		if err2 != nil {
			log.Println("登录完成后,设置Session失败！")
			c.JSON(http.StatusOK, gin.H{"code": 0, "message": "登录设置Session失败！"})
		}

		var userId = session.Get("WorldUserID").(string)

		fmt.Println("登录Session的WorldUserID: ", userId)
		//设置登录的缓存
		config.WorldLoginCacheCode.Set(userId, true, 3*time.Hour)

		uid_base64 := utils.Base64Encode(userId)

		fmt.Println("userId的base64编码后的字符串为：", uid_base64)
		if resource == "" {

		}
		c.JSON(http.StatusOK,
			gin.H{
				"code":    1,
				"message": "登录成功",
				"userid":  uid_base64,
				"tags":    tags,
			})
	} else {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "登录失败，用户名或者密码错误"})
	}

}

// 获取短信验证码
func GetCode(ctx *gin.Context) bool {
	/*
		   phone := utils.Strval(ctx.PostForm("phone"))
			flag := utils.Strval(ctx.PostForm("flag"))
	*/

	phone := ctx.PostForm("phone")
	flag := ctx.PostForm("flag")

	fmt.Println("phone: ", phone, "flag: ", flag)

	if !utils.Check_phone(phone) {
		ctx.JSON(201, gin.H{"code": 0, "message": "非法手机号"})
		return false
	}

	sms_code, bol := config.WorldLoginCacheCode.Get(phone)
	fmt.Println(sms_code, bol)

	if bol {
		ctx.JSON(201, gin.H{"code": 0, "message": "请勿重复发送验证码"})
		return false
	}
	err := PhoneGetCode(phone, flag)
	if err != nil {
		log.Println(err.Error())
		ctx.JSON(201, gin.H{"code": 0, "message": "验证码发送失败，请稍后重试"})
		return false
	}
	return true
}

//生成短信验证码
func PhoneGetCode(phone string, flag string) (err error) {
	var accessKeyId = "QWdd7iGKrwf6BctPcCUMfToLjFG7LCKtWNBLfEyTeVZW9mqcB"
	var accessKeySecret = "Y9c1ZKeEu2NbSwLYNvcAZDabAbPB9E"
	//accessKeyId := config.Conf.SmsAccessKeyId
	//accessKeySecret := config.Conf.SmsAccessKeySecret

	// 初始化
	//通过 unisms.NewClient 方法初始化了一个通信服务的客户端 client，并传入了 accessKeyId 和 accessKeySecret。
	client := unisms.NewClient(accessKeyId, accessKeySecret) // 若使用简易验签模式仅传入第一个参数即可
	// 构建信息
	uniMessage := unisms.BuildMessage()
	uniMessage.SetTo(phone)        // 接收人手机号
	uniMessage.SetSignature("觉卿者") // 短信签名

	switch flag {
	case "1": // 注册
		uniMessage.SetTemplateId("pub_verif_register_ttl") // 短信模板
	case "2": // 找回密码
		uniMessage.SetTemplateId("pub_verif_forgetpass_ttl") // 短信模板
	default: // 默认模板
		uniMessage.SetTemplateId("pub_verif_ttl3") // 短信模板
	}
	//生成验证码code，并将其转换为字符串类型，然后将验证码和有效期传入短信模板中。
	code := strconv.Itoa(utils.GetAuthCode())
	uniMessage.SetTemplateData(map[string]string{"code": code, "ttl": "3"}) // 设置自定义参数 (变量短信)

	// 发送短信
	//调用 client.Send(uniMessage) 方法发送短信，并将返回结果存储在 _ 中，同时检查是否有错误发生，若有则打印错误并返回。
	_, err = client.Send(uniMessage)
	if err != nil {
		fmt.Println(err)
		return err
	}
	fmt.Println(code)

	//注册短信验证码缓存到内存
	config.WorldLoginCacheCode.Set(phone, code, time.Minute*2)
	return err
}

//图形验证码
func CreateImg(ctx *gin.Context) {
	maskBase64, bgBase64, x, y := utils.CreateCode()
	//生成一个UUID
	newUUID, _ := uuid.NewUUID()
	UUID := newUUID.String()
	x = x - 40
	y = y - 40
	imgSlider := ImgSlider{
		ActivityId: message.SuccessGeneratedImg,
		Msg:        message.GetMsg(message.SuccessGeneratedImg),
		Uuid:       UUID,
		Mask:       maskBase64,
		Bg:         bgBase64,
		ImageRandY: y,
	}

	fmt.Println("x------------------", x)
	config.WorldLoginCacheCode.Set(UUID, x, time.Minute*5)

	ctx.JSON(http.StatusOK, imgSlider)

	//return
}

//图形验证码校验
func CheckImg(c *gin.Context) {
	//uuid := c.GetString("uuid")
	uuid := c.PostForm("uuid")
	x, _ := strconv.Atoi(c.PostForm("imageRandX"))

	getX, b := config.WorldLoginCacheCode.Get(uuid)
	if !b {
		c.JSON(http.StatusOK, gin.H{
			"code":    message.REQUEST_FAULT,
			"message": "无效验证",
		})
		fmt.Println("获取不到uuid")
		return
	}
	intCode, _ := strconv.Atoi(utils.Strval(getX))
	fCode := float64(intCode)
	f := float64(x)
	max := f + f*0.05
	min := f - f*0.05
	if fCode >= min && fCode <= max {
		config.WorldLoginCacheCode.Delete(uuid)
		//图片验证通过调用发送短信验证码的接口
		bol := GetCode(c)

		if bol {
			c.JSON(http.StatusOK, gin.H{
				"code":    message.REQUEST_SUCCESS,
				"message": "验证成功",
			})
		}
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"code":    message.REQUEST_FAULT,
		"message": "验证失败",
	})
	//return
}

// 刷新session会话
func RefreshSession(c *gin.Context) error {
	session := sessions.Default(c)

	// 重设 MaxAge 为 3600 秒，刷新会话过期时间
	session.Options(sessions.Options{MaxAge: 3600 * 3})

	// 保存会话的更改
	if err := session.Save(); err != nil {
		// 使用 Gin 的错误处理
		c.Error(err)
		log.Printf("Error saving session: %v", err) // 保留日志记录
		return err
	}

	return nil
}

// 心跳
func Heartbeat(c *gin.Context) {

	// 记录调用时间为“现在”
	//now := time.Now()
	// 获取会话信息
	session := sessions.Default(c)
	userId, exists := session.Get("WorldUserID").(string)

	if !exists || userId == "" {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "未授权访问"})
		return
	}

	// 从缓存中获取登录状态信息，设置登录缓存
	_, found := config.WorldLoginCacheCode.Get(userId)
	if found {
		// 存在登录状态信息，更新其过期时间为新的时间点
		config.WorldLoginCacheCode.Set(userId, true, 3*time.Hour) // 更新为30分钟后的过期时间
	} else {
		// 不存在登录状态信息，需要重新登录
		c.JSON(401, gin.H{
			"code":    0,
			"message": "登录状态已经过期，请重新登录",
		})
		return
	}
	//设置登录的缓存
	//config.LoginCacheCode.Set(userId, true, 30*time.Minute)
	// 刷新会话
	if err := RefreshSession(c); err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "刷新session失败"})
		return
	}
	// 计算和打印预期的session过期时间
	//newExpiryTime := now.Add(time.Second * 1800) // 使用与RefreshSession中相同的MaxAge
	//log.Printf("Session for user %s refreshed, expected expiry time: %v", userId, newExpiryTime.Format("2006-01-02 15:04:05"))
	// 响应消息体
	response := gin.H{
		"code":    1,
		"message": "session刷新成功",
		"userId":  userId,
	}

	/*
	     // 序列化响应数据为 JSON 字符串
	   	jsonData, err := json.Marshal(response)
	   	if err != nil {
	   		// 处理序列化错误
	   		fmt.Println("序列化 JSON 数据出错:", err)
	   		return
	   	}
	   	fmt.Println(config.ColorGreen, string(jsonData), config.ColorReset)
	*/
	// 返回 200 OK
	c.JSON(http.StatusOK, response)
	//c.JSON(http.StatusOK, gin.H{"code": 1, "message": "session刷新成功", "userId": userId})
}

//刷新session和Jwt token的心跳
func Heartbeat2(c *gin.Context) {
	// 从请求头 Authorization 中提取令牌
	tokenString := c.GetHeader("Authorization")
	if tokenString != "" {
		// 解析并验证 JWT 令牌
		token, isValid, err := service.ParseMIGUToken(tokenString)
		if err != nil || !isValid {
			c.JSON(http.StatusUnauthorized, gin.H{"code": 0, "message": "无效或已过期的令牌"})
			return
		}

		// 打印当前令牌的过期时间
		if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
			oldExp, ok := claims["exp"].(float64)
			if ok {
				fmt.Printf("当前令牌过期时间: %s\n", time.Unix(int64(oldExp), 0).Format("2006-01-02 15:04:05"))
			}

			userID, _ := claims["user_id"].(float64)
			level, _ := claims["level"].(int)

			// 生成新的令牌，并延长有效期
			newToken, newExp, err := GenerateLoginJWTToken2(int64(userID), level)
			if err != nil {
				c.JSON(http.StatusOK, gin.H{"code": 0, "message": "令牌续期失败"})
				return
			}

			// 打印新令牌的过期时间
			fmt.Printf("新令牌过期时间: %s\n", time.Unix(newExp, 0).Format("2006-01-02 15:04:05"))

			// 返回新的令牌和成功消息
			c.JSON(http.StatusOK, gin.H{
				"code":    1,
				"message": "令牌已成功续期",
				"token":   newToken,
			})
			return
		}
	}

	// 如果未发现令牌，则继续执行现有的会话检查
	session := sessions.Default(c)
	userId, exists := session.Get("WorldUserID").(string)
	if !exists || userId == "" {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "未授权访问"})
		return
	}

	// 从缓存中检查登录状态，并设置过期时间
	_, found := config.WorldLoginCacheCode.Get(userId)
	if found {
		config.WorldLoginCacheCode.Set(userId, true, 3*time.Hour)
	} else {
		c.JSON(http.StatusUnauthorized, gin.H{
			"code":    0,
			"message": "登录状态已过期，请重新登录",
		})
		return
	}

	if err := RefreshSession(c); err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "会话刷新失败"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"code":    1,
		"message": "会话已成功刷新",
		"userId":  userId,
	})
}

// generateToken 生成JWT令牌并返回新的过期时间
func GenerateLoginJWTToken2(userId int64, level int) (string, int64, error) {
	jwtSecret := config.Conf.JwtSecret
	expTime := time.Now().Add(3 * time.Hour).Unix()

	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"user_id": userId,
		"level":   level,
		"exp":     expTime,
	})
	token, err := tokenClaims.SignedString([]byte(jwtSecret))
	return token, expTime, err
}

/*
  更新用户信息的控制器
*/
func UpdateUserInfoPost(ctx *gin.Context) {

	//println("session userId: ", utils.GetSessionUserId(ctx))

	/*
	   判断用户是否登录
	*/
	id, isLogin := service.IsUserLoggedIn(ctx)
	if !isLogin {
		log.Println("用户未登录")
		ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户未登录"})
		return
	}
	/*
		    idStr := ctx.PostForm("id")
			id, err := strconv.ParseInt(idStr, 10, 64)
			if err != nil {
				ctx.JSON(400, gin.H{"error": "Invalid user Id"})
				return
			}
	*/
	//从前端获取需要修改的用户信息
	var user = models.User{
		Id:       id,
		Username: ctx.PostForm("username"),
		Phone:    ctx.PostForm("phone"),
	}

	userInfo, err1 := models.GetUserByID(id)
	if err1 != nil {
		ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "更新用户信息时时获取用户信息失败!"})
		return
	}
	userInfo.Username = user.Username
	userInfo.Phone = user.Phone

	fmt.Println("需要修改用户信息: ", user.Username, user.Phone)

	//需要对用户密码进行加密处理
	//fmt.Println("md5后：", utils.MD5(user.Password))
	//var pwd = utils.MD5(user.Password)
	//user.Password = pwd

	err2 := models.ModifyUserInfo(id, userInfo)
	if err2 != nil {
		ctx.JSON(200, gin.H{"code": 0, "message": "更新用户信息出错"})
		return
	}

	ctx.JSON(200, gin.H{"code": 1, "message": "更新用户信息成功"})

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

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

}

//根据用户id查询用户
func SearchUserGetById(ctx *gin.Context) {
	/*
	   判断用户是否登录
	*/
	id, isLogin := service.IsUserLoggedIn(ctx)
	if !isLogin {
		log.Println("用户未登录")
		ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户未登录"})
		return
	}
	userInfo, err1 := models.GetUserByID(id)
	userInfo.Password = ""
	if err1 != nil {
		ctx.JSON(http.StatusOK, gin.H{"code": 0, "message": "更新用户信息时时获取用户信息失败!"})
		return
	} else {
		ctx.JSON(http.StatusOK, gin.H{"code": 1, "message": userInfo})
	}
}

//重置用户密码
func ResetUserPassword(c *gin.Context) {
	// 获取表单信息
	phone := c.PostForm("phone")
	/*
	 通过手机号查询该手机号是否注册用户了
	*/
	//开启事务
	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
	}
	user, _, err := models.GetUserByPhone_Tx(session, phone)
	if err != nil {
		log.Println("修改密码时查询用户信息失败")
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": err.Error()})
		return
	}
	if user == nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户不存在"})
		return
	}
	// 从前端获取用户的密码和验证码信息
	password := c.PostForm("password")
	smsCode := c.PostForm("sms_code")
	//密码校验
	validatePassword := utils.ValidatePassword(password)
	if !validatePassword {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "密码无效，请输入至少8位不超过20位，包含数字和字母的组合"})
		return
	}
	// 验证手机验证码
	correctSmsCode, ok := config.WorldLoginCacheCode.Get(phone)
	if !ok || correctSmsCode.(string) != smsCode {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "验证码错误或已失效"})
		return
	}
	// 更新密码
	/*
	   调用工具类的MD5加盐的函数
	*/
	salt := config.Conf.Md5Salt

	md5String, err := utils.MD5BySalf(password, salt)
	if err != nil {
		log.Println("密码加盐失败！")
	}

	fmt.Println("密码加盐后: ", md5String)
	err = models.UpdatePasswordByPhone(session, phone, md5String)
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "密码重置失败! "})
		log.Println(err.Error())
		return
	}

	//提交事务
	err = session.Commit()
	if err != nil {
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "根据手机号重置密码提交事务失败"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"code": 1, "message": "手机号为" + phone + "的用户密码重置成功! "})
	fmt.Printf("手机号为%s的用户，密码重置成功!", phone)
}

//获取jwt的token
func GetJwtToken(c *gin.Context) {
	if sessions.Default(c).Get("UserID") == nil {
		c.String(401, "非法用户")
		return
	}
	session_userid := sessions.Default(c).Get("UserID").(string)
	fmt.Println("session_userid: ", session_userid)
	existUser, bol := config.WorldLoginCacheCode.Get(session_userid)

	if bol && existUser.(bool) {
		//生成jwt字符串
		//token, err := controllers.CreateAccessToken(accessKey, accessSecret)
		//tokenString, err := GenerateToken()
		tokenString, err := GenerateToken()
		fmt.Println("Jwt token: ", tokenString)
		if err != nil {
			fmt.Println("Token generation failed:", err)
			return
		}
		split := strings.Split(tokenString, "@")
		token, err := ParseToken(split[1])
		if err != nil {
			fmt.Println(err.Error())
		}
		fmt.Println("token: ", token)
		c.String(http.StatusOK, tokenString)
	} else {
		c.String(401, "非法用户")
	}

}

// SimulateLogin 实现了模拟登录的处理逻辑
func SimulateLogin(c *gin.Context) {

	session := sessions.Default(c)
	session.Options(sessions.Options{MaxAge: 3600 * 3}) // 设置Session的有效时间为1800秒
	Rest_UUID := c.PostForm("uuid")

	if service.AppCache[Rest_UUID] != nil {
		QuickUid := service.AppCache[Rest_UUID].UserId
		Quick_Userid, err := strconv.ParseInt(utils.Strval(QuickUid), 10, 64)
		if err != nil {

		}
		// 设置用户ID到会话中
		session.Set("UserID", utils.Strval(Quick_Userid))
		if err := session.Save(); err != nil {
			// 如果保存Session失败，处理错误
			c.JSON(http.StatusInternalServerError, gin.H{"code": 0, "message": "模拟登录失败"})
			return
		}

		// 在控制台输出UserId，仅供调试使用
		userId, _ := session.Get("UserID").(string) // 这里添加了错误检查，防止断言失败
		fmt.Println(config.ColorPurple, "从session中获取userId:", userId, config.ColorReset)

		// 设置登录缓存（根据你的具体逻辑）
		config.WorldLoginCacheCode.Set(userId, true, time.Hour)
		// 返回响应
		c.JSON(http.StatusOK, gin.H{"code": 1, "message": "模拟登录成功!"})
	}

}

// 修改用户密码
func UpdateUserAccount(c *gin.Context) {
	/*
	   判断用户是否登录
	*/
	_, isLogin := service.IsUserLoggedIn(c)
	if !isLogin {
		log.Println("用户未登录")
		c.JSON(http.StatusOK, gin.H{"code": 0, "message": "用户未登录"})
		return
	}

}
