-- A faint is announced once, and charged to happiness once.
--
--   luajit tests/gen2_faint_once_test.lua
--
-- Found by the Gold route bot (tests/drivers/gold_bot.lua), whose logs are full
-- of this:
--
--   TYPHLOSION fainted!
--   TYPHLOSION fainted!
--   TYPHLOSION fainted!
--
-- Battle:resolveFaints' player arm is the only one that returns WITHOUT
-- changing whose turn it is: it emits `choose-switch` and waits for the caller
-- to pick a replacement, so the caller calls back in with the same mon still at
-- 0 HP and the whole arm ran again. The repeated line is cosmetic; the bug
-- underneath it is not, because `faintHappiness` sat on the same path and was
-- charged once per re-entry. A single faint cost two or three times the
-- happiness the cart takes -- engine/battle/core.asm runs its happiness arm
-- once per faint.
--
-- The enemy arm cannot do this: it either ends the battle or switches in the
-- next mon, so it never re-enters holding a fainted enemy.
package.path = "./?.lua;./?/init.lua;" .. package.path

local S = require("tests.harness").suite("gen2 faint once")
local check, eq = S.check, S.eq

local Battle = require("src.battle.gen2.Battle")

-- resolveFaints' player arm reads self.player/self.party/self.enemy and calls
-- out to emit, faintHappiness and firstHealthy, so it runs against a stub.
local function stub(party)
  local st = {
    said = {},
    happiness = 0,
  }
  st.player = party[1]
  st.party = party
  st.enemy = { hp = 50, level = 50 }
  st.emit = function(_, ev) st.said[#st.said + 1] = ev.kind end
  st.faintHappiness = function() st.happiness = st.happiness + 1 end
  st.monName = function(_, m) return m.name or "MON" end
  return setmetatable(st, { __index = Battle })
end

local function countKind(said, kind)
  local n = 0
  for _, k in ipairs(said) do if k == kind then n = n + 1 end end
  return n
end

local function countFaints(said)
  return countKind(said, "faint")
end

-- The reported case: a fainted lead with a healthy mon behind it. The caller
-- drives the switch, so it calls resolveFaints repeatedly in the meantime.
do
  local b = stub({
    { hp = 0, level = 88, name = "TYPHLOSION" },
    { hp = 40, level = 30, name = "SLOWPOKE" },
  })
  eq(b:resolveFaints(), false, "the battle is not over, there is a mon left")
  eq(b:resolveFaints(), false, "and calling again while the switch is pending")
  eq(b:resolveFaints(), false, "and again")
  eq(countFaints(b.said), 1, "the faint is announced exactly once")
  eq(b.happiness, 1, "and happiness is charged exactly once")
  -- HandlePlayerMonFaint runs ForcePlayerMonChoice once (core.asm:2543) and
  -- the turn loop never comes back for a second answer.  Three prompts in the
  -- queue reopened the party list on top of the pick that had already been
  -- made, which is why the switch looked like it took several attempts.
  eq(countKind(b.said, "choose-switch"), 1, "and the party list is asked for "
    .. "exactly once")
end

-- The replacement announces its own faint normally: the guard is keyed on the
-- mon, not on the battle.
do
  local slowpoke = { hp = 40, level = 30, name = "SLOWPOKE" }
  local b = stub({ { hp = 0, level = 88, name = "TYPHLOSION" }, slowpoke })
  b:resolveFaints()
  b:resolveFaints()
  -- Battle:switch is what clears the guards; do what it does.
  b.faintAnnounced = nil
  b.pendingSwitch = nil
  b.player = slowpoke
  slowpoke.hp = 0
  eq(b:resolveFaints(), true, "no healthy mon left ends the battle")
  eq(countFaints(b.said), 2, "the second mon's faint is announced too")
  eq(b.happiness, 2, "and charged")
end

-- A wipe still reports the wipe, once.
do
  local b = stub({ { hp = 0, level = 88, name = "TYPHLOSION" } })
  eq(b:resolveFaints(), true, "a lone fainted mon ends the battle")
  eq(b.outcome, "lose", "as a loss")
  eq(countFaints(b.said), 1, "announced once")
end

S.finish()
