package service

import (
	"encoding/json"
	"fengkuangGameServerJob/dao"
	"fengkuangGameServerJob/model"
	"fmt"
	"gorm.io/gorm"
	"strconv"
	"strings"
	"time"
)

const delimiter = ":" //key的定界符

// MountMissionService 坐骑任务相关服务层
type MountMissionService struct {
}

func NewMountMissionService() *MountMissionService {
	return &MountMissionService{}
}

// GetStorageMaxByPlayerIds 获取玩家现有粮仓的上限
func (m *MountMissionService) GetStorageMaxByPlayerIds(playerIds []int) *map[int]int {
	storages := dao.GetMountStorage(playerIds)
	storageMaxMap := make(map[int]int)
	for _, storage := range storages {
		storageMaxMap[storage.PlayerId] = storage.StorageMax
	}
	return &storageMaxMap
}

// GetDueMission 获取到期的任务的切片(任务由玩家ID)
func (m *MountMissionService) GetDueMission() ([]MissionInfo, error) {
	nowUnix := time.Now().Unix()
	missionsInfoStr, err := dao.GetDueMission(nowUnix)
	if err != nil {
		return nil, err
	}
	missionsInfo := make([]MissionInfo, 0, 500)
	for _, v := range missionsInfoStr {
		//key是玩家ID:地址ID
		tmpArr := strings.Split(v, delimiter)
		if len(tmpArr) != 2 {
			continue
		}
		playerId, _ := strconv.Atoi(tmpArr[0])
		addrId, _ := strconv.Atoi(tmpArr[1])
		if playerId == 0 || addrId == 0 {
			continue
		}
		missionsInfo = append(missionsInfo, MissionInfo{
			PlayerId: playerId,
			AddrId:   uint8(addrId),
		})
	}
	return missionsInfo, nil
}

// ParseMissionConf 解析坐骑任务配置
func (m *MountMissionService) ParseMissionConf() (map[uint8]*MountMissionDetailConf, error) {
	//1.获取json配置信息
	confs, err := GetMountMissionConf()
	if err != nil {
		return nil, err
	}
	//2.自定义重组配置
	confMap := make(map[uint8]*MountMissionDetailConf)
	for _, conf := range confs {
		placeRewardTmp := strings.Split(conf.PlaceReward, "|")
		tmpResource := make([]Resource, 0, len(placeRewardTmp))
		for _, reward := range placeRewardTmp {
			tmp := strings.Split(reward, "-")
			if len(tmp) != 2 {
				continue
			}
			id, _ := strconv.Atoi(tmp[0])
			amount, _ := strconv.Atoi(tmp[1])
			tmpResource = append(tmpResource, Resource{
				ID:     id,
				Amount: amount,
			})
		}
		conf.PlaceRewardRes = tmpResource
		confMap[conf.PlaceID] = conf
	}
	return confMap, nil
}

// HandlerSingleMission 处理单个任务详情
func (m *MountMissionService) HandlerSingleMission(mission MissionInfo, conf *MountMissionDetailConf, storageMax int) error {
	tx := dao.DB().Begin()
	err := m.doSingleDueMission(tx, mission, conf, storageMax)
	if err != nil {
		tx.Rollback()
	} else {
		tx.Commit()
	}
	return err
}

// 事务内处理一个到期的任务
func (m *MountMissionService) doSingleDueMission(tx *gorm.DB, mission MissionInfo, conf *MountMissionDetailConf, storageMax int) (err error) {
	//1. 将坐骑奖励存放在粮仓临时表
	var (
		tmpMountStorage model.PlayerMountStorageTmp
		tmpForage       int //当前临时粮仓里面的粮草的数量
	)
	//1.1 先获取当前临时粮仓存储了多少数据，没有则新增
	for _, reward := range conf.PlaceRewardRes {
		//修改为先查找，没有则写入有则更新
		//若是直接更新判断受影响行数会存在bug,极端多个任务属于同一个玩家，且粮草都满了，更新语句执行过快，第二次以后的更新受影响行数=0,又会写入发生唯一索引冲突
		tmpMountStorage, err = dao.GetPlayerResourceAmountMountTmp(tx, mission.PlayerId, reward.ID)
		if err != nil {
			return err
		}
		if tmpMountStorage.Id == 0 {
			//若数据不存在则写入
			_, err = dao.AddPlayerResourceToMountTmp(tx, mission.PlayerId, reward.ID, reward.Amount)
			if err != nil {
				return err
			}
			tmpMountStorage.ResourceAmount = reward.Amount
		}
		if reward.ID == ItemStorage {
			//找到当前临时粮仓中有多少粮草
			tmpForage = tmpMountStorage.ResourceAmount
		}
	}
	//1.2 若当前临时粮仓存的粮草达到上限则不发放任何奖励
	if tmpForage < storageMax {
		//1.3粮仓未满则发放奖励，粮草发放到上限为止，其他奖励正常发放
		//粮仓满了则不会更新任何数据
		for _, reward := range conf.PlaceRewardRes {
			if reward.ID == ItemStorage {
				//粮草有最大值限制
				_, err = dao.IncrPlayerForageResourceTmp(tx, mission.PlayerId, reward.ID, reward.Amount, storageMax)
			} else {
				_, err = dao.IncrPlayerResourceToMountTmp(tx, mission.PlayerId, reward.ID, reward.Amount)
			}
		}
	}
	//2. 重新开始任务(当前任务的结束时间作为新任务的开始时间)
	//2.1 根据玩家ID+任务地点ID来获取任务信息(同一个任务地点任务信息相同，这里只需获取其中一条即可)
	tmpInfo, err := dao.GetMissionInfo(tx, mission.PlayerId, mission.AddrId, []string{"end_unix"})
	if err != nil {
		return err
	}
	newMissionStarUnix := tmpInfo.EndUnix
	newMissionEndUnix := newMissionStarUnix + conf.MissionTime
	//2.2 根据玩家ID+任务地点ID来更新任务
	err = dao.ResumeMission(tx, mission.PlayerId, mission.AddrId, newMissionStarUnix, newMissionEndUnix)
	if err != nil {
		return err
	}
	//3. 更新当前任务的score(即结束时间戳)
	pipeTx := dao.Redis().TxPipeline()
	//3.1 更新结束时间
	dao.SetMission(pipeTx, fmt.Sprintf("%d%s%d", mission.PlayerId, delimiter, mission.AddrId), newMissionEndUnix)
	//3.2 需要推送的协议
	logByte, _ := json.Marshal(RealTimeLog{
		PlayerId: mission.PlayerId,
		CmdId:    3050,
		Date:     time.Now(),
	})
	dao.AddRealTimeMsg(pipeTx, string(logByte))
	_, err = pipeTx.Exec()
	return err
}

// MissionInfo 写在redis中的信息
type MissionInfo struct {
	PlayerId int
	AddrId   uint8
}
