local DR = select(2, ...)
local constants = DR.Modules["Core.Constants"]

--- Proxy rolling: the raid leader makes roll choices on behalf of a player
--- who doesn't run the addon. Opened from the roster's "Roll For" button.
--- Shows every collecting session with the same category buttons the player
--- would see, plus their current choice and a cancel.
local ProxyRoll = {
  frame = nil,
  cards = {},
  targetGuid = nil,
  offset = 0,
}
LibStub("AceEvent-3.0"):Embed(ProxyRoll)

local CARD_HEIGHT = 58
local CARD_SPACING = 5
local VISIBLE_CARDS = 4

local CATEGORY_BUTTONS = {
  { id = constants.ROLL.CATEGORY.BIS, label = "BIS", width = 40 },
  { id = constants.ROLL.CATEGORY.TIER_4P, label = "4p", width = 32 },
  { id = constants.ROLL.CATEGORY.TIER_2P, label = "2p", width = 32 },
  { id = constants.ROLL.CATEGORY.UPGRADE, label = "Up", width = 34 },
  { id = constants.ROLL.CATEGORY.OFFSPEC, label = "OS", width = 34 },
  { id = constants.ROLL.CATEGORY.TMOG, label = "TM", width = 34 },
  { id = constants.ROLL.CATEGORY.PASS, label = "Pass", width = 42, danger = true },
}

local CATEGORY_NAMES = {
  [0] = "Pass",
  [1] = "Tmog",
  [2] = "Offspec",
  [3] = "Upgrade",
  [4] = "BIS",
  [5] = "2-piece",
  [6] = "4-piece",
}

local function Kit()
  return DR:Require("UI.Kit")
end

local function nowTs()
  return GetServerTime and GetServerTime() or time()
end

local function itemIcon(itemLink)
  if itemLink and C_Item and C_Item.GetItemInfoInstant then
    local ok, _, _, _, _, icon = pcall(C_Item.GetItemInfoInstant, itemLink)
    if ok and icon then
      return icon
    end
  end
  return 134400
end

function ProxyRoll:GetTargetDisplay()
  if not self.targetGuid then
    return "?"
  end
  return DR:Require("Services.Identity"):GetDisplayByGuid(self.targetGuid) or self.targetGuid
end

function ProxyRoll:SubmitFor(sessionId, categoryId, label)
  local manager = DR:Require("Services.SessionManager")
  local ok, value = manager:SubmitProxyRoll(sessionId, self.targetGuid, categoryId)
  if ok then
    if categoryId == constants.ROLL.CATEGORY.PASS then
      DR:Print(string.format("Passed for %s", self:GetTargetDisplay()))
    else
      DR:Print(string.format("Rolled %s for %s: %d", label, self:GetTargetDisplay(), value))
    end
  else
    DR:Print("Proxy roll failed: " .. tostring(value))
  end
  self:Render()
end

function ProxyRoll:CreateCard(index)
  local kit = Kit()
  local C = kit.COLORS
  local card = kit:CreateCard(self.frame.content, CARD_HEIGHT)
  card:SetPoint("TOPLEFT", self.frame.content, "TOPLEFT", 0, -26 - (index - 1) * (CARD_HEIGHT + CARD_SPACING))
  card:SetPoint("RIGHT", self.frame.content, "RIGHT", 0, 0)

  local icon = card:CreateTexture(nil, "ARTWORK")
  icon:SetSize(32, 32)
  icon:SetPoint("TOPLEFT", 8, -8)
  icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
  card.icon = icon

  local itemText = kit:CreateLabel(card, "", "small")
  itemText:SetPoint("TOPLEFT", icon, "TOPRIGHT", 8, -2)
  itemText:SetPoint("RIGHT", card, "RIGHT", -8, 0)
  itemText:SetJustifyH("LEFT")
  itemText:SetWordWrap(false)
  card.itemText = itemText

  local statusText = kit:CreateLabel(card, "", "small", C.textDim)
  statusText:SetPoint("TOPLEFT", itemText, "BOTTOMLEFT", 0, -3)
  statusText:SetJustifyH("LEFT")
  card.statusText = statusText

  card.buttons = {}
  local anchor = nil
  for i = #CATEGORY_BUTTONS, 1, -1 do
    local def = CATEGORY_BUTTONS[i]
    local btn = kit:CreateButton(card, def.label, def.width, 18, function()
      ProxyRoll:SubmitFor(card.sessionId, def.id, def.label)
    end, { smallText = true, danger = def.danger })
    if anchor then
      btn:SetPoint("RIGHT", anchor, "LEFT", -4, 0)
    else
      btn:SetPoint("BOTTOMRIGHT", card, "BOTTOMRIGHT", -8, 8)
    end
    anchor = btn
    card.buttons[#card.buttons + 1] = btn
  end

  local cancelBtn = kit:CreateButton(card, "Cancel Roll", 84, 18, function()
    local ok, reason = DR:Require("Services.SessionManager"):CancelRollFor(card.sessionId, ProxyRoll.targetGuid)
    if ok then
      DR:Print(string.format("Cancelled %s's roll", ProxyRoll:GetTargetDisplay()))
    else
      DR:Print("Cancel failed: " .. tostring(reason))
    end
    ProxyRoll:Render()
  end, { smallText = true, accent = true })
  cancelBtn:SetPoint("BOTTOMRIGHT", card, "BOTTOMRIGHT", -8, 8)
  card.cancelBtn = cancelBtn

  function card:Update(session, targetGuid)
    self.sessionId = session.id
    self.icon:SetTexture(itemIcon(session.itemLink))
    self.itemText:SetText(session.itemLink or "(no item)")

    local remaining = math.max((session.timeoutAt or 0) - nowTs(), 0)
    local submit = targetGuid and session.submissionsByGuid and session.submissionsByGuid[targetGuid]

    local canRoll = not submit and remaining > 0
    for _, btn in ipairs(self.buttons) do
      btn:SetShown(canRoll)
    end
    self.cancelBtn:SetShown(submit ~= nil and remaining > 0)

    if submit then
      if submit.category == constants.ROLL.CATEGORY.PASS then
        self.statusText:SetText(string.format("%ds left - passed", remaining))
      else
        self.statusText:SetText(string.format("%ds left - rolled %s (%s)", remaining,
          CATEGORY_NAMES[submit.category] or "?", tostring(submit.baseRoll or "?")))
      end
    else
      self.statusText:SetText(string.format("%ds left - no roll yet", remaining))
    end
  end

  return card
end

function ProxyRoll:CreateFrame()
  if self.frame then
    return self.frame
  end

  local kit = Kit()
  local frame = kit:CreateWindow("ProxyRoll", "DetentionRolls - Roll For", 560, 330, { strata = "DIALOG" })

  local targetText = kit:CreateLabel(frame.content, "", "normal", kit.COLORS.accent)
  targetText:SetPoint("TOPLEFT", 0, 0)
  frame.targetText = targetText

  local emptyText = kit:CreateLabel(frame.content, "No sessions collecting rolls", "normal", kit.COLORS.textDim)
  emptyText:SetPoint("CENTER", 0, 0)
  frame.emptyText = emptyText

  local scrollHint = kit:CreateLabel(frame.content, "", "small", kit.COLORS.textDim)
  scrollHint:SetPoint("TOPRIGHT", 0, 0)
  frame.scrollHint = scrollHint

  kit:AttachWheelScroll(frame, function(delta)
    self.offset = math.max(self.offset + delta, 0)
    self:Render()
  end)

  frame.updateAccumulator = 0
  frame:SetScript("OnUpdate", function(f, elapsed)
    f.updateAccumulator = f.updateAccumulator + elapsed
    if f.updateAccumulator < 1 then
      return
    end
    f.updateAccumulator = 0
    self:Render()
  end)

  self.frame = frame
  return frame
end

function ProxyRoll:Open(targetGuid)
  if not DR:Require("Services.SessionManager"):IsRaidLeaderAuthority() then
    DR:Print("Only the raid leader can roll for other players")
    return
  end
  if not targetGuid then
    return
  end

  self.targetGuid = targetGuid
  DR:Require("Services.Ledger"):Track(targetGuid)
  local frame = self:CreateFrame()
  frame:Show()
  self:Render()
end

function ProxyRoll:Render()
  local frame = self.frame
  if not frame or not frame:IsShown() then
    return
  end

  frame.targetText:SetText("Rolling for: " .. self:GetTargetDisplay())

  local sessions = {}
  for _, session in ipairs(DR:Require("Services.SessionManager"):GetActiveSessions()) do
    if session.state == constants.SESSION.STATE.COLLECTING then
      sessions[#sessions + 1] = session
    end
  end

  local total = #sessions
  local maxOffset = math.max(total - VISIBLE_CARDS, 0)
  if self.offset > maxOffset then
    self.offset = maxOffset
  end

  frame.emptyText:SetShown(total == 0)
  if total > VISIBLE_CARDS then
    frame.scrollHint:SetText(string.format("%d-%d of %d (scroll)", self.offset + 1, math.min(self.offset + VISIBLE_CARDS, total), total))
  else
    frame.scrollHint:SetText("")
  end

  for i = 1, VISIBLE_CARDS do
    local session = sessions[self.offset + i]
    local card = self.cards[i]
    if session then
      if not card then
        card = self:CreateCard(i)
        self.cards[i] = card
      end
      card:Update(session, self.targetGuid)
      card:Show()
    elseif card then
      card:Hide()
    end
  end
end

function ProxyRoll:OnInitialize()
  self:RegisterMessage("DR_SESSION_OPENED", function()
    self:Render()
  end)
  self:RegisterMessage("DR_SUBMISSION_ACCEPTED", function()
    self:Render()
  end)
  self:RegisterMessage("DR_SUBMISSION_CANCELLED", function()
    self:Render()
  end)
  self:RegisterMessage("DR_SESSION_RESOLVED", function()
    self:Render()
  end)
end

DR:RegisterModule("Modules.ProxyRoll", ProxyRoll)
