package service

import (
	"blazing/common/data/xmlres"
	"blazing/logic/service/fight/info"
	"blazing/modules/blazing/model"
	"fmt"
	"math"
	"math/rand"
	"time"

	"github.com/gogf/gf/v2/util/gconv"
	"github.com/jinzhu/copier"
	"github.com/mohae/deepcopy"
	"github.com/panjf2000/ants/v2"
)

type PlayerI interface {
	ID() uint32
	MapID() uint32
	GetAction()
	GetPetInfo() []model.PetInfo
	SendPack(b []byte) error
	SendReadyToFightInfo(info.FightStartOutboundInfo)
	SendNoteReadyToFightInfo(info.NoteReadyToFightInfo)
	SendFightEndInfo(info.FightOverInfo)
	SendAttackValue(info.AttackValueS)
	SendChangePet(info.ChangePetInfo)
	SetFightC(*FightC)
}
type Input struct {
	CanChange  bool                  //是否可以死亡切换CanChange
	CurrentPet *info.BattlePetEntity //当前精灵
	AllPet     []*info.BattlePetEntity
	Player     PlayerI
	Finished   bool //是否加载完成
}

func (i *Input) GetPetInfo() *info.BattlePetEntity {

	return i.CurrentPet

}

type FightC struct {
	Info info.NoteReadyToFightInfo

	OwnerID    uint32 // 战斗发起者ID
	Our        *Input //始终等于房主ID
	Opp        *Input //对手ID
	rand       *rand.Rand
	StartTime  time.Time
	actionChan chan info.BattleActionI // 所有操作统一从这里进入
	Round      int                     //回合数
	EffectS    info.NodeManager        //effects容器
	First      *BPET
	Second     *BPET
}

func (f *FightC) GetInputByPlayer(c PlayerI, isOpposite bool) *Input {
	// 判断当前玩家是否为我方玩家
	isOurPlayer := c == f.Our.Player

	// 逻辑简化：当"是否为我方玩家"与"是否需要对方"状态一致时，返回对方输入，否则返回我方输入
	if isOurPlayer == isOpposite {
		return f.Opp
	}
	return f.Our
}

func (f *FightC) GetInputByAction(c info.BattleActionI, isOpposite bool) *Input {
	// 判断动作所属玩家是否为我方
	isOurAction := c.GetPlayerID() == f.Our.Player.ID()

	// 根据isOpposite决定是否返回相反方向的输入
	if isOurAction == !isOpposite {
		return f.Our
	}
	return f.Opp
}

// 玩家逃跑
func (f *FightC) Escape(c PlayerI) {
	ret := &info.EscapeAction{
		PlayerID: c.ID(),
		Reason: info.FightOverInfo{

			Reason: uint32(info.BattleOverReason.PlayerEscape),
		},
	}

	f.actionChan <- ret
}

// 切换精灵  主动和被驱逐
func (f *FightC) ChangePet(c PlayerI, id int32) {
	ret := &info.ActiveSwitchAction{
		PlayerID: c.ID(),
	}

	rett := func(t *Input) *info.BattlePetEntity {
		for _, v := range t.AllPet {
			if v.Info.CatchTime == uint32(id) {
				copier.Copy(&ret.Reason, &v)
				ret.Reason.UserId = c.ID()
				return v
			}

		}
		return nil
	}

	f.GetInputByPlayer(c, false).CurrentPet = rett(f.GetInputByPlayer(c, false))

	f.actionChan <- ret
}

// 玩家使用技能
func (f *FightC) UseSkill(c PlayerI, id int32) {
	ret := &info.SelectSkillAction{
		PlayerID: c.ID(),
	}
	ret.PetInfo = f.GetInputByPlayer(c, false).CurrentPet

	for _, v := range ret.PetInfo.Skills {

		if v != nil && v.ID == int(id) {
			ret.Skill = v
		}

	}
	f.actionChan <- ret
}

// 战斗准备
func (f *FightC) ReadyFight(c PlayerI) {
	rett := info.FightStartOutboundInfo{}

	copier.Copy(&rett.Info1, &f.Info.OurPetList[0]) // 复制自己的信息
	copier.Copy(&rett.Info2, &f.Info.OpponentPetList[0])
	rett.Info1.UserID = f.Info.OurInfo.UserID //
	rett.Info2.UserID = f.Info.OpponentInfo.UserID

	rrsult := func() { //传回函数

		f.Our.Player.SendReadyToFightInfo(rett)
		f.Opp.Player.SendReadyToFightInfo(rett)
	}

	switch f.Info.FightId {
	case 1: // 1v1

		f.GetInputByPlayer(c, false).Finished = true
		if f.GetInputByPlayer(c, true).Finished {
			rrsult()
		}

	case 3: // 野怪战斗

		//判断捕捉率大于0
		if gconv.Int(xmlres.PetMAP[int(f.Info.OpponentPetList[0].ID)].CatchRate) > 0 {
			rett.Info2.Catchable = 1
		}

		rrsult()
	}
}

var Fightpool *ants.Pool

func init() {
	Fightpool, _ = ants.NewPool(math.MaxInt32)
	//defer p.Release()
}

// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
func NewFight(i info.NoteReadyToFightInfo, p1 PlayerI, p2 PlayerI) *FightC {
	f := &FightC{}
	f.OwnerID = p1.ID()
	f.Info = i                                                       //房主
	seed := f.StartTime.UnixNano() ^ int64(p1.ID()) ^ int64(p2.ID()) // ^ int64(f.Round) // 用异或运算混合多维度信息
	f.rand = rand.New(rand.NewSource(seed))

	f.Our = &Input{
		CurrentPet: info.CreateBattlePetEntity(p1.GetPetInfo()[0], f.rand),
		Player:     p1,
	}
	for k, v := range p1.GetPetInfo() {
		if i.MAXPET == 0 || k < int(i.MAXPET) { //todo 待测试
			f.Our.AllPet = append(f.Our.AllPet, info.CreateBattlePetEntity(v, f.rand))
		}

	}

	f.Opp = &Input{
		CurrentPet: info.CreateBattlePetEntity(p2.GetPetInfo()[0], f.rand),
		Player:     p2,
	}
	for k, v := range p2.GetPetInfo() {
		if i.MAXPET == 0 || k < int(i.MAXPET) {
			f.Opp.AllPet = append(f.Opp.AllPet, info.CreateBattlePetEntity(v, f.rand))
		}
	}

	p1.SetFightC(f) //给我方追加战斗容器
	p2.SetFightC(f) //给对方增加战斗容器
	defer func() {
		rr := Fightpool.Submit(f.battleLoop)
		if rr != nil {
			panic(rr)
		}
		f.Broadcast(func(ff *Input) {

			ff.Player.SendNoteReadyToFightInfo(i)
		})
	}()
	//go f.battleLoop() // 起战斗循环
	return f
}

// 广播,并是否结束回合
func (f *FightC) Broadcast(t func(ff *Input)) {

	t(f.Our)

	t(f.Opp)

}

// 战斗回合循环
func (f *FightC) battleLoop() {
	f.StartTime = time.Now()
	f.actionChan = make(chan info.BattleActionI, 2) // 初始化全局操作通道
	fmt.Println("战斗开始精灵", f.Our.Player.GetPetInfo()[0].CatchTime)
	//战斗开始前操作

	for {

		f.Round++ //回合数自增

		if f.Round > 250 { //回合数超过250,战斗平局结束

		}
		actions := make(map[uint32]info.BattleActionI) // 每个玩家一条记录
		timeout := time.After(60 * time.Second)

		for len(actions) < 2 {
			select {
			case action := <-f.actionChan:
				// 只接受有效玩家 ID\
				if action == nil {
					continue
				}

				if action.GetPlayerID() != f.Our.Player.ID() && action.GetPlayerID() != f.Opp.Player.ID() {
					continue
				}

				if a, isExpelled := action.(*info.ActiveSwitchAction); isExpelled {
					//fmt.Println("对方死亡切换")
					f.Broadcast(func(ff *Input) {

						ff.Player.SendChangePet(a.Reason)
					})

					if f.GetInputByAction(action, false).CanChange {
						//如果是被动切换,不计入回合结算
						f.GetInputByAction(action, false).CanChange = false
						continue

					}

				}

				if action.GetPlayerID() != 0 && f.Info.FightId == 3 {

					f.GetInputByAction(action, true).Player.GetAction()

				}

				// 如果该玩家已经提交过，就忽略重复动作
				if _, exists := actions[uint32(action.GetPlayerID())]; exists {
					fmt.Printf("玩家%d 已经提交过动作，忽略重复\n", action.GetPlayerID())
					continue
				}

				actions[uint32(action.GetPlayerID())] = action
				fmt.Println("玩家%d 执行动作", action.GetPlayerID())

			case <-timeout:
				fmt.Println("回合操作超时")

				if _, exists := actions[f.Our.Player.ID()]; !exists {
					actions[f.Our.Player.ID()] = &info.SystemGiveUpAction{PlayerID: f.Our.Player.ID()} //系统选择出手
				}
				if _, exists := actions[f.Opp.Player.ID()]; !exists {
					actions[f.Opp.Player.ID()] = &info.SystemGiveUpAction{PlayerID: f.Opp.Player.ID()} //系统选择出手
				}
			}
		}

		// 双方动作齐了，取出来结算
		p1Action := actions[f.Our.Player.ID()]
		p2Action := actions[f.Opp.Player.ID()]
		fmt.Println("开始结算回合")
		var BattleActionI [2]info.BattleActionI
		BattleActionI[0], BattleActionI[1] = info.Compare(p1Action, p2Action)

		switch faction := BattleActionI[0].(type) {
		case *info.EscapeAction: //优先逃跑

			f.Broadcast(func(ff *Input) {
				ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
			})
			break

		case *info.PlayerOfflineAction: //单方掉线

			f.Broadcast(func(ff *Input) {
				ff.Player.SendFightEndInfo(faction.Reason) //广播逃跑原因
			})
			break
		case *info.ActiveSwitchAction: //切换上场的
			f.enterturn(BattleActionI[1], nil) //切换,相当于后手直接出手

		case *info.SelectSkillAction: //选择技能
			//回合前操作,比如挂载buff
			f.enterturn(BattleActionI[0], BattleActionI[1])

			//回合后操作

		}

	}

}

type BPET struct {
	*Input

	//*info.SelectSkillAction     //技能实体
	*info.BattlePetEntity //精灵实体
	*info.AttackValue
	*FightC
	Damage uint32 //造成伤害
}

// 被击败的ID
func (b *BPET) IsWin(cache uint32) bool {

	fmt.Println("当前回合", b.FightC.Round, "开始检查战斗是否结束")

	var tt []info.ReadyFightPetInfo
	bbb := b.FightC.Info.OurPetList

	if b.Input.Player.ID() == b.FightC.OwnerID { //如果是房主
		bbb = b.FightC.Info.OpponentPetList
	} else {
		bbb = b.FightC.Info.OurPetList
	}

	for _, v := range bbb {
		if v.CatchTime == cache {
			v.NotAlive = true

		}
		tt = append(tt, v)
	}

	for _, v := range tt {
		if !v.NotAlive { //如果存活
			return false
		}
	}
	return true
}

// 解析并 施加effect
func (f *FightC) parseskill(id *info.SelectSkillAction) {
	temparg := id.Skill.SideEffectArgS
	for _, v := range id.Skill.SideEffectS {

		t, ok := info.NodeM[v]

		if ok { //获取成功

			args := t.GetArgSize()
			t.SetArgs(temparg[:args]) //设置入参
			//如果不是是房主方,说明施加的对象是反的,比如本来是false,实际上是给邀请方施加的
			//所以这里要对target取反
			if id.GetPlayerID() != f.OwnerID {
				t.SetTarget(!t.Target())
			}
			temparg = temparg[args:]
			f.EffectS.AddEffect(deepcopy.Copy(t).(info.Effect))

		}

	}
}

// 创建BPET实例的辅助函数
func (f *FightC) newBPET(input *Input) *BPET {
	return &BPET{
		FightC:          f,
		Input:           input,
		BattlePetEntity: input.CurrentPet,
		AttackValue:     info.NewAttackValue(input.Player.ID()),
		Damage:          0,
	}
}
func (f *FightC) initAttackers(fattack, sattack info.BattleActionI) {
	// 伤害值

	// 根据攻击方归属设置当前战斗的主/次攻击方属性
	var first, second *Input // 定义临时变量存储主/次攻击方
	if fattack.GetPlayerID() == f.OwnerID {
		first = f.Our  // 攻击方为我方时，主攻击方是我方
		second = f.Opp // 次攻击方是对方
	} else {
		first = f.Opp  // 攻击方为对方时，主攻击方是对方
		second = f.Our // 次攻击方是我方
	}

	// 统一赋值，减少重复代码
	f.First = f.newBPET(first)
	f.Second = f.newBPET(second)

	fmt.Println("先手", f.First.CurrentPet.Info.CatchTime, "后手", f.Second.CurrentPet.Info.CatchTime)

	// TODO: 在这里调用技能结算逻辑
}

// 处理技能攻击逻辑
func (f *FightC) processSkillAttack(attacker, defender *BPET, skill *info.SelectSkillAction) {
	f.parseskill(skill)
	spower := skill.Skill.CalculatePower(defender.Type().ID)
	attacker.Damage = spower

	// 记录技能信息
	attacker.AttackValue.SkillID = uint32(skill.Skill.ID)
	attacker.AttackValue.AttackTime = skill.Skill.AttackTime()
	// 判断是否暴击
	if _, ok := skill.Skill.IsCritical(); ok {
		attacker.AttackValue.IsCritical = 1
	}

	// 扣减防御方血量
	if attacker.Damage > defender.CurrentPet.Info.Hp {
		defender.CurrentPet.Info.Hp = 0
	} else {
		defender.CurrentPet.Info.Hp -= attacker.Damage
	}
}

// 检查战斗是否结束
func (f *FightC) winner(winner *BPET) func() {

	return func() {
		f.Broadcast(func(ff *Input) {
			ff.Player.SendFightEndInfo(info.FightOverInfo{
				WinnerId: winner.Player.ID(),
			})
		})
		close(f.actionChan)
	}
}
func (f *FightC) enterturn(fattack, sattack info.BattleActionI) {
	f.initAttackers(fattack, sattack) //初始化先后手
	skill, _ := fattack.(*info.SelectSkillAction)

	f.processSkillAttack(f.First, f.Second, skill)
	fmt.Printf("先手技能伤害: %v, 先手剩余血量: %v\n", f.First.Damage, f.First.CurrentPet.Info.Hp)
	fmt.Printf("%v 先手出招结束\n", f.First.CurrentPet.Info.CatchTime)
	if f.Second.CurrentPet.Info.Hp > 0 { //如果后手方没被打死
		skill, ok := sattack.(*info.SelectSkillAction)
		if ok {

			f.processSkillAttack(f.Second, f.First, skill)
			fmt.Printf("后手技能伤害: %v, 后手剩余血量: %v\n", f.Second.Damage, f.Second.CurrentPet.Info.Hp)
			fmt.Printf("%v 后手出招结束\n", f.Second.CurrentPet.Info.CatchTime)

		} //还有系统选择放弃出手的

		if f.First.CurrentPet.Info.Hp == 0 {
			f.First.CanChange = true                               //被打死就可以切精灵了
			if f.Second.IsWin(f.First.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
				defer f.winner(f.Second) //广播逃跑原因

			}
		}

	} else {
		f.Second.AttackValue = info.NewAttackValue(f.Second.Player.ID()) //已经被打死了,直接将后手方输出归0
		f.Second.CanChange = true                                        //被打死就可以切精灵了
		if f.First.IsWin(f.Second.CurrentPet.Info.CatchTime) {           //然后检查是否战斗结束
			defer f.winner(f.First) //广播逃跑原因

		}
	}

	f.Broadcast(func(ff *Input) {
		ret := info.AttackValueS{
			FAttack: *f.First.AttackValue,
			SAttack: *f.Second.AttackValue,
		}
		ret.FAttack.RemainHp = int32(f.First.CurrentPet.Info.Hp)

		ret.FAttack.LostHp = uint32(f.First.Damage) //先手方造成血量
		ret.SAttack.RemainHp = int32(f.Second.CurrentPet.Info.Hp)
		ret.SAttack.LostHp = uint32(f.Second.Damage) //后手方造成血量
		ff.Player.SendAttackValue(ret)
	})

}
