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

--- Raid activation: DetentionRolls' automation (boss-kill roll-power regain,
--- loot auto-add, group-loot funneling) only runs in raids the leader has
--- designated. Entering a raid instance as group leader shows a prompt, like
--- RCLootCouncil's usage popup; the decision is broadcast to the raid and
--- survives /reload. Manual control: /droll raid on|off.
local RaidActivation = {
  promptFrame = nil,
  pendingKey = nil,
  rosterTimer = nil,
}
LibStub("AceEvent-3.0"):Embed(RaidActivation)

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

--- Identifies the current raid instance; nil outside raid instances.
local function getInstanceKey()
  if not IsInInstance then
    return nil
  end
  local inInstance, instanceType = IsInInstance()
  if not inInstance or instanceType ~= "raid" then
    return nil
  end
  local name, _, difficultyID, _, _, _, _, instanceID = GetInstanceInfo()
  return string.format("%s:%s", tostring(instanceID or name or "unknown"), tostring(difficultyID or 0))
end

--- The master switch the automation modules check. Test mode counts as
--- active so the solo test flow keeps working.
function RaidActivation:IsActive()
  return DR.Runtime.raidActive == true or DR.Runtime.testMode == true
end

function RaidActivation:SetActive(active, reason, broadcast)
  active = active and true or false
  local changed = DR.Runtime.raidActive ~= active
  DR.Runtime.raidActive = active

  -- Remember the decision for this instance so we don't re-prompt (and so a
  -- /reload mid-raid restores it).
  local key = getInstanceKey()
  local saved = DR.db.global.activeRaid
  if key then
    saved.instanceKey = key
    saved.decision = active and "yes" or "no"
  end

  if changed then
    DR:Require("Services.Audit"):Append("RAID_ACTIVE_CHANGED", {
      active = active,
      reason = reason,
      instanceKey = key,
    })
    if active then
      DR:Print("DetentionRolls is |cff6bcc7aACTIVE|r for this raid: boss regain, loot sessions and funneling are on.")
    else
      DR:Print("DetentionRolls is |cffd08484inactive|r: boss regain and loot automation are off.")
    end
    DR:SendMessage("DR_RAID_ACTIVE_CHANGED", { active = active })
  end

  if broadcast and IsInGroup() and UnitIsGroupLeader("player") then
    DR:Require("Services.Comms"):SendMessage(constants.COMMS.MESSAGE.RAID_ACTIVE, {
      active = active,
    }, { reliable = true })
  end
end

function RaidActivation:OnRaidActiveReceived(envelope)
  if not DR:Require("Services.SessionManager"):IsEnvelopeFromLeader(envelope) then
    DR:Require("Services.Audit"):Append("AUTHORITY_REJECTED", {
      messageType = constants.COMMS.MESSAGE.RAID_ACTIVE,
      sender = envelope and (envelope.verifiedSenderName or tostring(envelope.sender)) or "unknown",
    })
    return
  end

  local active = envelope.payload and envelope.payload.active == true
  if DR.Runtime.raidActive == active then
    return
  end
  DR.Runtime.raidActive = active
  if active then
    DR:Print("The raid leader activated DetentionRolls for this raid.")
  else
    DR:Print("The raid leader deactivated DetentionRolls.")
  end
  DR:SendMessage("DR_RAID_ACTIVE_CHANGED", { active = active })
end

function RaidActivation:ShowPrompt(instanceKey)
  self.pendingKey = instanceKey
  local frame = self:CreatePromptFrame()
  frame:Show()
end

function RaidActivation:CreatePromptFrame()
  if self.promptFrame then
    return self.promptFrame
  end

  local kit = Kit()
  local C = kit.COLORS
  local frame = kit:CreateWindow("RaidPrompt", "DetentionRolls", 400, 150, { strata = "DIALOG" })

  local text = kit:CreateLabel(frame.content, "Run DetentionRolls for this raid?\n\nBoss kills will restore roll power, loot will auto-populate roll sessions, and group loot will funnel to you for distribution.", "normal")
  text:SetPoint("TOPLEFT", 0, 0)
  text:SetPoint("RIGHT", frame.content, "RIGHT", 0, 0)
  text:SetJustifyH("LEFT")
  text:SetWordWrap(true)

  local yesBtn = kit:CreateButton(frame.content, "Yes, activate", 110, 26, function()
    frame:Hide()
    RaidActivation:SetActive(true, "leader_confirmed", true)
  end)
  yesBtn:SetPoint("BOTTOMLEFT", 0, 0)

  local noBtn = kit:CreateButton(frame.content, "Not this raid", 110, 26, function()
    frame:Hide()
    RaidActivation:SetActive(false, "leader_declined", false)
  end, { smallText = true })
  noBtn:SetPoint("LEFT", yesBtn, "RIGHT", 8, 0)

  self.promptFrame = frame
  return frame
end

function RaidActivation:EvaluateZone()
  local key = getInstanceKey()
  local saved = DR.db.global.activeRaid

  if not key then
    -- Left the raid instance: automation off, decision forgotten.
    if DR.Runtime.raidActive then
      self:SetActive(false, "left_instance", false)
    end
    saved.instanceKey = nil
    saved.decision = nil
    if self.promptFrame then
      self.promptFrame:Hide()
    end
    return
  end

  if saved.instanceKey == key then
    -- Already decided for this instance (possibly before a /reload).
    if saved.decision == "yes" and not DR.Runtime.raidActive then
      DR.Runtime.raidActive = true
      DR:Print("DetentionRolls raid mode restored for this instance.")
      DR:SendMessage("DR_RAID_ACTIVE_CHANGED", { active = true })
    end
    return
  end

  -- New raid instance: the leader gets asked; raiders follow the broadcast.
  if IsInGroup() and UnitIsGroupLeader("player") then
    self:ShowPrompt(key)
  end
end

function RaidActivation:OnZoneChanged()
  -- Instance info can lag right after a load screen.
  DR:ScheduleTimer(function()
    self:EvaluateZone()
  end, 2)
end

function RaidActivation:OnRosterChanged()
  -- Late joiners never saw the activation broadcast; re-announce (debounced).
  if not (DR.Runtime.raidActive and IsInGroup() and UnitIsGroupLeader("player")) then
    return
  end
  if self.rosterTimer then
    DR:CancelTimer(self.rosterTimer, true)
  end
  self.rosterTimer = DR:ScheduleTimer(function()
    self.rosterTimer = nil
    if DR.Runtime.raidActive and IsInGroup() and UnitIsGroupLeader("player") then
      DR:Require("Services.Comms"):SendMessage(constants.COMMS.MESSAGE.RAID_ACTIVE, {
        active = true,
      }, { reliable = false })
    end
  end, 6)
end

function RaidActivation:OnInitialize()
  DR.db.global.activeRaid = DR.db.global.activeRaid or {}

  DR:Require("Services.Comms"):Subscribe(constants.COMMS.MESSAGE.RAID_ACTIVE, function(envelope)
    self:OnRaidActiveReceived(envelope)
  end)
end

function RaidActivation:OnEnable()
  self:RegisterEvent("PLAYER_ENTERING_WORLD", function()
    self:OnZoneChanged()
  end)
  self:RegisterEvent("ZONE_CHANGED_NEW_AREA", function()
    self:OnZoneChanged()
  end)
  self:RegisterEvent("PARTY_LEADER_CHANGED", function()
    self:OnZoneChanged()
  end)
  self:RegisterEvent("GROUP_ROSTER_UPDATE", function()
    self:OnRosterChanged()
  end)
end

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