package main

import (
	"mongo.games.com/game/worldsrv/internal"
	"strconv"
	"time"

	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/i18n"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/module"
	"mongo.games.com/goserver/core/task"

	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
	"mongo.games.com/game/proto"
	"mongo.games.com/game/protocol/bag"
	playerproto "mongo.games.com/game/protocol/player"
	"mongo.games.com/game/srvdata"
)

const (
	BagItemMax int32 = 200
)

// 道具功能  Function
const (
	ItemCanUse  = iota //可以使用
	ItemCanGive        //可以赠送
	ItemCanSell        //可以出售
	ItemMax
)

type Item struct {
	ItemId  int32 // 物品ID
	ItemNum int64 // 物品数量
	////数据表数据
	Name string // 名称
	//ShowLocation   []int32 // 显示位置
	//Classify       []int32 // 分页类型 1，道具类 	2，资源类	3，兑换类
	//Type           int32   // 道具种类 1，宠物碎片 2，角色碎片
	Effect0  []int32 // 竖版道具功能 1，使用 2，赠送 3，出售
	Effect   []int32 // 横版道具功能 1，使用 2，赠送 3，出售
	SaleType int32   // 出售类型
	SaleGold int32   // 出售金额
	//Composition    int32   // 能否叠加 1，能 2，不能
	//CompositionMax int32   // 叠加上限
	//Time           int32   // 道具时效 0为永久
	//Location       string  // 跳转页面
	//Describe       string  // 道具描述
	//数据库数据
	ObtainTime int64 //获取的时间
}

type BagInfo struct {
	SnId     int32           //玩家账号直接在这里生成
	Platform string          //平台
	BagItem  map[int32]*Item //背包数据  key为itemId
	dirty    bool
}

// BagMgrSingleton 背包管理器
var BagMgrSingleton = &BagMgr{
	PlayerBag: make(map[int32]*BagInfo),
}

type BagMgr struct {
	PlayerBag map[int32]*BagInfo // snid:背包
}

func (this *BagMgr) ModuleName() string {
	return "BagMgr"
}

func (this *BagMgr) Init() {
}

func (this *BagMgr) GetBagInfo(snid int32) *BagInfo {
	if v, exist := this.PlayerBag[snid]; exist {
		return v
	}
	return nil
}

// GetItem 获取个人的指定道具信息
func (this *BagMgr) GetItem(snid, itemId int32) *Item {
	if bagItem, ok := this.PlayerBag[snid]; ok {
		if bagItem != nil {
			item := bagItem.BagItem[itemId]
			if item != nil {
				itemX := srvdata.PBDB_GameItemMgr.GetData(item.ItemId)
				if itemX != nil {
					item.Name = itemX.Name
					//item.ShowLocation = itemX.ShowLocation
					//item.Classify = itemX.Classify
					//item.Type = itemX.Type
					item.Effect0 = itemX.Effect
					item.Effect = itemX.Effect
					item.SaleType = itemX.SaleType
					item.SaleGold = itemX.SaleGold
					//item.Composition = itemX.Composition
					//item.CompositionMax = itemX.CompositionMax
					//item.Time = itemX.Time
					//item.Location = itemX.Location
					//item.Describe = itemX.Describe
				}
				return item
			}
		}
	}
	return nil
}

// AddJybBagInfo 给玩家背包添加道具
// add 加成数量
// gainWay 记录类型
// oper 操作人
// remark 备注
func (this *BagMgr) AddJybBagInfo(p *Player, addItems []*Item, add int64, gainWay int32, oper, remark string) (*BagInfo, bag.OpResultCode) {
	var items []*Item
	for _, v := range addItems {
		if v == nil || v.ItemNum == 0 {
			continue
		}
		item := srvdata.PBDB_GameItemMgr.GetData(v.ItemId)
		switch item.Type {
		case common.ItemTypeCoin:
			//增加金币
			if item.Id == common.ItemIDCoin {
				p.AddCoin(v.ItemNum, 0, gainWay, oper, remark)
			}
		case common.ItemTypeDiamond:
			//增加钻石
			if item.Id == common.ItemIDDiamond {
				p.AddDiamond(v.ItemNum, 0, gainWay, oper, remark)
			}
		case common.ItemTypeFishPower:
			//增加炮台
			p.ItemUnPlayerPowerListEx(v.ItemId)
		case common.ItemTypeMoneyPond:
			//增加个人金币池
			if v.ItemId == common.ItemIDMoneyPond {
				p.MoneyPond += v.ItemNum
			}
		case common.ItemTypeVipExp:
			//增加玩家VIP经验
			if v.ItemId == common.ItemIDVipExp {
				p.AddMoneyPayTotal(v.ItemNum)
			}
		case common.ItemTypeShopScore:
			if v.ItemId == common.ItemIDPhoneScore {
				p.AddPhoneScore(v.ItemNum, 0, gainWay, oper, remark)
			}
		default:
			// 道具变化
			items = append(items, v)
		}
	}

	// 添加道具到背包

	var changeItems []int32
	var newBagInfo *BagInfo
	if _, exist := this.PlayerBag[p.SnId]; !exist {
		newBagInfo = &BagInfo{
			SnId:     p.SnId,
			Platform: p.Platform,
			BagItem:  make(map[int32]*Item),
		}
	} else {
		newBagInfo = this.PlayerBag[p.SnId]
	}

	if len(items) == 0 {
		return newBagInfo, bag.OpResultCode_OPRC_Sucess
	}

	var code = bag.OpResultCode_OPRC_Sucess
	for _, v := range items {
		if v == nil || v.ItemNum == 0 {
			continue
		}
		item := srvdata.PBDB_GameItemMgr.GetData(v.ItemId)
		if item == nil {
			code = bag.OpResultCode_OPRC_IdErr
			continue
		}

		changeItems = append(changeItems, v.ItemId)
		if itm, exist := newBagInfo.BagItem[v.ItemId]; exist {
			itm.ItemNum += v.ItemNum
		} else {
			newBagInfo.BagItem[v.ItemId] = &Item{
				ItemId:     item.Id,   // 物品id
				ItemNum:    v.ItemNum, // 数量
				ObtainTime: time.Now().Unix(),
			}
		}
		if v.ItemId == common.ItemIDWeekScore && v.ItemNum != 0 {
			TaskSubjectSingleton.Touch(common.TaskTypeActivityScore, &TaskData{
				SnId: p.SnId,
				Num:  v.ItemNum,
			})
		}
	}
	if len(changeItems) > 0 {
		newBagInfo.dirty = true
		p.dirty = true
		this.PlayerBag[p.SnId] = newBagInfo
		this.SyncBagData(p.SnId, changeItems...)
	}

	return newBagInfo, code
}

// SaleItem 出售道具，减少玩家道具数量
func (this *BagMgr) SaleItem(p *Player, itemId int32, num int64) bool {
	if bagInfo, ok := this.PlayerBag[p.SnId]; ok {
		if item, ok1 := bagInfo.BagItem[itemId]; ok1 {
			if item.ItemNum >= num {
				//可以出售
				return this.SalePlayerItem(p, item, num)
			}
		}
	}
	return false
}

// SalePlayerItem 出售道具，减少玩家道具数量
func (this *BagMgr) SalePlayerItem(p *Player, item *Item, num int64) bool {
	item.ItemNum -= num
	p.dirty = true
	this.SyncBagData(p.SnId, item.ItemId)
	return true
}

// RecordItemLog 道具操作记录（获得，消耗）
func (this *BagMgr) RecordItemLog(platform string, snid, logType, itemId int32, itemName string, count int64, remark string) {
	//logger.Logger.Trace("RecordItemLog:", platform, snid, logType, itemId, itemName, count, remark)
	log := model.NewItemLogEx(platform, snid, logType, itemId, itemName, count, remark)
	if log != nil {
		LogChannelSingleton.WriteLog(log)
	}
}

// AddMailByItem 赠送道具到邮件
// srcId 发送人 srcName发送人名字
// items[0]:道具id items[1]:道具数量 items[2]:道具id items[3]:道具数量
func (this *BagMgr) AddMailByItem(platform string, srcId int32, srcName string, snid int32, showId int64, items []int32) {
	logger.Logger.Trace("AddMailByItem:", srcId, srcName, items)
	var newMsg *model.Message
	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		content := i18n.Tr("languages", "GiftMail", srcName, srcName, srcName, srcName)
		title := i18n.Tr("languages", "GiftMailTitle")
		newMsg = model.NewMessageByPlayer("", 1, srcId, srcName, snid, model.MSGTYPE_ITEM, title, content,
			0, 0, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", items, platform, showId)
		return model.InsertMessage(platform, newMsg)
	}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
		if data == nil {
			p := PlayerMgrSington.GetPlayerBySnId(snid)
			if p != nil {
				p.AddMessage(newMsg)
			}
		}
	}), "AddMailByItem").Start()
}

// VerifyUpJybInfo 兑换礼包
func (this *BagMgr) VerifyUpJybInfo(p *Player, args *model.VerifyUpJybInfoArgs) {

	type VerifyInfo struct {
		jyb *model.JybInfo
		err error
	}
	pack := &playerproto.SCPlayerSetting{
		OpRetCode: playerproto.OpResultCode_OPRC_Sucess,
	}
	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		jyb, err := model.VerifyUpJybInfo(args)
		info := &VerifyInfo{
			jyb: jyb,
			err: err,
		}
		return info
	}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
		if data != nil && data.(*VerifyInfo) != nil {
			jyb := data.(*VerifyInfo).jyb
			err := data.(*VerifyInfo).err
			if jyb != nil && jyb.Award != nil { // 领取到礼包
				pack.GainItem = &playerproto.JybInfoAward{}
				if jyb.Award.Item != nil {
					if len(jyb.Award.Item) > 0 {
						items := make([]*Item, 0)
						for _, v := range jyb.Award.Item {
							items = append(items, &Item{
								ItemId:     v.ItemId,  // 物品id
								ItemNum:    v.ItemNum, // 数量
								ObtainTime: time.Now().Unix(),
							})
						}
						if _, code := this.AddJybBagInfo(p, items, 0, common.GainWay_ActJybAward, "system", "礼包码兑换"); code != bag.OpResultCode_OPRC_Sucess { //TODO 添加失败 要回退礼包
							logger.Logger.Errorf("CSPlayerSettingHandler AddJybBagInfo err", code)
							pack.OpRetCode = playerproto.OpResultCode_OPRC_Error
							proto.SetDefaults(pack)
							p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack)
						} else {
							for _, v := range items {
								itemData := srvdata.PBDB_GameItemMgr.GetData(v.ItemId)
								if itemData != nil {
									BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, v.ItemId, itemData.Name, int64(v.ItemNum), "礼包领取")
								}
							}
							PetMgrSington.CheckShowRed(p)
						}
						p.dirty = true
					}
				}
				for _, v := range jyb.Award.Item {
					//if _, code := BagMgrSingleton.UpBagInfo(p.SnId, p.Platform, v.ItemId, v.ItemNum); code == bag.OpResultCode_OPRC_Sucess { // 需修改
					pack.GainItem.ItemId = append(pack.GainItem.ItemId, &playerproto.ItemInfo{
						ItemId:  v.ItemId,
						ItemNum: v.ItemNum,
					})
				}
				if jyb.Award.Coin > 0 {
					p.AddCoin(jyb.Award.Coin, 0, common.GainWay_ActJybAward, "system", "礼包码兑换")
					LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ActJybAward, model.SystemFreeGive_CoinType_Coin, int64(jyb.Award.Coin)))
				}
				if jyb.Award.Diamond > 0 {
					p.AddDiamond(jyb.Award.Diamond, 0, common.GainWay_ActJybAward, "system", "礼包码兑换")
					LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ActJybAward, model.SystemFreeGive_CoinType_Diamond, int64(jyb.Award.Diamond)))
				}
				p.dirty = true
				pack.GainItem.Coin = jyb.Award.Coin
				pack.GainItem.Diamond = jyb.Award.Diamond
			} else {
				switch err.Error() {
				case model.ErrJybISReceive.Error():
					pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_Receive
				case model.ErrJYBPlCode.Error():
					pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_CodeExist
				case model.ErrJybTsTimeErr.Error():
					pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_TimeErr
				default:
					pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_CodeErr
				}
			}
		} else {
			proto.SetDefaults(pack)
			p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack)
		}
		proto.SetDefaults(pack)
		p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack)
	}), "VerifyUpJybInfo").Start()
	// 先检查玩家背包是否足够

}

// SyncBagData 通知玩家背包数据变化
func (this *BagMgr) SyncBagData(snid int32, changeItemIds ...int32) {
	p := PlayerMgrSington.GetPlayerBySnId(snid)
	if p == nil || p.IsRob {
		return
	}

	var itemInfos []*bag.ItemInfo
	for _, itemId := range changeItemIds {
		itemInfo := this.GetItem(snid, itemId)
		if itemInfo != nil {
			itemInfos = append(itemInfos, &bag.ItemInfo{
				ItemId:  itemInfo.ItemId,
				ItemNum: itemInfo.ItemNum,
				//Name:           itemInfo.Name,
				//Classify:       itemInfo.Classify,
				//Type:           itemInfo.Type,
				//Effect0:        itemInfo.Effect0,
				//Effect:         itemInfo.Effect,
				//SaleType:       itemInfo.SaleType,
				//SaleGold:       itemInfo.SaleGold,
				//Composition:    itemInfo.Composition,
				//CompositionMax: itemInfo.CompositionMax,
				//Time:           itemInfo.Time,
				//Location:       itemInfo.Location,
				//Describe:       itemInfo.Describe,
				ObtainTime: itemInfo.ObtainTime,
			})
			if itemInfo.ItemId == VCard {
				FriendMgrSington.UpdateInfo(p.Platform, p.SnId)
			}
		}
	}
	p.SyncBagData(itemInfos)
}

func (this *BagMgr) Update() {
}

func (this *BagMgr) Shutdown() {
	module.UnregisteModule(this)
}

//========================implement IPlayerLoad ==============================

func (this *BagMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay {
	data := model.GetBagInfo(snid, platform)
	return &internal.PlayerLoadReplay{
		Platform: platform,
		Snid:     snid,
		Err:      nil,
		Data:     data,
	}
}

func (this *BagMgr) Callback(player any, ret *internal.PlayerLoadReplay) {
	if ret == nil || ret.Data == nil {
		return
	}

	bagInfo, ok := ret.Data.(*model.BagInfo)
	if !ok || bagInfo == nil {
		return
	}

	//数据表 数据库数据相结合
	newBagInfo := &BagInfo{
		SnId:     ret.Snid,
		Platform: ret.Platform,
		BagItem:  make(map[int32]*Item),
	}
	for k, bi := range bagInfo.BagItem {
		item := srvdata.PBDB_GameItemMgr.GetData(bi.ItemId)
		if item != nil {
			if bi.ItemNum > 0 {
				newBagInfo.BagItem[k] = &Item{
					ItemId:     bi.ItemId,
					ItemNum:    bi.ItemNum,
					ObtainTime: bi.ObtainTime,
				}
			}
		} else {
			logger.Logger.Error("InitBagInfo err: item is nil. ItemId:", bi.ItemId)
		}
	}
	this.PlayerBag[ret.Snid] = newBagInfo
}

func (this *BagMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay {
	// 查询最近游戏
	gameID := model.GetRecentGame(platform, snid)
	return &internal.PlayerLoadReplay{
		Platform: platform,
		Snid:     snid,
		Err:      nil,
		Data:     gameID,
	}
}

func (this *BagMgr) CallbackAfter(ret *internal.PlayerLoadReplay) {
	if ret == nil {
		return
	}
	if ret.Err != nil {
		logger.Logger.Error("BagMgr LoadAfter err:", ret.Err)
		return
	}
	p := PlayerMgrSington.GetPlayerBySnId(ret.Snid)
	if p != nil {
		p.GameID = ret.Data.([]int32)
	}
}

func (this *BagMgr) Save(platform string, snid int32, isSync, force bool) {
	bagInfo := this.PlayerBag[snid]
	logger.Logger.Trace("SaveBagData:", bagInfo)

	if bagInfo == nil || (!bagInfo.dirty && !force) {
		return
	}

	type BagInfoMap struct {
		SnId     int32   //玩家账号直接在这里生成
		Platform string  //平台
		BagItem  []*Item //背包数据  key为itemId
	}
	// biMap 数据拷贝
	var biMap = BagInfoMap{
		SnId:     bagInfo.SnId,
		Platform: bagInfo.Platform,
	}
	for _, v := range bagInfo.BagItem {
		biMap.BagItem = append(biMap.BagItem, &Item{ItemId: v.ItemId, ItemNum: v.ItemNum, ObtainTime: v.ObtainTime})
	}

	var err error
	f := func() {
		newBagInfo := &model.BagInfo{
			SnId:     biMap.SnId,
			Platform: biMap.Platform,
			BagItem:  make(map[int32]*model.Item),
		}
		for _, v := range biMap.BagItem {
			newBagInfo.BagItem[v.ItemId] = &model.Item{ItemId: v.ItemId, ItemNum: v.ItemNum, ObtainTime: v.ObtainTime}
		}
		err = model.UpBagItem(newBagInfo)
	}

	cf := func() {
		if err == nil {
			bagInfo.dirty = false
		}
	}

	if isSync {
		f()
		cf()
		return
	}

	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		f()
		return nil
	}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
		cf()
	}), "SaveBagData").StartByFixExecutor("SnId:" + strconv.Itoa(int(snid)))
}

func (this *BagMgr) Release(platform string, snid int32) {
	delete(this.PlayerBag, snid)
}

func init() {
	module.RegisteModule(BagMgrSingleton, time.Second, 0)
	internal.RegisterPlayerLoad(BagMgrSingleton)
}
