local PREFIX = "[AdminAgentLab]"
local labWindow = nil
local labButton = nil
local commandPath = "/modules/game_tibiaagent_control/command.json"

local function log(message)
  g_logger.info(PREFIX .. " " .. tostring(message))
end

local function setStatus(message, color)
  log(message)
  if labWindow and labWindow.status then
    labWindow.status:setText(message)
    if color then
      labWindow.status:setColor(color)
    end
  end
end

local function isAdminMode()
  return _G.NewTibia_AdminMode == true
end

local function isOnline()
  return g_game and g_game.isOnline and g_game.isOnline()
end

local function say(text)
  if not isOnline() then
    setStatus("El juego no esta online todavia.", "yellow")
    return false
  end

  if modules and modules.game_console and modules.game_console.enableChat and modules.game_console.setTextEditText and modules.game_console.sendCurrentMessage then
    local ok = pcall(function()
      modules.game_console.enableChat(true)
      modules.game_console.setTextEditText(text)
      modules.game_console.sendCurrentMessage()
    end)
    if ok then
      setStatus("Enviado: " .. text, "green")
      return true
    end
  end

  if g_game.talkChannel and MessageModes then
    local ok = pcall(function() g_game.talkChannel(MessageModes.Say, 0, text) end)
    if ok then
      setStatus("Enviado: " .. text, "green")
      return true
    end
  end

  if g_game.talk then
    local ok = pcall(function() g_game.talk(text) end)
    if ok then
      setStatus("Enviado: " .. text, "green")
      return true
    end
  end

  setStatus("No he podido enviar el comando al chat.", "red")
  return false
end

local function useInventoryItemWithSelf(itemId)
  local player = g_game.getLocalPlayer and g_game.getLocalPlayer() or nil
  if itemId and player and g_game.useInventoryItemWith then
    g_game.useInventoryItemWith(itemId, player)
    return true
  end
  return false
end

local function useInventoryItemWithTile(itemId, dx, dy)
  local player = g_game.getLocalPlayer and g_game.getLocalPlayer() or nil
  local pos = player and player:getPosition() or nil
  if not pos or not g_map or not g_map.getTile or not g_game.useInventoryItemWith then
    return false
  end
  local tile = g_map.getTile({ x = pos.x + dx, y = pos.y + dy, z = pos.z })
  local target = tile and tile.getTopCreature and tile:getTopCreature() or nil
  if target then
    g_game.useInventoryItemWith(itemId, target)
    return true
  end
  return false
end

local function schedulePlan(id, actions, firstDelayMs, defaultAfterMs)
  if not isAdminMode() then
    setStatus("Agent Lab solo funciona desde Admin.", "red")
    return
  end
  if not isOnline() then
    setStatus("Entra al juego antes de lanzar pruebas.", "yellow")
    return
  end

  local cursor = tonumber(firstDelayMs or 400) or 400
  local spacing = tonumber(defaultAfterMs or 1000) or 1000
  setStatus("Plan en marcha: " .. id, "yellow")
  log("plan start id=" .. id .. " actions=" .. tostring(#actions))

  for _, action in ipairs(actions) do
    local scheduledAction = action
    scheduleEvent(function()
      if scheduledAction.type == "say" then
        say(scheduledAction.text)
      elseif scheduledAction.type == "inspect" then
        inspectPlayer(scheduledAction.label)
      elseif scheduledAction.type == "selfRune" then
        useInventoryItemWithSelf(scheduledAction.itemId)
      elseif scheduledAction.type == "targetRune" then
        useInventoryItemWithTile(scheduledAction.itemId, scheduledAction.dx or 0, scheduledAction.dy or -2)
      elseif scheduledAction.type == "log" then
        setStatus(scheduledAction.message, scheduledAction.color)
      end
    end, cursor)
    cursor = cursor + (tonumber(action.afterMs or spacing) or spacing)
  end

  scheduleEvent(function()
    setStatus("Plan completado: " .. id, "green")
    log("plan complete id=" .. id)
  end, cursor + 250)
end

local function executeControlPlan(plan)
  if modules.game_tibiaagent_control and modules.game_tibiaagent_control.executePlan then
    modules.game_tibiaagent_control.executePlan(plan)
    setStatus("Plan externo enviado al puente de agentes.", "green")
    return true
  end
  return false
end

local function readExternalPlan()
  if not g_resources or not g_resources.fileExists or not g_resources.readFileContents then
    return nil
  end
  if not g_resources.fileExists(commandPath) then
    return nil
  end

  local ok, contents = pcall(function() return g_resources.readFileContents(commandPath) end)
  if not ok or not contents or contents:len() == 0 then
    return nil
  end

  local decodedOk, plan = pcall(function() return json.decode(contents) end)
  if decodedOk and type(plan) == "table" then
    return plan
  end
  return nil
end

function runFirstHoursRoute()
  schedulePlan("first-hours-route", {
    { type = "say", text = "!start", afterMs = 1000 },
    { type = "say", text = "!stats", afterMs = 1000 },
    { type = "say", text = "!progreso", afterMs = 1000 },
    { type = "inspect", label = "first-hours-route" }
  })
end

function runProgressCheck()
  say("!progreso")
end

function runCleanup()
  say("!phase4cleanup")
end

function runPhase4Smoke()
  schedulePlan("phase4-smoke", {
    { type = "say", text = "!phase4gameplay", afterMs = 2500 },
    { type = "inspect", label = "phase4-before", afterMs = 900 },
    { type = "selfRune", itemId = 2281, afterMs = 1100 },
    { type = "selfRune", itemId = 2282, afterMs = 1100 },
    { type = "say", text = "!phase4target", afterMs = 1400 },
    { type = "targetRune", itemId = 2280, dx = 0, dy = -2, afterMs = 1200 },
    { type = "say", text = "!phase4target", afterMs = 1400 },
    { type = "targetRune", itemId = 2283, dx = 0, dy = -2, afterMs = 1200 },
    { type = "say", text = "exevo forge flam", afterMs = 1200 },
    { type = "say", text = "utani forge hur", afterMs = 1200 },
    { type = "say", text = "!phase4status", afterMs = 1000 },
    { type = "inspect", label = "phase4-after", afterMs = 900 }
  }, 600, 1000)
end

function runExternalPlan()
  local plan = readExternalPlan()
  if not plan then
    setStatus("No hay plan externo valido en command.json.", "yellow")
    return
  end
  if executeControlPlan(plan) then
    return
  end
  if type(plan.actions) == "table" then
    schedulePlan(tostring(plan.id or "external"), plan.actions, plan.firstDelayMs, plan.defaultAfterMs)
  else
    setStatus("El plan externo no tiene acciones.", "red")
  end
end

function inspectPlayer(label)
  if not isOnline() then
    setStatus("Sin jugador online.", "yellow")
    return false
  end
  local player = g_game.getLocalPlayer and g_game.getLocalPlayer() or nil
  if not player then
    setStatus("No encuentro el jugador local.", "red")
    return false
  end
  local pos = player:getPosition()
  local positionText = pos and (pos.x .. "," .. pos.y .. "," .. pos.z) or "?"
  local name = player.getName and player:getName() or "?"
  local level = player.getLevel and player:getLevel() or "?"
  local hp = player.getHealth and player:getHealth() or "?"
  local mana = player.getMana and player:getMana() or "?"
  local text = string.format("%s | %s lvl=%s hp=%s mana=%s pos=%s", tostring(label or "estado"), name, tostring(level), tostring(hp), tostring(mana), positionText)
  setStatus(text, "green")
  return true
end

function toggle()
  if not labWindow then return end
  if labWindow:isVisible() then
    labWindow:close()
  else
    labWindow:open()
    inspectPlayer("admin")
  end
end

function onMiniWindowClose()
  if labButton then
    labButton:setOn(false)
  end
end

function init()
  if not isAdminMode() then
    log("not loaded: admin mode disabled")
    return
  end

  labWindow = g_ui.loadUI("agentlab", modules.game_interface.getRightPanel())
  labWindow:setup()
  labWindow:close(true)

  labButton = modules.client_topmenu.addRightGameToggleButton("adminAgentLabButton", tr("Agent Lab"), "/images/topbuttons/options", toggle, false, 1)
  setStatus("Admin Agent Lab listo.", "green")

  connect(g_game, {
    onGameStart = function()
      setStatus("Admin online. Agent Lab listo.", "green")
    end,
    onGameEnd = function()
      setStatus("Admin desconectado.", "yellow")
    end
  })
end

function terminate()
  disconnect(g_game, {
    onGameStart = function() end,
    onGameEnd = function() end
  })
  if labButton then
    labButton:destroy()
    labButton = nil
  end
  if labWindow then
    labWindow:destroy()
    labWindow = nil
  end
end
