package model

import (
	
	// "encoding/pem"
	// "goskeleton/app/global/consts"
	"goskeleton/app/global/variable"
	"goskeleton/app/service/users/token_cache_redis"
	"goskeleton/app/utils/md5_encrypt"
	rsa "goskeleton/app/utils/rsa"
	"time"

	"go.uber.org/zap"
	"fmt"
	// "github.com/google/uuid" // 导入uuid库
)

// 操作数据库喜欢使用gorm自带语法的开发者可以参考 GinSkeleton-Admin 系统相关代码
// Admin 项目地址：https://gitee.com/daitougege/gin-skeleton-admin-backend/
// gorm_v2 提供的语法+ ginskeleton 实践 ：  http://gitee.com/daitougege/gin-skeleton-admin-backend/blob/master/app/model/button_cn_en.go

// 创建 userFactory
// 参数说明： 传递空值，默认使用 配置文件选项：UseDbType（mysql）

func CreateUserFactory(sqlType string) *UsersModel {
	return &UsersModel{BaseModel: BaseModel{DB: UseDbConn(sqlType)}}
}

type UsersModel struct {
	BaseModel
	UserName    string `gorm:"column:user_name" json:"user_name"`
	Pass        string `json:"-"`
	// Phone       string `json:"phone"`
	// RealName    string `gorm:"column:real_name" json:"real_name"`
	Status      int    `json:"status"`
	Token       string `json:"token"`
	// LastLoginIp string `gorm:"column:last_login_ip" json:"last_login_ip"`
}

// 表名
func (u *UsersModel) TableName() string {
	return "tb_users"
}

// 用户注册（写一个最简单的使用账号、密码注册即可）
func (u *UsersModel) Register(userName, pass string) bool {
	// userID:=uuid.New()
	sql := "INSERT  INTO tb_users(user_name,pass) SELECT ?,? FROM DUAL   WHERE NOT EXISTS (SELECT 1  FROM tb_users WHERE  user_name=?)"
	result := u.Exec(sql, userName, pass, userName)
	if result.RowsAffected > 0 {
		return true
	} else {
		variable.ZapLog.Error("注册失败:", zap.Error(result.Error))
		return false
	}
}

// 用户登录,
func (u *UsersModel) Login(userName string, pass string) *UsersModel {
	sql := "select pass,id  from tb_users where  user_name=?  limit 1"
	result := u.Raw(sql, userName).First(u)
	if result.Error == nil {
		// 账号密码验证成功
		if len(u.Pass) > 0 && (u.Pass == md5_encrypt.Base64Md5(pass)) {
			return u
		}
	} else {
		variable.ZapLog.Error("根据账号查询单条记录出错:", zap.Error(result.Error))
	}
	return nil
}

//记录用户登陆（login）生成的token，每次登陆记录一次token
func (u *UsersModel) OauthLoginToken(userId int64, token string, expiresAt int64) bool {
	sql := `
		INSERT   INTO  tb_oauth_access_tokens(fr_user_id,action_name,token,expires_at)
		SELECT  ?,'login',? ,? FROM DUAL    WHERE   NOT   EXISTS(SELECT  1  FROM  tb_oauth_access_tokens a WHERE  a.fr_user_id=?  AND a.action_name='login' AND a.token=?  )
	`
	//注意：token的精确度为秒，如果在一秒之内，一个账号多次调用接口生成的token其实是相同的，这样写入数据库，第二次的影响行数为0，知己实际上操作仍然是有效的。
	//所以这里只判断无错误即可，判断影响行数的话，>=0 都是ok的
	if u.Exec(sql, userId, token, time.Unix(expiresAt, 0).Format(variable.DateFormat), userId, token).Error == nil {
		// 异步缓存用户有效的token到redis
		if variable.ConfigYml.GetInt("Token.IsCacheToRedis") == 1 {
			go u.ValidTokenCacheToRedis(userId)
		}
		return true
	}
	return false
}

//用户刷新token,条件检查: 相关token在过期的时间之内，就符合刷新条件
func (u *UsersModel) OauthRefreshConditionCheck(userId int64, oldToken string) bool {
	// 首先判断旧token在本系统自带的数据库已经存在，才允许继续执行刷新逻辑
	var oldTokenIsExists int
	sql := "SELECT count(*)  as  counts FROM tb_oauth_access_tokens  WHERE fr_user_id =? and token=? and NOW()<DATE_ADD(expires_at,INTERVAL  ? SECOND)"
	if u.Raw(sql, userId, oldToken, variable.ConfigYml.GetInt64("Token.JwtTokenRefreshAllowSec")).First(&oldTokenIsExists).Error == nil && oldTokenIsExists == 1 {
		return true
	}
	return false
}

//用户刷新token
func (u *UsersModel) OauthRefreshToken(userId, expiresAt int64, oldToken, newToken string) bool {
	sql := "UPDATE   tb_oauth_access_tokens   SET  token=? ,expires_at=?,updated_at=NOW(),action_name='refresh'  WHERE   fr_user_id=? AND token=?"
	if u.Exec(sql, newToken, time.Unix(expiresAt, 0).Format(variable.DateFormat),  userId, oldToken).Error == nil {
		// 异步缓存用户有效的token到redis
		if variable.ConfigYml.GetInt("Token.IsCacheToRedis") == 1 {
			go u.ValidTokenCacheToRedis(userId)
		}
		// go u.UpdateUserloginInfo(clientIp, userId)
		return true
	}
	return false
}

// 更新用户登陆次数、最近一次登录ip、最近一次登录时间
// func (u *UsersModel) UpdateUserloginInfo(userId int64) {
// 	sql := "UPDATE  tb_users   SET  login_times=IFNULL(login_times,0)+1,last_login_time=?  WHERE   id=?  "
// 	_ = u.Exec(sql, time.Now().Format(variable.DateFormat), userId)
// }

//当用户更改密码后，所有的token都失效，必须重新登录
func (u *UsersModel) OauthResetToken(userId int, newPass string) bool {
	//如果用户新旧密码一致，直接返回true，不需要处理
	userItem, err := u.ShowOneItem(userId)
	if userItem != nil && err == nil && userItem.Pass == newPass {
		return true
	} else if userItem != nil {

		// 如果用户密码被修改，那么redis中的token值也清除
		if variable.ConfigYml.GetInt("Token.IsCacheToRedis") == 1 {
			go u.DelTokenCacheFromRedis(int64(userId))
		}

		sql := "UPDATE  tb_oauth_access_tokens  SET  revoked=1,updated_at=NOW(),action_name='ResetPass' WHERE  fr_user_id=?  "
		if u.Exec(sql, userId).Error == nil {
			return true
		}
	}
	return false
}

//当tb_users 删除数据，相关的token同步删除
func (u *UsersModel) OauthDestroyToken(userId int) bool {
	//如果用户新旧密码一致，直接返回true，不需要处理
	sql := "DELETE FROM  tb_oauth_access_tokens WHERE  fr_user_id=?  "
	//判断>=0, 有些没有登录过的用户没有相关token，此语句执行影响行数为0，但是仍然是执行成功
	if u.Exec(sql, userId).Error == nil {
		return true
	}
	return false
}

// 判断用户token是否在数据库存在+状态OK
func (u *UsersModel) OauthCheckTokenIsOk(userId int64, token string) bool {
	sql := "SELECT   token  FROM  `tb_oauth_access_tokens`  WHERE   fr_user_id=?  AND  revoked=0  AND  expires_at>NOW() ORDER  BY  expires_at  DESC , updated_at  DESC  LIMIT ?"
	maxOnlineUsers := variable.ConfigYml.GetInt("Token.JwtTokenOnlineUsers")
	rows, err := u.Raw(sql, userId, maxOnlineUsers).Rows()
	defer func() {
		//  凡是查询类记得释放记录集
		_ = rows.Close()
	}()
	if err == nil && rows != nil {
		for rows.Next() {
			var tempToken string
			err := rows.Scan(&tempToken)
			if err == nil {
				if tempToken == token {
					return true
				}
			}
		}
	}
	return false
}

// 禁用一个用户的: 1.tb_users表的 status 设置为 0，tb_oauth_access_tokens 表的所有token删除
// 禁用一个用户的token请求（本质上就是把tb_users表的 status 字段设置为 0 即可）
// func (u *UsersModel) SetTokenInvalid(userId int) bool {
// 	sql := "delete from  `tb_oauth_access_tokens`  where  `fr_user_id`=?  "
// 	if u.Exec(sql, userId).Error == nil {
// 		if u.Exec("update  tb_users  set  status=0 where   id=?", userId).Error == nil {
// 			return true
// 		}
// 	}
// 	return false
// }

//根据用户ID查询一条信息
func (u *UsersModel) ShowOneItem(userId int) (*UsersModel, error) {
	sql := "SELECT  `id`, `user_name`,`pass` FROM  `tb_users`  WHERE `status`=1 and   id=? LIMIT 1"
	result := u.Raw(sql, userId).First(u)
	if result.Error == nil {
		return u, nil
	} else {
		return nil, result.Error
	}
}

// 查询数据之前统计条数
// func (u *UsersModel) counts(userName string) (counts int64) {
// 	sql := "SELECT  count(*) as counts  FROM  tb_users  WHERE status=1 and   user_name like ?"
// 	if res := u.Raw(sql, "%"+userName+"%").First(&counts); res.Error != nil {
// 		variable.ZapLog.Error("UsersModel - counts 查询数据条数出错", zap.Error(res.Error))
// 	}
// 	return counts
// }

// 查询（根据关键词模糊查询）
type baseInfo struct{
	Id        int64  `gorm:"primaryKey" json:"id"`
	UserName    string `gorm:"column:user_name" json:"user_name"`
}
func (u *UsersModel) Info(userName string) (temp baseInfo) {
	sql := "SELECT  `id`, `user_name` FROM  `tb_users`  WHERE user_name= ?"
	if res := u.Raw(sql,userName).Scan(&temp); res.RowsAffected > 0 {
		return temp
	}
	return 
}

//新增
func (u *UsersModel) Store(userName string, pass string, realName string, phone string, remark string) bool {
	sql := "INSERT  INTO tb_users(user_name,pass,real_name,phone,remark) SELECT ?,?,?,?,? FROM DUAL   WHERE NOT EXISTS (SELECT 1  FROM tb_users WHERE  user_name=?)"
	return u.Exec(sql, userName, pass, realName, phone, remark, userName).RowsAffected > 0 
	// return false
}

//UpdateDataCheckUserNameIsUsed 更新前检查新的用户名是否已经存在（避免和别的账号重名）
func (u *UsersModel) UpdateDataCheckUserNameIsUsed(userId int, userName string) (exists int64) {
	sql := "select count(*) as counts from tb_users where  id!=?  AND user_name=?"
	_ = u.Raw(sql, userId, userName).First(&exists)
	return exists
}

//更新
func (u *UsersModel) NameUpdate(id int, userName string) bool {
	sql := "update tb_users set user_name=? WHERE status=1 AND id=?"
	return u.Exec(sql, userName, id).RowsAffected >= 0
}
func (u *UsersModel) UpdatePassword(id int,userName,oldpass,newpass string) *UsersModel {
	sql := "select pass from tb_users where  id=?  limit 1"
	result := u.Raw(sql, id).First(u)
	if result.Error == nil {
		// 账号密码验证成功
		if len(u.Pass) > 0 && (u.Pass == md5_encrypt.Base64Md5(oldpass)) {
			if u.OauthResetToken(id,md5_encrypt.Base64Md5(newpass)){
				variable.ZapLog.Info("密码更改后，token重已置")
			}else{
				variable.ZapLog.Error("密码更改后，token重置失败")
			}
			
			// 更新密码
			sql = "update tb_users set pass=? WHERE status=1 AND id=?"
			if u.Exec(sql,newpass,id).RowsAffected>=0{
				return u
			}
		}
	} else {
		variable.ZapLog.Error("根据账号查询单条记录出错:", zap.Error(result.Error))
	}
	return nil
}

//删除用户以及关联的token记录
func (u *UsersModel) Destroy(id int,userName,pass string) bool {

	// 删除用户时，清除用户缓存在redis的全部token
	if variable.ConfigYml.GetInt("Token.IsCacheToRedis") == 1 {
		go u.DelTokenCacheFromRedis(int64(id))
	}
	
	// 检查密码是否正确
	sql := "select pass  from tb_users where  id=?  limit 1"
	result := u.Raw(sql, id).First(u)
	if result.Error == nil {
		// 账号密码验证成功
		if len(u.Pass) > 0 && (u.Pass == md5_encrypt.Base64Md5(pass)) {
			// 删除用户密钥
			err:=u.deleteKeyFromDB(userName)
			if err!=nil{
				variable.ZapLog.Error(err.Error())
			}
			// 删除用户
			if u.Delete(u, id).Error == nil {
				// 删除token
				if u.OauthDestroyToken(id) {
					return true
				}else{
					variable.ZapLog.Error("删除用户时token删除失败")
				}
			}else{
				variable.ZapLog.Error("删除用户失败")
			}
		}
	}

	return false
}

func (u *UsersModel)Logout(id int,userName string)bool{
	// 登出用户时，清除用户缓存在redis的全部token
	if variable.ConfigYml.GetInt("Token.IsCacheToRedis") == 1 {
		go u.DelTokenCacheFromRedis(int64(id))
	}
	// 删除token
	if !u.OauthDestroyToken(id) {
		// return true
		variable.ZapLog.Error("登出时删除token失败")
	}
	// 删除用户密钥
	if err:=u.deleteKeyFromDB(userName);err!=nil{
		variable.ZapLog.Error("登出时删除密钥失败")
	}
	return true
}
// 返回用户密码加密所需的公钥
func (u *UsersModel)PublicKey(userName string)[]byte{
	// sql :="SELECT public_key FROM `tb_rsa_keypair` WHERE user_name = ?"
	// var publicKey string
	// if res := u.Raw(sql,userName).Scan(&publicKey); res.RowsAffected > 0 {
	// 	return []byte(publicKey)
	// }
	// // 不存在密钥，重新生成密钥
	// return u.GenerateKeyPair(userName)
	publicKey, err := u.getKeyFromDB(userName, "public_key")
    if err == nil {
        return []byte(publicKey)
    }
    // 不存在密钥，重新生成密钥
    pubPEM, priPEM, err := rsa.GenerateRSAKeyPair()
    if err != nil {
		variable.ZapLog.Error("不存在密钥且密钥生成失败")
        return nil
    }
    if err := u.storeKeyPair(userName, pubPEM, priPEM); err != nil {
        variable.ZapLog.Error("密钥存储失败")
		return nil
    }
    return pubPEM
}
// 返回服务器解密所需的私钥
func (u *UsersModel)PrivateKey(userName string)[]byte{
	// sql :="SELECT private_key FROM `tb_rsa_keypair` WHERE user_name = ?"
	// var privateKey string
	// if res := u.Raw(sql,userName).Scan(&privateKey); res.RowsAffected > 0 {
	// 	priPEM:=pem.EncodeToMemory(&pem.Block{
	// 		Type: "PRIVATE KEY",
	// 		Bytes: []byte(privateKey),
	// 	})
	// 	return priPEM
	// }
	// return nil
	privateKey, err := u.getKeyFromDB(userName, "private_key")
    if err == nil {
        // priPEM := pem.EncodeToMemory(&pem.Block{
        //     Type:  "PRIVATE KEY",
        //     Bytes: []byte(privateKey),
        // })
        // return priPEM
		return []byte(privateKey)
    }
	variable.ZapLog.Error("返回私钥失败")
    return nil
}
// func (u *UsersModel)GenerateKeyPair(userName string)[]byte{
// 	priKey,err:=rsa.GenerateKey(rand.Reader,variable.ConfigYml.GetInt("RSA.keySize"))
// 	if err!=nil{
// 		return nil
// 	}
// 	pubKey:=&priKey.PublicKey

// 	// convert to byte slice
// 	priASN1:=x509.MarshalPKCS1PrivateKey(priKey)
// 	priPEM:=pem.EncodeToMemory(&pem.Block{
// 		Type: "PRIVATE KEY",
// 		Bytes: priASN1,
// 	})
// 	pubASN1,err:=x509.MarshalPKIXPublicKey(pubKey)
// 	if err!=nil{
// 		return nil
// 	}
// 	pubPEM := pem.EncodeToMemory(&pem.Block{
// 		Type:  "PUBLIC KEY",
// 		Bytes: pubASN1,
// 	})
// 	// store key_pair
// 	sql:="INSERT  INTO tb_rsa_keypair(user_name,public_key,private_key) VALUES (?,?,?)"
// 	if u.Exec(sql, userName, pubPEM, priPEM).RowsAffected > 0 {
// 		return pubPEM
// 	}
// 	return nil
// }
// 从数据库获取密钥（公钥/私钥）
func (u *UsersModel) getKeyFromDB(userName, keyType string) (string, error) {
    sql := fmt.Sprintf("SELECT %s FROM `tb_rsa_keypair` WHERE user_name = ?", keyType)
    var key string
    if res := u.Raw(sql, userName).Scan(&key); res.RowsAffected > 0 {
        return key, nil
    }
    return "", fmt.Errorf("key not found")
}
// 从数据库删除用户的所有密钥
func (u *UsersModel)deleteKeyFromDB(userName string) error {
    sql := "DELETE FROM `tb_rsa_keypair` WHERE user_name = ?"
    if u.Exec(sql, userName).RowsAffected > 0 {
        return nil
    }
    return fmt.Errorf("failed to delete key pair")
}
// 存储公私钥对到数据库
func (u *UsersModel) storeKeyPair(userName string, pubPEM, priPEM []byte) error {
    sql := "INSERT INTO tb_rsa_keypair(user_name, public_key, private_key) VALUES (?, ?, ?)"
    if u.Exec(sql, userName, pubPEM, priPEM).RowsAffected > 0 {
        return nil
    }
    return fmt.Errorf("failed to store key pair")
}
// 后续两个函数专门处理用户 token 缓存到 redis 逻辑

func (u *UsersModel) ValidTokenCacheToRedis(userId int64) {
	tokenCacheRedisFact := token_cache_redis.CreateUsersTokenCacheFactory(userId)
	if tokenCacheRedisFact == nil {
		variable.ZapLog.Error("redis连接失败，请检查配置")
		return
	}
	defer tokenCacheRedisFact.ReleaseRedisConn()

	sql := "SELECT   token,expires_at  FROM  `tb_oauth_access_tokens`  WHERE   fr_user_id=?  AND  revoked=0  AND  expires_at>NOW() ORDER  BY  expires_at  DESC , updated_at  DESC  LIMIT ?"
	maxOnlineUsers := variable.ConfigYml.GetInt("Token.JwtTokenOnlineUsers")
	rows, err := u.Raw(sql, userId, maxOnlineUsers).Rows()
	defer func() {
		//  凡是获取原生结果集的查询，记得释放记录集
		_ = rows.Close()
	}()

	var tempToken, expires string
	if err == nil && rows != nil {
		for i := 1; rows.Next(); i++ {
			err = rows.Scan(&tempToken, &expires)
			if err == nil {
				if ts, err := time.ParseInLocation(variable.DateFormat, expires, time.Local); err == nil {
					tokenCacheRedisFact.SetTokenCache(ts.Unix(), tempToken)
					// 因为每个用户的token是按照过期时间倒叙排列的，第一个是有效期最长的，将该用户的总键设置一个最大过期时间，到期则自动清理，避免不必要的数据残留
					if i == 1 {
						tokenCacheRedisFact.SetUserTokenExpire(ts.Unix())
					}
				} else {
					variable.ZapLog.Error("expires_at 转换位时间戳出错", zap.Error(err))
				}
			}
		}
	}
	// 缓存结束之后删除超过系统设置最大在线数量的token
	tokenCacheRedisFact.DelOverMaxOnlineCache()
}

// DelTokenCacheFromRedis 用户密码修改后，删除redis所有的token
func (u *UsersModel) DelTokenCacheFromRedis(userId int64) {
	tokenCacheRedisFact := token_cache_redis.CreateUsersTokenCacheFactory(userId)
	if tokenCacheRedisFact == nil {
		variable.ZapLog.Error("redis连接失败，请检查配置")
		return
	}
	tokenCacheRedisFact.ClearUserToken()
	tokenCacheRedisFact.ReleaseRedisConn()
}
