package dao

import (
	"encoding/json"
	"fengkuangGameServerJob/model"
	"fengkuangGameServerJob/pkg/util"
	"fmt"
	"github.com/go-redis/redis"
	"gorm.io/gorm"
	"strconv"
	"strings"
	"time"
)

// IncrPlayerResourceToMountTmp 给玩家增加资源
func IncrPlayerResourceToMountTmp(tx *gorm.DB, playerId int, resourceId int, resourceAmount int) (int64, error) {
	exec := tx.Exec(`UPDATE game_player_mount_storage_tmp 
SET 
    resource_amount = resource_amount + ?,
    updated_at = ?
WHERE
    player_id = ? AND resource_id = ? LIMIT 1;`, resourceAmount, time.Now(), playerId, resourceId)
	return exec.RowsAffected, exec.Error
}

// IncrPlayerForageResourceTmp IncrPlayerForageResource 给玩家临时仓库增加粮草(粮草是有上限的)
func IncrPlayerForageResourceTmp(tx *gorm.DB, playerId int, resourceId int, resourceAmount int, maxAmount int) (int64, error) {
	exec := tx.Exec(`UPDATE game_player_mount_storage_tmp 
SET 
    resource_amount = IF(resource_amount + ? >= ?, ?, resource_amount + ?),
    updated_at = ?
WHERE
    player_id = ? AND resource_id = ? LIMIT 1;`, resourceAmount, maxAmount, maxAmount, resourceAmount, time.Now(), playerId, resourceId)
	return exec.RowsAffected, exec.Error
}

// AddPlayerResourceToMountTmp  新增玩家坐骑任务临时资源
func AddPlayerResourceToMountTmp(tx *gorm.DB, playerId int, resourceId int, resourceAmount int) (int, error) {
	record := model.PlayerMountStorageTmp{
		PlayerId:       playerId,
		ResourceId:     resourceId,
		ResourceAmount: resourceAmount,
	}
	err := tx.Create(&record).Error
	return record.Id, err
}

// GetPlayerResourceAmountMountTmp 获取玩家对应的资源个数
func GetPlayerResourceAmountMountTmp(tx *gorm.DB, playerId int, resourceId int) (model.PlayerMountStorageTmp, error) {
	var res model.PlayerMountStorageTmp
	err := tx.Select("id", "resource_amount").
		Where("player_id = ?", playerId).
		Where("resource_id = ?", resourceId).
		Limit(1).
		Find(&res).Error
	return res, err
}

// AddResourceChangeLog 增加资源变动日志
func AddResourceChangeLog(pipeTx redis.Pipeliner, logs []string) {
	pipeTx.LPush(ResourceChangeLogKey, logs)
}

// AddRealTimeMsg 把消息发送到实时推送队列
func AddRealTimeMsg(pipeTx redis.Pipeliner, msgJsonStr string) {
	pipeTx.LPush(MsgNotifyListKey, msgJsonStr)
}

// FindNewestServerWithTcp 查找最新的区服及对应的tcp信息
func FindNewestServerWithTcp(grayscale int) *model.ServerWithTcp {
	var info model.ServerWithTcp
	err := DB().Raw(`
SELECT 
    s.server_id, t.tcp_id, t.port, t.ip, s.cross_id
FROM
    game_server AS s
        INNER JOIN
    game_tcp AS t ON s.tcp_id = t.tcp_id AND t.tcp_state = 1
WHERE
    s.server_state = 1 AND s.server_grayscale = ? AND s.running_status = 2
ORDER BY s.server_id DESC
LIMIT 1;`, grayscale).Scan(&info).Error
	util.CheckErr(err)
	return &info
}

//FindNewestServerInCache 从缓存中获取最新的服务器信息
func FindNewestServerInCache(grayscale int) *model.ServerWithTcp {
	jsonStr, err := Redis().Get(fmt.Sprintf("%s:%d", LoginNewestServer, grayscale)).Result()
	util.CheckRedisErr(err)
	if len(jsonStr) == 0 {
		return nil
	}
	var res *model.ServerWithTcp
	err = json.Unmarshal([]byte(jsonStr), &res)
	if err != nil {
		return nil
	}
	return res
}

//CacheNewestServer 缓存最新的区服信息
func CacheNewestServer(info *model.ServerWithTcp, expired int, grayscale int) {
	b, _ := json.Marshal(&info)
	key := fmt.Sprintf("%s:%d", LoginNewestServer, grayscale)
	_, err := Redis().Set(key, string(b), time.Duration(expired)*time.Second).Result()
	util.CheckRedisErr(err)
}

// FindLastServerWithTcp 根据openid查找玩家最近登录的区服信息
func FindLastServerWithTcp(openid string, grayscale int) *model.ServerWithTcp {
	var info model.ServerWithTcp
	err := DB().Raw(`
SELECT 
    player.player_id, t.tcp_id, t.ip, t.port, s.server_id, s.cross_id
FROM
    game_channel_user AS u
        INNER JOIN
    game_player AS player ON u.user_id = player.user_id
        AND player.state = 1
        INNER JOIN
    game_server AS s ON player.server_id = s.server_id
        AND s.server_state = 1 AND s.server_grayscale = ?
        INNER JOIN
    game_tcp AS t ON s.tcp_id = t.tcp_id AND t.tcp_state = 1
WHERE
    u.openid = ? AND u.user_state = 1
ORDER BY player.last_login DESC
LIMIT 1;`, grayscale, openid).Scan(&info).Error
	util.CheckErr(err)
	return &info
}

func FindFixedServerWithTcp(serverId int, grayscale int) *model.ServerWithTcp {
	var info model.ServerWithTcp
	err := DB().Raw(`
SELECT 
    0 AS player_id, t.tcp_id, t.ip, t.port, s.server_id, s.cross_id
FROM
    game_server AS s
        INNER JOIN
    game_tcp AS t ON s.tcp_id = t.tcp_id AND t.tcp_state = 1
WHERE
    s.server_id = ? AND s.server_state = 1 AND s.server_grayscale = ?
LIMIT 1;`, serverId, grayscale).Scan(&info).Error
	util.CheckErr(err)
	return &info
}

// FindTcpIdByPlayerId 根据玩家的ID来获取所属的tcpId
func FindTcpIdByPlayerId(playerId int) (int, error) {
	var tcpId int
	err := DB().Raw(`
SELECT 
	s.tcp_id AS tcpId
FROM
	game_player AS p
		INNER JOIN
	game_server AS s ON p.server_id = s.server_id
WHERE
	player_id = ?
LIMIT 1;`, playerId).Scan(&tcpId).Error
	return tcpId, err
}

// FindTcpInfoByPlayerId 根据openid查找玩家最近登录的区服信息
func FindTcpInfoByPlayerId(playerId int) *model.ServerWithTcp {
	var info model.ServerWithTcp
	err := DB().Raw(`
SELECT 
    player.player_id, s.server_id, s.tcp_id, s.cross_id
FROM
    game_player AS player
        INNER JOIN
    game_server AS s ON player.server_id = s.server_id
WHERE
    player.player_id = ?
LIMIT 1;`, playerId).Scan(&info).Error
	util.CheckErr(err)
	return &info
}

// AddZSetMsg 定时任务消息添加到有序集合
func AddZSetMsg(tx redis.Pipeliner, tcpId int, tokenSign string) {
	keyZSet := fmt.Sprintf("%s:%d", MsgNotifyListNew, tcpId)
	tx.ZAdd(keyZSet, redis.Z{
		Score:  float64(time.Now().Unix()),
		Member: tokenSign,
	})
}

// AddHashMsg 添加任务到hash中
func AddHashMsg(tx redis.Pipeliner, tcpId int, content map[string]interface{}) {
	keyHash := fmt.Sprintf("%s:%d", MsgNotifyContentNew, tcpId)
	tx.HMSet(keyHash, content)
}

// CacheLoginToken 缓存登录的token
func CacheLoginToken(openid string, loginCacheMap map[string]interface{}, expireSeconds int) {
	key := LoginToken + ":" + openid
	pipeTx := Redis().TxPipeline()
	pipeTx.HMSet(key, loginCacheMap)
	pipeTx.Expire(key, time.Second*time.Duration(expireSeconds))
	_, err := pipeTx.Exec()
	util.CheckErr(err)
}

// FindAppSecret 通过appid找出密钥
func FindAppSecret(appid string, state uint8) string {
	var appSecret string
	err := DB().Raw(`
SELECT 
    appsecret
FROM
    game_package
WHERE
    appid = ? AND package_state = ?
LIMIT 1;`, appid, state).Scan(&appSecret).Error
	util.CheckErr(err)
	return appSecret
}

// CacheAppSecret 缓存app的密钥
func CacheAppSecret(appid, appSecret string, expired int) {
	pipe := Redis().Pipeline()
	pipe.HSet(MiniGameSecrets, appid, appSecret)
	duration, _ := pipe.TTL(MiniGameSecrets).Result()
	if int64(duration) <= 0 {
		pipe.Expire(MiniGameSecrets, time.Duration(expired)*time.Second)
	}
	_, err := pipe.Exec()
	util.CheckErr(err)
}

// GetAppSecretFromCache 从缓存中读取appSecret
func GetAppSecretFromCache(appid string) string {
	appSecret, err := Redis().HGet(MiniGameSecrets, appid).Result()
	if err != nil && err != redis.Nil {
		panic(err)
	}
	return appSecret
}

// CacheOpenId 缓存校验成功的openid减少再次校验
func CacheOpenId(appId, openid string, isOk bool, expired int) {
	var key string
	if isOk == true {
		key = fmt.Sprintf(OpenIdOk, appId)
	} else {
		key = fmt.Sprintf(OpenIdWrong, appId)
	}
	pipe := Redis().Pipeline()
	pipe.SAdd(key, openid)
	duration, _ := pipe.TTL(key).Result()
	if int64(duration) <= 0 {
		pipe.Expire(key, time.Duration(expired)*time.Second)
	}
	_, err := pipe.Exec()
	util.CheckErr(err)
}

// GetOpenIdInCache 判断当前openid是否已经校验过
func GetOpenIdInCache(appId, openid string, isOk bool) bool {
	var key string
	if isOk == true {
		key = fmt.Sprintf(OpenIdOk, appId)
	} else {
		key = fmt.Sprintf(OpenIdWrong, appId)
	}
	result, err := Redis().SIsMember(key, openid).Result()
	util.CheckErr(err)
	return result
}

// IpInBlackList 校验ip是否在黑名单中
func IpInBlackList(ip string) bool {
	result, err := Redis().SIsMember(LoginBlacklist, ip).Result()
	util.CheckRedisErr(err)
	return result
}

// AddIpBlackList 将ip添加到黑名单中
func AddIpBlackList(ip string) {
	_, err := Redis().SAdd(LoginBlacklist, ip).Result()
	util.CheckRedisErr(err)
}

// GetVisitTimesOneMinute 获取一分钟内的访问次数
func GetVisitTimesOneMinute(ip string, expired int) int {
	key := fmt.Sprintf(LoginRate, ip)
	times, err := Redis().Incr(key).Result()
	util.CheckRedisErr(err)
	duration, _ := Redis().TTL(key).Result()
	if int64(duration) <= 0 {
		Redis().Expire(key, time.Duration(expired)*time.Second)
	}
	return int(times)
}

// GetLoginServerId 获取登录指定的区服信息
func GetLoginServerId(openid string) int {
	key := fmt.Sprintf(LoginServer, openid)
	serverIdStr, err := Redis().Get(key).Result()
	util.CheckRedisErr(err)
	serverId, _ := strconv.Atoi(serverIdStr)
	return serverId
}

// SetLoginServerId 设置选择指定的区服
func SetLoginServerId(openid string, serverId int) {
	key := fmt.Sprintf(LoginServer, openid)
	err := Redis().Set(key, serverId, time.Duration(12*3600)*time.Second).Err()
	util.CheckRedisErr(err)
}

// DelLoginServerId 删除指定的区服
func DelLoginServerId(openid string) {
	key := fmt.Sprintf(LoginServer, openid)
	Redis().Del(key)
}

// GetPlayerIdByOpenIdServerId 通过openid和区服id来找玩家角色ID
func GetPlayerIdByOpenIdServerId(openid string, serverId int) int {
	var playerId int
	err := DB().Raw(`
SELECT 
    player.player_id
FROM
    game_channel_user AS u
        INNER JOIN
    game_player AS player ON u.user_id = player.user_id
        AND (player.server_id = ?
        AND player.state = 1)
WHERE
    u.openid = ?
LIMIT 1;`, serverId, openid).Scan(&playerId).Error
	util.CheckErr(err)
	return playerId
}

// IncrResource 给玩家角色增加资源
func IncrResource(playerId, resourceId, amount int) int64 {
	res := DB().Exec(`UPDATE game_player_resource 
SET 
    resource_amount = resource_amount + ?
WHERE
    player_id = ? AND resource_id = ? LIMIT 1`, amount, playerId, resourceId)
	util.CheckErr(res.Error)
	return res.RowsAffected
}

// AddResource 增加用户资源
func AddResource(playerId, resourceId, amount int) {
	resource := model.PlayerResource{
		PlayerId:       playerId,
		ResourceId:     resourceId,
		ResourceAmount: amount,
	}
	res := db.Create(&resource)
	util.CheckErr(res.Error)
}

// GetPlayerList 分页获取玩家角色列表
func GetPlayerList(beginId, size int, column []string) []*model.Player {
	columnStr := strings.Join(column, ",")
	var players []*model.Player
	err := DB().Select(columnStr).
		Where("player_id > ?", beginId).
		Order("player_id ASC").
		Limit(size).
		Find(&players).Error
	util.CheckErr(err)
	return players
}

// FindChannelUser 获取渠道用户信息
func FindChannelUser(openid string, column []string) *model.ChannelUser {
	columnStr := strings.Join(column, ",")
	var cUser *model.ChannelUser
	err := DB().Select(columnStr).
		Where("openid = ?", openid).
		Limit(1).
		Find(&cUser).Error
	util.CheckErr(err)
	return cUser
}

// FindVersion 根据版本名称来获取版本信息
func FindVersion(version string, column []string) model.Version {
	columnStr := strings.Join(column, ",")
	var versionInfo model.Version
	err := DB().Select(columnStr).
		Where("version_name = ?", version).
		Where("version_state = 1").
		Limit(1).
		Find(&versionInfo).Error
	util.CheckErr(err)
	return versionInfo
}

func GetOpenIdByPlayerId(playerId int) string {
	var openid string
	err := DB().Raw(`
SELECT 
    user.openid
FROM
    game_channel_user AS user
        inner JOIN
    game_player AS player ON user.user_id = player.user_id
        AND player.player_id = ?
LIMIT 1;`, playerId).Scan(&openid).Error
	util.CheckErr(err)
	return openid
}

// FindServer 根据区服ID来获取服务信息
func FindServer(serverId int, column []string) *model.Server {
	columnStr := strings.Join(column, ",")
	serverInfo := new(model.Server)
	err := DB().Select(columnStr).
		Where("server_id = ?", serverId).
		Limit(1).
		Find(&serverInfo).Error
	util.CheckErr(err)
	return serverInfo
}

func FindPlayerLastServer(openid string, grayscale int) *model.Server {
	var info model.Server
	err := DB().Raw(`
SELECT 
    s.server_id, s.server_name, s.running_status, s.open_at
FROM
    game_channel_user AS u
        INNER JOIN
    game_player AS player ON u.user_id = player.user_id
        AND player.state = 1
        INNER JOIN
    game_server AS s ON player.server_id = s.server_id
        AND s.server_state = 1 AND s.server_grayscale = ?
WHERE
    u.openid = ? AND u.user_state = 1
ORDER BY player.last_login DESC
LIMIT 1;`, grayscale, openid).Scan(&info).Error
	util.CheckErr(err)
	return &info
}

// FindNewestServer 查找最新的区服
func FindNewestServer(grayscale int) *model.Server {
	var info model.Server
	err := DB().Raw(`
SELECT 
    s.server_id, s.server_name, s.running_status, s.open_at
FROM
    game_server AS s
WHERE
    s.server_state = 1 AND s.server_grayscale = ? AND s.running_status = 2
ORDER BY s.server_id DESC
LIMIT 1;`, grayscale).Scan(&info).Error
	util.CheckErr(err)
	return &info
}

// GetServerTotal 获取正常运行中的区服数量
func GetServerTotal(grayscale int) int {
	var total int
	err := DB().Raw(`
SELECT 
	COUNT(server_id) AS total
FROM
	game_server
WHERE
	server_state = 1 AND
	server_grayscale = ?;`, grayscale).Scan(&total).Error
	util.CheckErr(err)
	return total
}

// GetRecommendServer 获取推荐的服务器
func GetRecommendServer(userId, grayscale int) *model.PlayerWithServer {
	var info model.PlayerWithServer
	err := DB().Raw(`
SELECT 
	s.server_id,
	s.server_name,
	s.open_at,
	s.running_status,
	IFNULL(p.nick_name, '') AS nick_name,
	IFNULL(p.player_level, 0) AS player_level,
	IFNULL(p.player_id, 0) AS player_id
FROM
	game_server AS s
		LEFT JOIN
	game_player AS p ON s.server_id = p.server_id
		AND p.user_id = ?
WHERE
	s.server_state = 1 AND s.running_status = 2 AND s.server_grayscale = ?
ORDER BY s.server_id DESC
LIMIT 1;`, userId, grayscale).Scan(&info).Error
	util.CheckErr(err)
	return &info
}

// GetMyRoleServer 获取自己所属的区服列表
//grayscale 灰度服 1是灰度 0不是
func GetMyRoleServer(userId, grayscale int) []*model.PlayerWithServer {
	var lists []*model.PlayerWithServer
	err := DB().Raw(`
SELECT 
	s.server_id,
	s.server_name,
	s.open_at,
	s.running_status,
	IFNULL(p.nick_name, '') AS nick_name,
	IFNULL(p.player_level, 0) AS player_level,
	IFNULL(p.player_id, '') AS player_id
FROM
	game_player AS p
		INNER JOIN
	game_server AS s ON p.server_id = s.server_id AND s.server_grayscale = ? AND s.server_state = 1
WHERE
	p.user_id = ?
ORDER BY p.player_level DESC
LIMIT 100;`, grayscale, userId).Scan(&lists).Error
	util.CheckErr(err)
	return lists
}

// GetPagingServer 分页获取区服信息
//userId 玩家渠道ID
//serverGrayscale 灰度服 1是 0不是
//begin 分页开始(page - 1) * size
//size 每页显示条数
func GetPagingServer(userId, grayscale, begin, size int) []*model.PlayerWithServer {
	var lists []*model.PlayerWithServer
	err := DB().Raw(`
SELECT 
	s.server_id,
	s.server_name,
	s.open_at,
	s.running_status,
	IFNULL(p.nick_name, '') AS nick_name,
	IFNULL(p.player_level, 0) AS player_level,
	IFNULL(p.player_id, 0) AS player_id
FROM
	game_server AS s
		LEFT JOIN
	game_player AS p ON s.server_id = p.server_id
		AND p.user_id = ?
WHERE
	s.server_state = 1 AND s.server_grayscale = ?
ORDER BY s.server_id ASC
LIMIT ? , ?;`, userId, grayscale, begin, size).Scan(&lists).Error
	util.CheckErr(err)
	return lists
}

// GetPackageByChannelKey 根据channelKey来获取包信息
func GetPackageByChannelKey(channelKey string) *model.Package {
	var p *model.Package
	err := DB().Where("channel_key = ?", channelKey).Limit(1).Find(&p).Error
	util.CheckErr(err)
	return p
}

// GetUserLastLogin 根据openid来获取玩家最新的登录时间
func GetUserLastLogin(openid string) model.Player {
	var p model.Player
	err := DB().Raw(`
SELECT 
    p.player_id, p.last_login
FROM
    game_channel_user AS u
        INNER JOIN
    game_player AS p ON u.user_id = p.user_id
WHERE
    u.openid = ?
#ORDER BY p.last_login DESC
LIMIT 1;`, openid).Scan(&p).Error
	util.CheckErr(err)
	return p
}

// GetUserNewOrOldFromCache 缓存中获取用户是否是新、老用户
func GetUserNewOrOldFromCache(appid, openid string, isNew bool) bool {
	redisKey := ""
	if isNew == true {
		redisKey = IsNewUser + appid
	} else {
		redisKey = IsOldUser + appid
	}
	ret, err := Redis().SIsMember(redisKey, openid).Result()
	util.CheckErr(err)
	return ret
}

// SetIsNewUserInCache 用户是新用户的话写入缓存
func SetIsNewUserInCache(appid, openid string) {
	key := IsNewUser + appid
	err := Redis().SAdd(key, openid).Err()
	util.CheckErr(err)
}

// SetIsOldUserInCache 新老用户状态设置到缓存
func SetIsOldUserInCache(appid, openid string, seconds int) {
	key := IsOldUser + appid
	err := Redis().SAdd(key, openid).Err()
	util.CheckErr(err)
	expire, err := Redis().TTL(key).Result()
	util.CheckErr(err)
	if expire < 0 {
		err = Redis().Expire(key, time.Second*time.Duration(seconds)).Err()
	}
	util.CheckErr(err)
}

// GetTcpByCrossId 根据跨服ID来获取Tcp信息
func GetTcpByCrossId(crossId int) *model.Tcp {
	tcp := new(model.Tcp)
	err := DB().Raw(`
SELECT 
    IFNULL(t.tcp_id, 0) AS tcp_id, IFNULL(t.ip, '') AS ip
FROM
    game_server_cross AS c
        LEFT JOIN
    game_tcp AS t ON c.cross_tcp_id = t.tcp_id
WHERE
    c.cross_id = ?
LIMIT 1;`, crossId).Scan(&tcp).Error
	util.CheckErr(err)
	return tcp
}
