-- Parity test: Lance room walk-in stays on the floor, and defeat dialogue
-- includes the rival-became-champion after-battle text.
--
-- Sources: scripts/LancesRoom.asm (WalkToLance / LancesRoomLanceEndBattleScript),
-- text/LancesRoom.asm (_LancesRoomLanceAfterBattleText).
-- Self-contained: run via `luajit tests/parity_lance.lua`; also dofile'd
-- by tests/run_tests.lua's aggregator.
package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local Data = require("src.core.Data")
if not (Data.maps and Data.maps.LANCES_ROOM) then Data:load() end
local Font = require("src.render.Font")
if not pcall(Font.encode, "A") then Font.load(Data) end
local S = require("tests.harness").suite("parity lance")
local check, eq = S.check, S.eq

local mapScripts = require("data.scripts.init")
local hooks = mapScripts.get("LANCES_ROOM")
check(hooks and hooks.walkInRoute, "LANCES_ROOM exposes walkInRoute")

local D = { up = { 0, -1 }, down = { 0, 1 }, left = { -1, 0 }, right = { 1, 0 } }

-- === (1) walk-in RLE lands on the door-lock trigger (6,11) ===
do
  local x, y = 24, 16
  local steps = 0
  for _, seg in ipairs(hooks.walkInRoute) do
    local d = D[seg[1]]
    check(d ~= nil, "walk segment direction " .. tostring(seg[1]))
    for _ = 1, seg[2] do
      x, y = x + d[1], y + d[2]
      steps = steps + 1
    end
  end
  eq(x, 6, "walk-in ends at x=6")
  eq(y, 11, "walk-in ends at y=11 (door-lock / arena entry)")
  check(steps > 0, "walk-in has steps")
end

-- === (2) every cell of the route is walkable with the entrance open ===
do
  local Map = require("src.world.Map")
  local def = Data.maps.LANCES_ROOM
  local tileset = Data.tilesets[def.tileset]
  -- mutate a copy of the block list so we don't poison the registry
  local blocks = {}
  for i, b in ipairs(def.blocks) do blocks[i] = b end
  local copy = {}
  for k, v in pairs(def) do copy[k] = v end
  copy.blocks = blocks
  -- LanceShowOrHideEntranceBlocks with the door unlocked
  blocks[6 * def.width + 2 + 1] = 0x31
  blocks[6 * def.width + 3 + 1] = 0x32
  local map = Map.new(copy, tileset)
  local x, y = 24, 16
  check(map:isWalkableCell(x, y), "start (24,16) walkable")
  for _, seg in ipairs(hooks.walkInRoute) do
    local d = D[seg[1]]
    for _ = 1, seg[2] do
      x, y = x + d[1], y + d[2]
      check(map:isWalkableCell(x, y),
            ("walk-in cell (%d,%d) is floor, not void/wall"):format(x, y))
    end
  end
end

-- === (3) after-battle text names the rival as the real champion ===
do
  local after = Data.text._LancesRoomLanceAfterBattleText
  check(after ~= nil, "_LancesRoomLanceAfterBattleText extracted")
  check(after:find("ELITE", 1, true) or after:find("{RIVAL}", 1, true),
        "after text mentions rival / Elite Four")
  check(after:find("champion", 1, true) or after:find("CHAMPION", 1, true),
        "after text has the champion reveal")
  check(after:find("before you", 1, true) or after:find("before you!", 1, true)
        or after:find("FOUR before", 1, true),
        "after text says rival beat the Elite Four first")
  local header = Data:trainerHeader("LancesRoom", 1)
  check(header and header.after == "_LancesRoomLanceAfterBattleText",
        "trainer header wires the after-battle label")
  check(header and header.won == "_LancesRoomLanceEndBattleText",
        "trainer header wires the won label")
end

-- === (4) onStep win callback pushes the after text (not only won text) ===
do
  local pushed = {}
  local game = {
    data = Data,
    save = {
      flags = {},
      defeatedTrainers = {},
      player = { name = "RED", rival = "BLUE" },
    },
    stack = {
      push = function(_, state)
        pushed[#pushed + 1] = state
      end,
    },
  }
  local lance = {
    def = { name = "LANCESROOM_LANCE", index = 1,
            trainerClass = "OPP_LANCE", trainerParty = 1, text = 1 },
    id = "LANCES_ROOM:1",
    facePlayer = function() end,
  }
  local engaged = false
  local ow = {
    npcs = { lance },
    player = { cellX = 6, cellY = 2 },
    trainerDefeated = function(_, npc)
      return game.save.defeatedTrainers[npc.id] == true
    end,
    engageTrainer = function(_, npc, onDone)
      engaged = npc == lance
      -- simulate engageTrainer's win path: flag the trainer, then onDone
      game.save.defeatedTrainers[npc.id] = true
      game.save.flags.EVENT_BEAT_LANCES_ROOM_TRAINER_0 = true
      if onDone then onDone() end
    end,
  }
  local handled = hooks.onStep(game, ow, 6, 2)
  check(handled == true, "Lance coord trigger engages")
  check(engaged, "engageTrainer called for Lance")
  check(#pushed == 1 and pushed[1].pages ~= nil, "after-battle TextBox pushed")
  -- TextBox.substitute already expanded {RIVAL}/{PLAYER}; page glyphs
  -- are opaque, so re-check the source label via the header + that a
  -- box was queued on win only.
  check(#pushed[1].pages > 0, "after-battle TextBox has pages")
  -- loss path must not show after text
  pushed = {}
  game.save.defeatedTrainers = {}
  ow.engageTrainer = function(_, npc, onDone)
    -- lose: do not mark defeated
    if onDone then onDone() end
  end
  hooks.onStep(game, ow, 6, 2)
  eq(#pushed, 0, "loss does not push after-battle text")
end

S.finish()
