---@class AddonPrivate
local Private = select(2, ...)

---@class Events
local events = {}
Private.Events = events

function events:InitEvents()
    local addon = Private.Addon
    addon:RegisterEvent("PLAYER_ENTERING_WORLD", function ()
        self:RequestQuestLines()
        self:AutoContinent()
        self:UpdateData()
    end)
    addon:RegisterEvent("ACHIEVEMENT_EARNED", function ()
        self:UpdateData()
    end)
    addon:RegisterEvent("CRITERIA_UPDATE", function ()
        self:UpdateData()
    end)
    -- Refresh the objective HUDs when the player changes zone/subzone.
    addon:RegisterEvent("ZONE_CHANGED_NEW_AREA", function ()
        self:RequestQuestLines()
        self:AutoContinent()
        self:RefreshHuds()
    end)
    addon:RegisterEvent("ZONE_CHANGED", function ()
        self:RefreshHuds()
    end)
    -- Questline data loads asynchronously; refresh the planner when it arrives so
    -- the Storylines & Quests section can populate.
    addon:RegisterEvent("QUESTLINE_UPDATE", function ()
        if Private.Objectives then Private.Objectives:InvalidateQuests() end
        self:RefreshHuds()
    end)
    -- Quest log changes affect the "continue this quest" list; also advance the tracked
    -- storyline's pin (giver -> [accepted: no pin] -> turn-in -> next quest).
    addon:RegisterEvent("QUEST_ACCEPTED", function (_, a, b)
        local questID = b or a -- payload is (questID) on modern clients, (index, questID) on older
        -- Accepting a meta quest auto-starts following its storyline (no click needed):
        -- SyncTrackedQuest then drops the objective pin now and the turn-in pin when ready.
        if questID and Private.Objectives and Private.WaypointsUtil then
            local lineID, name = Private.Objectives:GetMetaQuestLine(questID)
            if lineID then
                Private.WaypointsUtil:TrackQuestline(lineID, name)
            elseif name and Private.WaypointsUtil.GetTurnInWaypoint then
                -- meta quest with no resolved questline: one-off objective pin
                local m, x, y = Private.WaypointsUtil:GetTurnInWaypoint(questID)
                if m and x and y then Private.WaypointsUtil:TrackPoint(m, x * 100, y * 100, name) end
            end
        end
        if Private.WaypointsUtil then Private.WaypointsUtil:SyncTrackedQuest() end
        self:QuestsChanged()
    end)
    addon:RegisterEvent("QUEST_TURNED_IN", function ()
        if Private.WaypointsUtil then Private.WaypointsUtil:SyncTrackedQuest() end
        self:QuestsChanged()
    end)
    addon:RegisterEvent("QUEST_REMOVED", function () self:QuestsChanged() end)
    -- Quest progress (incl. a quest becoming READY to turn in). Fires a lot -> debounced.
    addon:RegisterEvent("QUEST_LOG_UPDATE", function () self:QuestLogUpdate() end)
end

-- QUEST_LOG_UPDATE fires frequently; coalesce. Refreshes the planner's "ready to turn in"
-- status and re-points the tracked storyline's pin (to the turn-in NPC once ready).
function events:QuestLogUpdate()
    if self._qluTimer then return end
    self._qluTimer = C_Timer.NewTimer(0.4, function()
        events._qluTimer = nil
        if Private.Objectives then Private.Objectives:InvalidateQuests() end
        if Private.WaypointsUtil then Private.WaypointsUtil:SyncTrackedQuest() end
        events:RefreshHuds()
    end)
end

-- Requesting questlines fans out to every zone on the continent, and each reply
-- fires QUESTLINE_UPDATE -- so spamming this (e.g. on every quest event when entering
-- a World Quest area) causes an event storm. Rate-limit: questline AVAILABILITY does
-- not change second-to-second, so at most once per 8s is plenty.
function events:RequestQuestLines()
    local now = GetTime()
    if self._lastQLRequest and (now - self._lastQLRequest) < 8 then return end
    self._lastQLRequest = now
    if Private.Objectives and Private.Objectives.RequestQuestLines then
        Private.Objectives:RequestQuestLines()
    end
end

function events:QuestsChanged()
    if Private.Objectives then Private.Objectives:InvalidateQuests() end
    -- re-request questline data so a completed step rolls to the next quest to pick up
    self:RequestQuestLines()
    self:RefreshHuds()
end

-- Auto open/close the planner + Rare Scanner when the CONTINENT changes (only when the
-- setting is on). Only acts on a real continent transition, so within a meta continent
-- the player's own show/hide choices still stick. Meta continent -> show; a continent
-- with nothing tracked at all -> hide.
function events:AutoContinent()
    local db = Private.Settings and Private.Settings.db
    if not db or not db.autoContinent or not Private.Objectives then return end
    local mapID = C_Map.GetBestMapForUnit and C_Map.GetBestMapForUnit("player")
    local cont = mapID and Private.Objectives:GetContinentID(mapID)
    if not cont or cont == self.lastContinent then return end
    self.lastContinent = cont
    local hasMeta = Private.Objectives:IsMetaContinent(mapID)
    if Private.RouteHud then Private.RouteHud:SetEnabled(hasMeta) end
    if Private.RareAlerts then Private.RareAlerts:SetEnabled(hasMeta) end
end

-- Coalesce bursts of events into ONE refresh. Entering a World Quest area fires a
-- rapid burst of QUESTLINE_UPDATE/quest events (one per zone we requested); refreshing
-- on each would run many continent-wide scans back-to-back -- a multi-second freeze
-- and a spike of throwaway tables. Debounce to a single refresh just after the burst.
function events:RefreshHuds()
    if self._refreshTimer then return end
    self._refreshTimer = C_Timer.NewTimer(0.2, function()
        events._refreshTimer = nil
        if Private.NearbyHud then Private.NearbyHud:Refresh() end
        if Private.RouteHud then Private.RouteHud:Refresh() end
    end)
end

function events:UpdateData()
    Private.UI:QueueUpdate()
    if Private.Objectives then
        Private.Objectives:InvalidateSummaries()
        Private.Objectives:InvalidateInstances()
        Private.Objectives:InvalidateQuests()
    end
    self:RefreshHuds()
end