package fight

import (
	"blazing/logic/service/common"
	"blazing/logic/service/fight/info"
	"blazing/logic/service/fight/input"
	"blazing/logic/service/player"
	"fmt"
	"math/rand"
	"sort"
	"time"

	"github.com/jinzhu/copier"
	"github.com/mohae/deepcopy"
	"github.com/shopspring/decimal"
)

type FightC struct {
	Info info.NoteReadyToFightInfo

	ownerID uint32       // 战斗发起者ID
	Our     *input.Input //始终等于房主ID
	Opp     *input.Input //对手ID
	Switch  []*ActiveSwitchAction

	rand       *rand.Rand
	StartTime  time.Time
	actionChan chan BattleActionI // 所有操作统一从这里进入
	Round      int                //回合数

	First      *input.Input
	Second     *input.Input
	closefight bool
}

func (f *FightC) Ownerid() uint32 {

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

	// 当isOurPlayer与isOpposite值不同时返回我方，相同时返回对方
	if isOurPlayer != isOpposite {
		return f.Our
	}
	return f.Opp
}

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

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

// 玩家使用技能
func (f *FightC) GetCurrPET(c common.PlayerI) *info.BattlePetEntity {
	if f.Our.Player.GetInfo().UserID == c.GetInfo().UserID {
		return f.Our.CurrentPet
	} else {
		return f.Opp.CurrentPet
	}

}

// 获取随机数
func (f *FightC) GetRand() *rand.Rand {

	return f.rand

}

func (f *FightC) initplayer(c common.PlayerI, opp bool) {

	temp := input.NewInput(f, c)
	temp.AllPet = make([]*info.BattlePetEntity, 0)

	for i := 0; i < len(c.GetInfo().PetList); i++ {
		if f.Info.MAXPET == 0 || i < int(f.Info.MAXPET) {

			temp.AllPet = append(temp.AllPet, info.CreateBattlePetEntity(&c.GetInfo().PetList[i], f.rand))
		}

	}

	sort.Slice(temp.AllPet, func(i, j int) bool {
		x, y := temp.AllPet[i], temp.AllPet[j]
		// 若x血量>0且y血量=0，则x排在前
		if x.Info.Hp > 0 && y.Info.Hp <= 0 {
			return true
		}
		// 若x血量=0且y血量>0，则x排在后
		if x.Info.Hp <= 0 && y.Info.Hp > 0 {
			return false
		}
		// 同类型（都>0或都=0）保持原有顺序
		return i < j
	})
	if opp {
		f.Opp = temp //这里是对方的
		copier.Copy(&f.Info.OpponentInfo, f.Opp.Player.GetInfo())
		f.Info.OpponentPetList = make([]info.ReadyFightPetInfo, len(temp.AllPet))
		for i := 0; i < len(temp.AllPet); i++ {

			err := copier.CopyWithOption(&f.Info.OpponentPetList[i], temp.AllPet[i].Info, copier.Option{IgnoreEmpty: true, DeepCopy: true})
			if err != nil {
				panic(err)
			}
		}
	} else {
		f.Our = temp
		copier.Copy(&f.Info.OurInfo, f.Our.Player.GetInfo())
		f.Info.OurPetList = make([]info.ReadyFightPetInfo, len(temp.AllPet))
		for i := 0; i < len(temp.AllPet); i++ {

			err := copier.CopyWithOption(&f.Info.OurPetList[i], &temp.AllPet[i].Info, copier.Option{IgnoreEmpty: true, DeepCopy: true})
			if err != nil {
				panic(err)
			}
		}
	}

	for _, v := range temp.AllPet {
		if v.Info.Hp == 0 {
			v.NotAlive = true

		}

	}

	temp.CurrentPet = temp.AllPet[0]

}

// 创建新战斗,邀请方和被邀请方,或者玩家和野怪方
func NewFight(i info.EnumBattleMode, p1 common.PlayerI, p2 common.PlayerI) *FightC {
	f := &FightC{}
	f.ownerID = p1.GetInfo().UserID
	f.Info.FightId = i //房主
	switch i {
	case info.BattleMode.PVP_6V6:
		f.Info.MAXPET = 6
	case info.BattleMode.PVP_1V1:
		f.Info.MAXPET = 1
	}
	seed := f.StartTime.UnixNano() ^ int64(p1.GetInfo().UserID) ^ int64(p2.GetInfo().UserID) // ^ int64(f.Round) // 用异或运算混合多维度信息
	f.rand = rand.New(rand.NewSource(seed))
	f.Info = info.NoteReadyToFightInfo{

		FightId: i,
	}
	f.initplayer(p1, false)

	f.initplayer(p2, true)

	defer func() {
		rr := Fightpool.Submit(f.battleLoop)
		if rr != nil {
			panic(rr)
		}
		f.Broadcast(func(ff *input.Input) {

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

// 被击败的ID
func (b *FightC) IsWin(c *input.Input, cache uint32) bool {

	var tt []*info.BattlePetEntity
	bbb := b.Our.AllPet

	if c.Player.GetInfo().UserID == b.ownerID { //如果是房主
		bbb = b.Opp.AllPet
	}

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

		}
		tt = append(tt, v)
	}

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

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

	t(f.Our)
	t(f.Opp)

}

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

	for {

		if f.closefight { //回合数超过250,战斗平局结束f.Round > 250 ||
			close(f.actionChan)
			break
		}

		f.Round++                                 //回合数自增
		actions := make(map[uint32]BattleActionI) // 每个玩家一条记录
		timeout := time.After(60 * time.Second)

		for len(actions) < 2 {
			select {
			case action, ok := <-f.actionChan:
				// 只接受有效玩家 ID\
				if action == nil {
					continue
				}
				if !ok {
					fmt.Println("战斗结束")
					break
				}

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

				if a, isExpelled := action.(*ActiveSwitchAction); isExpelled {

					f.Broadcast(func(ff *input.Input) {
						if ff.Player.GetInfo().UserID == a.PlayerID { //先给自身广播
							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("玩家%i 执行动作", action.GetPlayerID())

			case <-timeout:

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

					actions[f.Opp.Player.GetInfo().UserID] = &SystemGiveUpAction{BaseAction: NewBaseAction(f.Opp.Player.GetInfo().UserID)} //系统选择出手
				}

			}
		}

		// 双方动作齐了，取出来结算
		//todo 如果一方没有选择,实际上就是后端判断PP是否还有,前端是直接发的
		p1Action := actions[f.Our.Player.GetInfo().UserID]
		p2Action := actions[f.Opp.Player.GetInfo().UserID]
		fmt.Println("开始结算回合")

		var BattleActionI [2]BattleActionI
		BattleActionI[0], BattleActionI[1] = f.Compare(p1Action, p2Action)

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

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

		case *PlayerOfflineAction: //单方掉线

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

		case *ActiveSwitchAction: //切换上场的,切换方放弃出手
			f.enterturn(BattleActionI[1], &SystemGiveUpAction{BaseAction: NewBaseAction(BattleActionI[0].GetPlayerID())}) //切换,相当于后手直接出手

		case *UseItemAction: //使用道具
			//fmt.Println(faction.ItemID)

			switch {
			case faction.ItemID >= 30001 && faction.ItemID <= 300010: //胶囊

				//todo 将血量和技能pp传回enterturn
				tt, ok := f.Our.Player.(*player.Player)
				mo, ism := f.Opp.Player.(*player.AI_player)

				if ok && ism && mo.CanCapture { //如果获取玩家

					ok, _ := f.Our.Capture(f.Opp.CurrentPet, faction.ItemID, -1)
					if ok { //todo 待补充
						tt.Service.PetAdd(*f.Opp.CurrentPet.Info)
						tt.CatchPetInfo(info.CatchMonsterOutboundInfo{
							CatchTime: uint32(f.Opp.CurrentPet.Info.CatchTime),
							PetId:     uint32(f.Opp.CurrentPet.ID),
						})
						tt.SendFightEndInfo(info.FightOverInfo{

							WinnerId: f.ownerID,
						})
						f.closefight = true
					} else {
						tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})
					}

				} else { //说明不是可以捕捉的
					tt.CatchPetInfo(info.CatchMonsterOutboundInfo{})

				}

				// 当 ItemID 在 30001-300010 之间时执行的逻辑
				fmt.Println("ItemID 在范围内")
			case faction.ItemID == 300001:
				// 原来的单个值判断（如果还需要保留）
				fmt.Println("ItemID 是 300001")
			default:
				// 其他情况
				fmt.Println("ItemID 不在指定范围内")
			}
			f.enterturn(BattleActionI[1], &SystemGiveUpAction{BaseAction: NewBaseAction(BattleActionI[0].GetPlayerID())}) //切换,相当于后手直接出手
		default: //选择技能或者放弃出手
			//回合前操作,比如挂载buff

			f.enterturn(BattleActionI[0], BattleActionI[1])
			//回合后操作

		}

	}

}

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

		t, ok := input.NodeM[v+1000000]

		if ok { //获取成功

			args := t.GetArgSize()
			eff := deepcopy.Copy(t).(input.Effect)
			eff.SetArgs(temparg[:args]) //设置入参

			temparg = temparg[args:]
			if t.GetOwner() { //如果取反,说明是给对方添加的回合效果
				//实际上,owner永远为反,说明是对方给我添加的
				defender.AddEffect(eff)
			} else {
				attacker.AddEffect(eff)
			}

		}

	}
}

func (f *FightC) initAttackers(fattack BattleActionI) {
	// 伤害值

	// 根据攻击方归属设置当前战斗的主/次攻击方属性

	if fattack.GetPlayerID() == f.ownerID {
		f.First, f.Second = f.Our, f.Opp // 攻击方为我方时，主攻击方是我方

	} else {
		f.First, f.Second = f.Opp, f.Our // 攻击方为对方时，主攻击方是对方

	}

	// 统一赋值，减少重复代码
	f.First.InitAttackValue()
	f.Second.InitAttackValue()

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

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

// 处理技能攻击逻辑
func (f *FightC) processSkillAttack(attacker, defender *input.Input, a *SelectSkillAction) {

	// 记录技能信息
	attacker.AttackValue.SkillID = uint32(a.Skill.ID) //获取技能ID

	attacker.Exec(func(t input.Effect) bool { //计算命中

		t.IsHit(defender, a.Skill) //相当于先调整基础命中

		return attacker.AttackTime == 0 //等于0,继续处理
	})
	defender.Exec(func(t input.Effect) bool { //计算闪避
		t.TakeHit(attacker, a.Skill)

		return attacker.AttackTime > 0 //
	})
	attacker.AttackValue.AttackTime = a.Skill.AttackTime
	if attacker.AttackValue.AttackTime > 0 { //如果命中
		f.parseskill(attacker, defender, a) //命中后解析effect
		spower := a.Skill.CalculatePower(defender.CurrentPet)
		attacker.Damage = spower
		attacker.Exec(func(t input.Effect) bool { //计算暴击率加成

			t.IsCrit(defender, a.Skill)
			attacker.AttackValue.IsCritical = a.Skill.Crit
			return attacker.AttackValue.IsCritical == 0
		})
		if attacker.AttackValue.IsCritical == 1 {
			attacker.Damage = attacker.Damage.Mul(decimal.NewFromInt(2)) //暴击翻倍
		}
		if uint32(attacker.Damage.IntPart()) > defender.CurrentPet.Info.Hp {
			defender.CurrentPet.Info.Hp = 0
		} else {
			defender.CurrentPet.Info.Hp = defender.CurrentPet.Info.Hp - uint32(attacker.Damage.IntPart())
		}

		// 扣减防御方血量
	} //todo 处理未命中效果

}

//回合有先手方和后手方,同时有攻击方和被攻击方

func (f *FightC) enterturn(fattack, sattack BattleActionI) {

	if f.closefight { //战斗结束
		return
	}
	f.initAttackers(fattack) //初始化先后手
	var attacker, defender *input.Input

	//开始回合操作
	for i := 0; i < 2; i++ {

		var attackeraction BattleActionI
		if i == 0 { //
			attacker, defender = f.First, f.Second
			attackeraction = fattack
			attacker.First = true //先手技能
			//attacker.BattleActionI, defender.BattleActionI = fattack, sattack

		} else {
			attacker, defender = f.Second, f.First
			attackeraction = sattack
			attacker.First = false //先手技能

		}
		skill, ok := attackeraction.(*SelectSkillAction)

		if !ok || attacker.CurrentPet.Info.Hp <= 0 { //还有系统选择放弃出手的
			continue

		}

		//**回合开始前enterturn

		attacker.Exec(func(t input.Effect) bool { //计算命中

			//结算状态
			t.OnTurnStart(defender)
			return true
		})
		canuseskill := attacker.Exec(func(t input.Effect) bool { //这个是能否使用技能
			//结算状态
			return t.UseSkill(defender) //返回本身结算,如果false,说明不能使用技能了

		})

		if canuseskill { //可以使用技能

			f.processSkillAttack(attacker, defender, skill)

			skill.Skill.Info.PP-- //减少PP
		}
		fmt.Println(i,
			"玩家技能伤害:", attacker.Damage,
			"自身剩余血量:", attacker.CurrentPet.Info.Hp,
			"对手剩余血量:", defender.CurrentPet.Info.Hp,
		)
		if defender.CurrentPet.Info.Hp == 0 {
			defender.CanChange = true                                  //被打死就可以切精灵了
			if f.IsWin(attacker, defender.CurrentPet.Info.CatchTime) { //然后检查是否战斗结束
				var WinnerId uint32
				if i == 0 {
					WinnerId = f.First.Player.GetInfo().UserID
				} else {
					WinnerId = f.Second.Player.GetInfo().UserID
				}
				defer f.Broadcast(func(ff *input.Input) {
					//todo 将血量和技能pp传回enterturn

					ff.Player.SendFightEndInfo(info.FightOverInfo{
						WinnerId: WinnerId,
					})
				})
				defer func() {
					f.closefight = true
				}()

			}
		}

	}
	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.IntPart()) //先手方造成血量
	ret.SAttack.RemainHp = int32(f.Second.CurrentPet.Info.Hp)
	ret.SAttack.LostHp = uint32(f.Second.Damage.IntPart()) //后手方造成血量

	for i := 0; i < 6; i++ { //堆叠属性提升
		ret.FAttack.Prop[i] = int8(attacker.GetProp(i, true))
		ret.FAttack.Prop[i] = int8(defender.GetProp(i, true))
	}
	for i := 0; i < 20; i++ { //堆叠状态剩余回合
		ate, ok := attacker.GetStatusEffect(i)
		if ok {
			ret.FAttack.Status[i] = int8(ate.Duration(0))
		}

		dte, ok := defender.GetStatusEffect(i)
		if ok {
			ret.FAttack.Status[i] = int8(dte.Duration(0))
		}

	}

	f.Broadcast(func(ff *input.Input) {
		for _, v := range f.Switch {
			if ff.Player.GetInfo().UserID != v.PlayerID {
				ff.Player.SendChangePet(v.Reason)
			}

		}
		f.Switch = []*ActiveSwitchAction{}
		ff.Player.SendAttackValue(ret)
	})

}
