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

local VersionCheck = {
  peers = {},
}

local function eachRaidMember(callback)
  if not IsInRaid() then
    return
  end

  local total = GetNumGroupMembers() or 0
  for i = 1, total do
    local unit = "raid" .. i
    if UnitExists(unit) then
      callback(unit)
    end
  end
end

function VersionCheck:OnInitialize()
  self.peers = DR.Runtime.versions or {}
  DR.Runtime.versions = self.peers

  local comms = DR:Require("Services.Comms")
  comms:Subscribe(constants.COMMS.MESSAGE.VERSION_ANNOUNCE, function(envelope)
    local payload = envelope.payload or {}
    local guid = payload.guid or envelope.sender
    if not guid then
      return
    end

    self.peers[guid] = {
      version = payload.version,
      protocol = payload.protocol,
      displayName = payload.displayName,
      updatedAt = time(),
    }
  end)
end

function VersionCheck:OnEnable()
  local comms = DR:Require("Services.Comms")
  comms:SendVersionAnnounce(DR.version)

  -- Players who join after our login never saw the initial announce, so
  -- re-announce (debounced) whenever the roster changes.
  DR:RegisterEvent("GROUP_ROSTER_UPDATE", function()
    if self.rosterTimer then
      DR:CancelTimer(self.rosterTimer, true)
    end
    self.rosterTimer = DR:ScheduleTimer(function()
      self.rosterTimer = nil
      if IsInGroup() then
        DR:Require("Services.Comms"):SendVersionAnnounce(DR.version)
      end
    end, 5)
  end)
end

function VersionCheck:GetOutdatedPeers()
  local outdated = {}
  local protocolVersion = DR.db.global.protocolVersion
  local expectedVersion = DR.version
  local identity = DR:Require("Services.Identity")

  eachRaidMember(function(unit)
    local guid, display = identity:NormalizeUnit(unit)
    if guid and guid ~= UnitGUID("player") then
      local peer = self.peers[guid]
      local bad = (not peer) or (peer.protocol ~= protocolVersion) or (peer.version ~= expectedVersion)
      if bad then
        outdated[#outdated + 1] = display or guid
      end
    end
  end)

  table.sort(outdated)
  return outdated
end

function VersionCheck:CanStartSession()
  if not DR.db.profile.versionGateStrict then
    return true, {}
  end

  local outdated = self:GetOutdatedPeers()
  if #outdated > 0 then
    return false, outdated
  end

  return true, {}
end

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