-- Session Replay Controller
-- Provides UI for playing back .pktrec session recordings with seek support

ReplayController = {}

local replayPanel = nil
local updateEvent = nil
local isDragging = false

function ReplayController:init()
  -- Hook into game start to detect replay mode
  connect(g_game, {
    onGameStart = function()
      -- Check if we're in replay mode (pktrec player exists)
      local player = g_game.getPktrecPlayer()
      if player and player:isLoaded() then
        ReplayController:showUI()
      end
    end,
    onGameEnd = function()
      ReplayController:stopReplay()
    end
  })
end

function ReplayController:terminate()
  self:stopReplay()
  if replayPanel then
    replayPanel:destroy()
    replayPanel = nil
  end
end

-- Show the replay UI (called when replay mode is detected)
function ReplayController:showUI()
  -- Create UI if needed
  if not replayPanel then
    self:createUI()
  end

  -- Update UI with replay info
  self:updateTimeLabel()
  self:updateSpeedCombo()
  replayPanel:show()

  -- Start the update loop
  self:startUpdateLoop()
end

-- Load and play a .pktrec file
function ReplayController:loadReplay(filePath)
  -- Stop any existing replay
  self:stopReplay()

  -- Use the C++ integration to load and play
  local success, err = pcall(function()
    g_game.playPktrecRecord(filePath)
  end)

  if not success then
    displayErrorBox("Replay Error", "Failed to load replay file:\n" .. tostring(err))
    return false
  end

  -- UI will be shown automatically via onGameStart hook
  return true
end

function ReplayController:createUI()
  replayPanel = g_ui.displayUI('replay', modules.game_interface.getMapPanel())

  -- Position at bottom center of map
  replayPanel:addAnchor(AnchorHorizontalCenter, 'parent', AnchorHorizontalCenter)
  replayPanel:addAnchor(AnchorBottom, 'parent', AnchorBottom)
  replayPanel:setMarginBottom(10)
  replayPanel:setWidth(400)

  local playPauseButton = replayPanel:getChildById('playPauseButton')
  local speedCombo = replayPanel:getChildById('speedCombo')
  local closeButton = replayPanel:getChildById('closeButton')
  local progressBarBackground = replayPanel:getChildById('progressBarBackground')

  -- Play/pause button
  playPauseButton.onClick = function()
    self:togglePlayPause()
  end

  -- Speed combo
  speedCombo:addOption('0.5x', 0.5)
  speedCombo:addOption('1x', 1.0)
  speedCombo:addOption('2x', 2.0)
  speedCombo:addOption('4x', 4.0)
  speedCombo:setCurrentOption('1x')
  speedCombo.onOptionChange = function(widget, text, data)
    local player = g_game.getPktrecPlayer()
    if player then
      player:setSpeed(data)
    end
  end

  -- Close button
  closeButton.onClick = function()
    self:stopReplay()
    -- Return to main menu
    g_game.cancelLogin()
    SignalTerminate()
  end

  -- Progress bar click/drag for seeking
  -- Only seek on release to avoid repeated disconnect/reconnect cycles during drag
  progressBarBackground.onMousePress = function(widget, mousePos, mouseButton)
    if mouseButton == MouseLeftButton then
      isDragging = true
      self:updateDragVisual(widget, mousePos)  -- Visual feedback only
    end
  end

  progressBarBackground.onMouseRelease = function(widget, mousePos, mouseButton)
    if mouseButton == MouseLeftButton and isDragging then
      isDragging = false
      self:seekToMouse(widget, mousePos)  -- Actual seek on release
    end
  end

  progressBarBackground.onMouseMove = function(widget, mousePos, mouseMoved)
    if isDragging then
      self:updateDragVisual(widget, mousePos)  -- Visual feedback only
    end
  end
end

function ReplayController:seekToMouse(widget, mousePos)
  local player = g_game.getPktrecPlayer()
  if not player then return end

  local barRect = widget:getRect()
  local relativeX = mousePos.x - barRect.x
  local progress = math.max(0, math.min(1, relativeX / barRect.width))
  local duration = player:getDuration()
  local targetTime = math.floor(progress * duration)

  -- Use full reconnect cycle for seeking - this properly resets all state
  g_game.seekReplay(targetTime)

  -- Update play button to show playing state (seekReplay auto-plays)
  if replayPanel then
    local playPauseButton = replayPanel:getChildById('playPauseButton')
    playPauseButton:setText('||')
  end
end

-- Update progress bar visual during drag without actually seeking
-- This provides immediate feedback while avoiding expensive reconnect cycles
function ReplayController:updateDragVisual(widget, mousePos)
  if not replayPanel then return end

  local barRect = widget:getRect()
  local relativeX = mousePos.x - barRect.x
  local progress = math.max(0, math.min(1, relativeX / barRect.width))

  local progressBarBackground = replayPanel:getChildById('progressBarBackground')
  local progressBarFill = progressBarBackground:getChildById('progressBarFill')
  local progressBarHandle = progressBarBackground:getChildById('progressBarHandle')

  local barWidth = progressBarBackground:getWidth()
  local fillWidth = math.floor(progress * barWidth)

  progressBarFill:setWidth(fillWidth)
  progressBarHandle:setMarginLeft(math.max(0, fillWidth - 6))

  -- Also update time label to show target position
  local player = g_game.getPktrecPlayer()
  if player then
    local duration = player:getDuration()
    local targetTime = math.floor(progress * duration)
    local timeLabel = replayPanel:getChildById('timeLabel')
    timeLabel:setText(self:formatTime(targetTime) .. ' / ' .. self:formatTime(duration))
  end
end

function ReplayController:togglePlayPause()
  local player = g_game.getPktrecPlayer()
  if not player then return end

  local playPauseButton = replayPanel:getChildById('playPauseButton')

  if player:isPlaying() and not player:isPaused() then
    player:pause()
    playPauseButton:setText('>')
  else
    player:play()
    playPauseButton:setText('||')
  end
end

function ReplayController:startUpdateLoop()
  if updateEvent then
    removeEvent(updateEvent)
  end

  local wasPlaying = true -- Assume playing when we start

  updateEvent = cycleEvent(function()
    self:updateProgressBar()
    self:updateTimeLabel()

    -- Detect when playback stops (end of recording reached)
    local player = g_game.getPktrecPlayer()
    if player then
      local isPlaying = player:isPlaying() and not player:isPaused()
      if wasPlaying and not isPlaying then
        -- Playback just stopped - update the button
        self:onReplayEnded()
      end
      wasPlaying = isPlaying
    end
  end, 100) -- Update every 100ms
end

function ReplayController:stopUpdateLoop()
  if updateEvent then
    removeEvent(updateEvent)
    updateEvent = nil
  end
end

function ReplayController:updateProgressBar()
  local player = g_game.getPktrecPlayer()
  if not player or not replayPanel then return end

  local currentTime = player:getCurrentTime()
  local duration = player:getDuration()
  local progress = duration > 0 and (currentTime / duration) or 0

  local progressBarBackground = replayPanel:getChildById('progressBarBackground')
  local progressBarFill = progressBarBackground:getChildById('progressBarFill')
  local progressBarHandle = progressBarBackground:getChildById('progressBarHandle')

  local barWidth = progressBarBackground:getWidth()
  local fillWidth = math.floor(progress * barWidth)

  progressBarFill:setWidth(fillWidth)
  progressBarHandle:setMarginLeft(math.max(0, fillWidth - 6))
end

function ReplayController:updateTimeLabel()
  local player = g_game.getPktrecPlayer()
  if not player or not replayPanel then return end

  local currentTime = player:getCurrentTime()
  local duration = player:getDuration()

  local timeLabel = replayPanel:getChildById('timeLabel')
  timeLabel:setText(self:formatTime(currentTime) .. ' / ' .. self:formatTime(duration))
end

function ReplayController:updateSpeedCombo()
  local player = g_game.getPktrecPlayer()
  if not player or not replayPanel then return end

  local speed = player:getSpeed()
  local speedCombo = replayPanel:getChildById('speedCombo')

  if speed == 0.5 then
    speedCombo:setCurrentOption('0.5x')
  elseif speed == 1.0 then
    speedCombo:setCurrentOption('1x')
  elseif speed == 2.0 then
    speedCombo:setCurrentOption('2x')
  elseif speed == 4.0 then
    speedCombo:setCurrentOption('4x')
  end
end

function ReplayController:formatTime(ms)
  local totalSeconds = math.floor(ms / 1000)
  local minutes = math.floor(totalSeconds / 60)
  local seconds = totalSeconds % 60
  return string.format('%02d:%02d', minutes, seconds)
end

function ReplayController:onReplayEnded()
  if replayPanel then
    local playPauseButton = replayPanel:getChildById('playPauseButton')
    playPauseButton:setText('>')
  end
end

function ReplayController:stopReplay()
  self:stopUpdateLoop()

  local player = g_game.getPktrecPlayer()
  if player then
    player:stop()
  end

  if replayPanel then
    replayPanel:hide()
  end
end

-- Start replay mode with a given file
function startReplay(filePath)
  return ReplayController:loadReplay(filePath)
end

-- Get the current PktrecPlayer instance
function getReplayPlayer()
  return g_game.getPktrecPlayer()
end

-- Check if replay is active
function isReplayActive()
  local player = g_game.getPktrecPlayer()
  return player ~= nil and player:isLoaded()
end
