local _, addonTable = ...
local WipedQoL = addonTable.WipedQoL
local TRANSLATE = addonTable.TRANSLATE or function(msg) return msg end

local DEFAULT_WIDTH = 320
local MIN_WIDTH = 280
local ROW_HEIGHT = 28
local ROW_GAP = 4
local MAX_VISIBLE_ROWS = 12
local EMPTY_HEIGHT = 64
local INSERT_DURATION = 0.16
local DELETE_DURATION = 0.18
local CHECK_PULSE_DURATION = 0.22
local AUTO_HIDE_COLLAPSE_DURATION = 0.22
local AUTO_HIDE_CHECK_DURATION = 0.34
local AUTO_HIDE_DURATION = AUTO_HIDE_COLLAPSE_DURATION + AUTO_HIDE_CHECK_DURATION
local AUTO_HIDE_CHECK_SIZE = 52
local AUTO_HIDE_CHECK_PULSE_SIZE = 14
local QUEST_TRACKER_REANCHOR_DELAY = 0.05
local MIN_QUEST_TRACKER_OFFSET = -200
local MAX_QUEST_TRACKER_OFFSET = 200
local VALID_QUEST_TRACKER_DOCKS = {
    TOP = true,
    BOTTOM = true,
    LEFT = true,
    RIGHT = true,
}
local QUEST_TRACKER_FRAME_NAMES = {
    "KT_ObjectiveTrackerFrame",
    "ObjectiveTrackerFrame",
    "QuestObjectiveTracker",
}
local QUEST_TRACKER_CONTENT_FRAME_NAMES = {
    "KT_ScenarioObjectiveTracker",
    "KT_UIWidgetObjectiveTracker",
    "KT_CampaignQuestObjectiveTracker",
    "KT_QuestObjectiveTracker",
    "KT_AdventureObjectiveTracker",
    "KT_AchievementObjectiveTracker",
    "KT_MonthlyActivitiesObjectiveTracker",
    "KT_InitiativeTasksObjectiveTracker",
    "KT_ProfessionsRecipeTracker",
    "KT_BonusObjectiveTracker",
    "KT_WorldQuestObjectiveTracker",
    "KT_RareScannerObjectiveTracker",
    "ScenarioObjectiveTracker",
    "UIWidgetObjectiveTracker",
    "CampaignQuestObjectiveTracker",
    "QuestObjectiveTracker",
    "AdventureObjectiveTracker",
    "AchievementObjectiveTracker",
    "MonthlyActivitiesObjectiveTracker",
    "InitiativeTasksObjectiveTracker",
    "ProfessionsRecipeTracker",
    "BonusObjectiveTracker",
    "WorldQuestObjectiveTracker",
}

local function TrimText(text)
    text = tostring(text or "")
    if strtrim then
        return strtrim(text)
    end
    return text:match("^%s*(.-)%s*$") or ""
end

local function GetLastWeeklyResetTimestamp()
    if C_DateAndTime and C_DateAndTime.GetWeeklyResetStartTime then
        local resetTime = C_DateAndTime.GetWeeklyResetStartTime()
        if type(resetTime) == "number" and resetTime > 0 then
            return resetTime
        end
    end

    if C_DateAndTime and C_DateAndTime.GetSecondsUntilWeeklyReset then
        local secondsUntilReset = C_DateAndTime.GetSecondsUntilWeeklyReset()
        if type(secondsUntilReset) == "number" and secondsUntilReset > 0 then
            return time() + secondsUntilReset - (7 * 24 * 60 * 60)
        end
    end

    local now = time()
    local current = date("*t", now)
    local daysSinceWednesday = (current.wday - 4) % 7
    local resetDate = {
        year = current.year,
        month = current.month,
        day = current.day - daysSinceWednesday,
        hour = 5,
        min = 0,
        sec = 0,
    }
    local resetTime = time(resetDate)

    if now < resetTime then
        resetTime = resetTime - (7 * 24 * 60 * 60)
    end

    return resetTime
end

local function GetWeeklyKey()
    return tostring(GetLastWeeklyResetTimestamp())
end

local function SkinFrame(frame, opts)
    local helpers = addonTable.UI
    if helpers and helpers.SkinFrame then
        helpers.SkinFrame(frame, opts)
    elseif addonTable.SkinFrame then
        addonTable.SkinFrame(frame, opts)
    end
end

local function ApplyFont(fontString, size, flags)
    local helpers = addonTable.UI
    if helpers and helpers.ApplyFont then
        helpers.ApplyFont(fontString, size, flags)
    elseif addonTable.ApplyFont then
        addonTable.ApplyFont(fontString, size, flags)
    end
end

local function CreateLabel(parent, text, size)
    local helpers = addonTable.UI
    if helpers and helpers.CreateLabel then
        return helpers.CreateLabel(parent, text, size)
    end

    local label = parent:CreateFontString(nil, "ARTWORK", "GameFontNormal")
    label:SetText(text)
    ApplyFont(label, size or 12, "")
    return label
end

local function GetClassColor()
    if addonTable.GetClassColor then
        return addonTable.GetClassColor()
    end

    return 1, 0.82, 0
end

local function AddClassColorBorder(frame)
    if not frame or frame._wqolWeeklyClassBorder then
        return
    end

    local r, g, b = GetClassColor()
    local border = {}
    border.top = frame:CreateTexture(nil, "OVERLAY")
    border.bottom = frame:CreateTexture(nil, "OVERLAY")
    border.left = frame:CreateTexture(nil, "OVERLAY")
    border.right = frame:CreateTexture(nil, "OVERLAY")

    border.top:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
    border.top:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, 0)
    border.top:SetHeight(1)

    border.bottom:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 0, 0)
    border.bottom:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
    border.bottom:SetHeight(1)

    border.left:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
    border.left:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 0, 0)
    border.left:SetWidth(1)

    border.right:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, 0)
    border.right:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
    border.right:SetWidth(1)

    for _, edge in pairs(border) do
        edge:SetTexture("Interface\\Buttons\\WHITE8X8")
        if addonTable.SetAddonColorTarget then
            addonTable.SetAddonColorTarget(edge, "SetVertexColor", 1, 0.95)
        else
            edge:SetVertexColor(r, g, b, 0.95)
        end
    end

    frame._wqolWeeklyClassBorder = border
end

local function SetTaskTextState(row, done)
    if not row or not row.text then
        return
    end

    row.text:SetTextColor(done and 0.58 or 0.92, done and 0.58 or 0.92, done and 0.58 or 0.92, done and 0.85 or 1)
    if row.strike then
        row.strike:SetShown(done == true)
        row.strike:SetWidth(math.max(math.min(math.ceil(row.text:GetStringWidth() or 0), row.text:GetWidth() or 1), 1))
    end
end

local function IsQuestTrackerFrameSized(frame)
    if not frame or not frame.GetObjectType then
        return false
    end

    local width = frame:GetWidth() or 0
    local height = frame:GetHeight() or 0
    return width >= 40 and width <= 700 and height >= 10
end

local function IsQuestTrackerFrameUsable(frame)
    return IsQuestTrackerFrameSized(frame) and frame:IsShown()
end

local function IsQuestTrackerFramePositioned(frame)
    return IsQuestTrackerFrameSized(frame)
        and frame:GetLeft()
        and frame:GetRight()
        and frame:GetTop()
        and frame:GetBottom()
end

local function AddQuestTrackerContentBounds(frame, bounds)
    if not IsQuestTrackerFrameUsable(frame) then
        return
    end

    local left = frame:GetLeft()
    local right = frame:GetRight()
    local top = frame:GetTop()
    local bottom = frame:GetBottom()
    if not left or not right or not top or not bottom then
        return
    end

    bounds.left = bounds.left and math.min(bounds.left, left) or left
    bounds.right = bounds.right and math.max(bounds.right, right) or right
    bounds.top = bounds.top and math.max(bounds.top, top) or top
    bounds.bottom = bounds.bottom and math.min(bounds.bottom, bottom) or bottom
end

local function GetQuestTrackerOuterFrame()
    -- Prefer the currently visible tracker when more than one implementation exists.
    for _, frameName in ipairs(QUEST_TRACKER_FRAME_NAMES) do
        local frame = _G[frameName]
        if IsQuestTrackerFrameUsable(frame) then
            WipedQoL.weeklyTaskReminderQuestTrackerOuterFrame = frame
            return frame
        end
    end

    -- Blizzard can hide the outer tracker while it has no visible objectives. Its
    -- layout position is still valid and is a more stable anchor than falling back
    -- to the reminder's standalone position until the next tracker layout update.
    local previousFrame = WipedQoL.weeklyTaskReminderQuestTrackerOuterFrame
    if IsQuestTrackerFramePositioned(previousFrame) then
        return previousFrame
    end

    for _, frameName in ipairs(QUEST_TRACKER_FRAME_NAMES) do
        local frame = _G[frameName]
        if IsQuestTrackerFramePositioned(frame) then
            WipedQoL.weeklyTaskReminderQuestTrackerOuterFrame = frame
            return frame
        end
    end

    return nil
end

local function GetQuestTrackerContentAnchorFrame()
    local bounds = {}
    for _, frameName in ipairs(QUEST_TRACKER_CONTENT_FRAME_NAMES) do
        AddQuestTrackerContentBounds(_G[frameName], bounds)
    end

    local outer = GetQuestTrackerOuterFrame()
    if outer then
        local outerLeft = outer:GetLeft()
        local outerRight = outer:GetRight()
        local outerTop = outer:GetTop()
        local outerBottom = outer:GetBottom()
        if outerLeft and outerRight and outerTop and outerBottom then
            -- Keep the side anchors stable while still following the visible tracker height.
            bounds.left = outerLeft
            bounds.right = outerRight
            bounds.top = outerTop
            bounds.bottom = bounds.bottom or outerBottom
        end
    end

    if not bounds.left or not bounds.right or not bounds.top or not bounds.bottom then
        return nil
    end

    local width = math.max(bounds.right - bounds.left, 1)
    local height = math.max(bounds.top - bounds.bottom, 1)
    if width < 40 or height < 10 then
        return nil
    end

    local anchor = WipedQoL.weeklyTaskReminderQuestTrackerAnchor
    if not anchor then
        anchor = CreateFrame("Frame", nil, UIParent)
        anchor:EnableMouse(false)
        WipedQoL.weeklyTaskReminderQuestTrackerAnchor = anchor
    end

    anchor:ClearAllPoints()
    anchor:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", bounds.left, bounds.bottom)
    anchor:SetSize(width, height)
    anchor:Show()
    return anchor
end

local function GetQuestTrackerFrame()
    local contentAnchor = GetQuestTrackerContentAnchorFrame()
    if contentAnchor then
        return contentAnchor
    end

    return GetQuestTrackerOuterFrame()
end

local function IsMythicPlusActive()
    return C_ChallengeMode and C_ChallengeMode.IsChallengeModeActive and C_ChallengeMode.IsChallengeModeActive() == true
end

local function IsRaidEncounterActive()
    local _, instanceType = GetInstanceInfo()
    return instanceType == "raid" and WipedQoL.weeklyTaskReminderInEncounter == true
end

local function NormalizeQuestTrackerDock(dock)
    dock = tostring(dock or "RIGHT"):upper()
    return VALID_QUEST_TRACKER_DOCKS[dock] and dock or "RIGHT"
end

local function ClampQuestTrackerOffset(offset)
    offset = tonumber(offset) or 0
    offset = math.floor(offset + 0.5)
    return math.max(MIN_QUEST_TRACKER_OFFSET, math.min(offset, MAX_QUEST_TRACKER_OFFSET))
end

local function ApplyQuestTrackerDock(frame, tracker, dock, horizontalOffset, verticalOffset)
    dock = NormalizeQuestTrackerDock(dock)
    horizontalOffset = ClampQuestTrackerOffset(horizontalOffset)
    verticalOffset = ClampQuestTrackerOffset(verticalOffset)
    frame:ClearAllPoints()

    if dock == "TOP" then
        frame:SetPoint("BOTTOM", tracker, "TOP", horizontalOffset, verticalOffset)
    elseif dock == "BOTTOM" then
        frame:SetPoint("TOP", tracker, "BOTTOM", horizontalOffset, -verticalOffset)
    elseif dock == "LEFT" then
        frame:SetPoint("TOPRIGHT", tracker, "TOPLEFT", -horizontalOffset, verticalOffset)
    else
        frame:SetPoint("TOPLEFT", tracker, "TOPRIGHT", horizontalOffset, verticalOffset)
    end
end

local function ReanchorQuestTrackerReminder()
    local settings = WipedQoL:GetWeeklyTaskReminderSettings()
    if settings.enabled and not IsMythicPlusActive() and settings.attachToQuestTracker and WipedQoL.weeklyTaskReminderFrame and WipedQoL.weeklyTaskReminderFrame:IsShown() then
        WipedQoL:ApplyWeeklyTaskReminderPosition()
    end
end

local function ScheduleQuestTrackerReanchor()
    if WipedQoL.weeklyTaskReminderQuestTrackerReanchorPending then
        return
    end

    WipedQoL.weeklyTaskReminderQuestTrackerReanchorPending = true
    if C_Timer and C_Timer.After then
        C_Timer.After(QUEST_TRACKER_REANCHOR_DELAY, function()
            WipedQoL.weeklyTaskReminderQuestTrackerReanchorPending = nil
            ReanchorQuestTrackerReminder()
        end)
    else
        WipedQoL.weeklyTaskReminderQuestTrackerReanchorPending = nil
        ReanchorQuestTrackerReminder()
    end
end

local function QueueQuestTrackerLayoutReanchors()
    if C_Timer and C_Timer.After then
        C_Timer.After(0.05, ReanchorQuestTrackerReminder)
        C_Timer.After(0.25, ReanchorQuestTrackerReminder)
        C_Timer.After(0.75, ReanchorQuestTrackerReminder)
    else
        ReanchorQuestTrackerReminder()
    end
end

local function HookQuestTrackerFrame(frame)
    if not frame or frame._wqolWeeklyTaskReminderHooked then
        return
    end

    frame:HookScript("OnSizeChanged", ScheduleQuestTrackerReanchor)
    frame:HookScript("OnShow", ScheduleQuestTrackerReanchor)
    frame:HookScript("OnHide", ScheduleQuestTrackerReanchor)
    frame._wqolWeeklyTaskReminderHooked = true
end

local function HookEditModeManagerFrame()
    local frame = _G.EditModeManagerFrame
    if not frame or frame._wqolWeeklyTaskReminderHooked then
        return
    end

    frame:HookScript("OnHide", QueueQuestTrackerLayoutReanchors)
    frame._wqolWeeklyTaskReminderHooked = true
end

local function SyncWeeklyTaskReminderFrameLayers(frame)
    if not frame then
        return
    end

    local strata = frame:GetFrameStrata() or "LOW"
    local baseLevel = frame:GetFrameLevel() or 0
    if frame.closeButton then
        frame.closeButton:SetFrameStrata(strata)
        frame.closeButton:SetFrameLevel(baseLevel + 3)
    end

    if type(frame.rows) ~= "table" then
        return
    end

    for _, row in ipairs(frame.rows) do
        if row then
            row:SetFrameStrata(strata)
            row:SetFrameLevel(baseLevel + 1)
            if row.checkBox then
                row.checkBox:SetFrameStrata(strata)
                row.checkBox:SetFrameLevel(baseLevel + 2)
            end
            if row.delete then
                row.delete:SetFrameStrata(strata)
                row.delete:SetFrameLevel(baseLevel + 2)
            end
        end
    end
end

local function RegisterEventSafely(frame, eventName)
    if not frame or not eventName then
        return
    end

    if pcall(frame.RegisterEvent, frame, eventName) then
        return
    end

    if frame.UnregisterEvent then
        pcall(frame.UnregisterEvent, frame, eventName)
    end
end

local function EaseOut(progress)
    progress = math.max(0, math.min(progress or 0, 1))
    local inverse = 1 - progress
    return 1 - (inverse * inverse)
end

local function GetInsertProgress(frame, task)
    local animation = frame and frame.insertAnimation
    if not animation or animation.task ~= task then
        return 1
    end

    return EaseOut((tonumber(animation.elapsed) or 0) / INSERT_DURATION)
end

local function GetDeleteProgress(frame, task)
    local animation = frame and frame.deleteAnimation
    if not animation or animation.task ~= task then
        return 0
    end

    return EaseOut((tonumber(animation.elapsed) or 0) / DELETE_DURATION)
end

local function GetCheckPulseProgress(frame, task)
    local animation = frame and frame.checkAnimation
    if not animation or animation.task ~= task then
        return 0
    end

    return math.max(0, math.min((tonumber(animation.elapsed) or 0) / CHECK_PULSE_DURATION, 1))
end

local function SetCheckBoxChecked(row, checked, pulseProgress)
    if not row or not row.checkTexture then
        return
    end

    local progress = tonumber(pulseProgress) or 0
    local pulse = progress > 0 and math.sin(progress * math.pi) or 0
    local size = 12 + (4 * pulse)
    row.checkTexture:SetSize(size, size)
    row.checkTexture:SetShown(checked == true)
end

local function HideWeeklyTaskReminderAutoHideCheck()
    local checkFrame = WipedQoL.weeklyTaskReminderAutoHideCheckFrame
    if checkFrame then
        checkFrame:Hide()
        checkFrame:SetAlpha(0)
    end
end

local function EnsureWeeklyTaskReminderAutoHideCheck()
    if WipedQoL.weeklyTaskReminderAutoHideCheckFrame then
        return WipedQoL.weeklyTaskReminderAutoHideCheckFrame
    end

    local checkFrame = CreateFrame("Frame", nil, UIParent)
    checkFrame:SetFrameStrata("DIALOG")
    checkFrame:SetFrameLevel(240)
    checkFrame:EnableMouse(false)
    checkFrame:SetAlpha(0)
    checkFrame:Hide()

    checkFrame.texture = checkFrame:CreateTexture(nil, "OVERLAY")
    checkFrame.texture:SetAllPoints()
    checkFrame.texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
    checkFrame.texture:SetVertexColor(0.32, 1, 0.42, 1)

    WipedQoL.weeklyTaskReminderAutoHideCheckFrame = checkFrame
    return checkFrame
end

local function UpdateWeeklyTaskReminderAutoHideCheck(animation, progress)
    if not animation then
        HideWeeklyTaskReminderAutoHideCheck()
        return
    end

    local checkFrame = EnsureWeeklyTaskReminderAutoHideCheck()
    local fadeIn = math.min(progress / 0.28, 1)
    local fadeOut = progress > 0.82 and math.max(0, 1 - ((progress - 0.82) / 0.18)) or 1
    local grow = EaseOut(math.min(progress / 0.62, 1))
    local pulseProgress = progress >= 0.48 and progress <= 0.94 and ((progress - 0.48) / 0.46) or 0
    local pulse = pulseProgress > 0 and math.sin(pulseProgress * math.pi) or 0
    local size = 10 + ((AUTO_HIDE_CHECK_SIZE - 10) * grow) + (AUTO_HIDE_CHECK_PULSE_SIZE * pulse)

    checkFrame:ClearAllPoints()
    checkFrame:SetPoint("CENTER", UIParent, "CENTER", animation.centerOffsetX or 0, animation.centerOffsetY or 0)
    checkFrame:SetSize(size, size)
    checkFrame:SetAlpha(fadeIn * fadeOut)
    checkFrame:Show()
end

local function FinishWeeklyTaskReminderAutoHideAnimation(frame)
    if not frame then
        return
    end

    frame.autoHideAnimation = nil
    frame:SetAlpha(1)
    frame:SetScale(1)
    frame:SetClampedToScreen(true)
    HideWeeklyTaskReminderAutoHideCheck()
    if WipedQoL:IsWeeklyTaskReminderConfigActive() then
        if not frame.deleteAnimation and not frame.insertAnimation and not frame.checkAnimation then
            frame:SetScript("OnUpdate", nil)
        end
        WipedQoL:RefreshWeeklyTaskReminder()
        return
    end

    frame:Hide()
    if not frame.deleteAnimation and not frame.insertAnimation and not frame.checkAnimation then
        frame:SetScript("OnUpdate", nil)
    end
end

local function FinishWeeklyTaskDeleteAnimation(frame)
    local animation = frame and frame.deleteAnimation
    if not animation or not animation.task then
        return
    end

    local settings = WipedQoL:GetWeeklyTaskReminderSettings()
    for index, task in ipairs(settings.tasks) do
        if task == animation.task then
            table.remove(settings.tasks, index)
            break
        end
    end

    frame.deleteAnimation = nil
    WipedQoL:UpdateWeeklyTaskReminderAutoHide(settings)
    WipedQoL.weeklyTaskReminderOffset = math.min(
        WipedQoL.weeklyTaskReminderOffset or 0,
        math.max(#settings.tasks - MAX_VISIBLE_ROWS, 0)
    )
    WipedQoL:RefreshWeeklyTaskReminder()
end

local function UpdateWeeklyTaskReminderAnimations(frame, delta)
    local hasAnimation = false

    if frame.deleteAnimation then
        hasAnimation = true
        frame.deleteAnimation.elapsed = (tonumber(frame.deleteAnimation.elapsed) or 0) + (delta or 0)
        if frame.deleteAnimation.elapsed >= DELETE_DURATION then
            FinishWeeklyTaskDeleteAnimation(frame)
            return
        end
    end

    if frame.insertAnimation then
        hasAnimation = true
        frame.insertAnimation.elapsed = (tonumber(frame.insertAnimation.elapsed) or 0) + (delta or 0)
        if frame.insertAnimation.elapsed >= INSERT_DURATION then
            frame.insertAnimation = nil
        end
    end

    if frame.checkAnimation then
        hasAnimation = true
        frame.checkAnimation.elapsed = (tonumber(frame.checkAnimation.elapsed) or 0) + (delta or 0)
        if frame.checkAnimation.elapsed >= CHECK_PULSE_DURATION then
            frame.checkAnimation = nil
        end
    end

    if frame.autoHideAnimation then
        hasAnimation = true
        frame.autoHideAnimation.elapsed = (tonumber(frame.autoHideAnimation.elapsed) or 0) + (delta or 0)
        if frame.autoHideAnimation.elapsed >= AUTO_HIDE_DURATION then
            FinishWeeklyTaskReminderAutoHideAnimation(frame)
            return
        end
    end

    if not frame.deleteAnimation and not frame.insertAnimation and not frame.checkAnimation and not frame.autoHideAnimation then
        frame:SetScript("OnUpdate", nil)
    end

    if hasAnimation then
        WipedQoL:RefreshWeeklyTaskReminder()
    end
end

local function StartWeeklyTaskReminderAnimation(frame, animationKey, task)
    if not frame or not task then
        return
    end

    frame[animationKey] = {
        task = task,
        elapsed = 0,
    }
    frame:SetScript("OnUpdate", UpdateWeeklyTaskReminderAnimations)
end

local function StartWeeklyTaskReminderAutoHideAnimation(frame)
    if not frame or not frame:IsShown() or frame.autoHideAnimation then
        return
    end

    local centerX, centerY = frame:GetCenter()
    local parentCenterX, parentCenterY = UIParent:GetCenter()
    frame.autoHideAnimation = {
        elapsed = 0,
        centerOffsetX = (centerX and parentCenterX) and (centerX - parentCenterX) or 0,
        centerOffsetY = (centerY and parentCenterY) and (centerY - parentCenterY) or 0,
        startWidth = frame:GetWidth() or DEFAULT_WIDTH,
        startHeight = frame:GetHeight() or EMPTY_HEIGHT,
    }
    frame:SetAlpha(1)
    frame:SetScale(1)
    frame:SetClampedToScreen(false)
    HideWeeklyTaskReminderAutoHideCheck()
    frame:SetScript("OnUpdate", UpdateWeeklyTaskReminderAnimations)
end

local function AreAllWeeklyTasksDone(settings)
    local tasks = settings and settings.tasks
    if type(tasks) ~= "table" or #tasks == 0 then
        return false
    end

    for _, task in ipairs(tasks) do
        if type(task) ~= "table" or task.doneWeek ~= settings.weekKey then
            return false
        end
    end

    return true
end

function WipedQoL:GetWeeklyTaskReminderSettings()
    local modules = self.db and self.db.profile and self.db.profile.modules
    if type(modules) ~= "table" then
        return {}
    end

    if type(modules.weeklyTaskReminder) ~= "table" then
        modules.weeklyTaskReminder = {}
    end

    local settings = modules.weeklyTaskReminder
    settings.enabled = settings.enabled == true
    settings.autoHideCompleted = settings.autoHideCompleted == true
    settings.hiddenByCompleted = settings.hiddenByCompleted == true
    settings.locked = settings.locked == true
    settings.attachToQuestTracker = settings.attachToQuestTracker == true
    settings.questTrackerDock = NormalizeQuestTrackerDock(settings.questTrackerDock)
    settings.questTrackerHorizontalOffset = ClampQuestTrackerOffset(settings.questTrackerHorizontalOffset or settings.questTrackerOffset)
    settings.questTrackerVerticalOffset = ClampQuestTrackerOffset(settings.questTrackerVerticalOffset or settings.questTrackerOffset)
    settings.questTrackerOffset = nil
    settings.point = settings.point or "CENTER"
    settings.relativePoint = settings.relativePoint or "CENTER"
    settings.x = tonumber(settings.x) or 360
    settings.y = tonumber(settings.y) or 0
    settings.width = math.max(tonumber(settings.width) or DEFAULT_WIDTH, MIN_WIDTH)
    if type(settings.tasks) ~= "table" then
        settings.tasks = {}
    end

    self:RefreshWeeklyTaskReminderWeek(settings)
    return settings
end

function WipedQoL:GetWeeklyTaskReminderWeekKey()
    return GetWeeklyKey()
end

function WipedQoL:RefreshWeeklyTaskReminderWeek(settings)
    settings = settings or (self.db and self.db.profile and self.db.profile.modules and self.db.profile.modules.weeklyTaskReminder)
    if type(settings) ~= "table" then
        return
    end

    local weekKey = GetWeeklyKey()
    if settings.weekKey == weekKey then
        return
    end

    settings.weekKey = weekKey
    settings.hiddenByCompleted = false
    if type(settings.tasks) == "table" then
        for _, task in ipairs(settings.tasks) do
            if type(task) == "table" then
                task.doneWeek = nil
            end
        end
    end
end

function WipedQoL:IsWeeklyTaskDone(task)
    local settings = self:GetWeeklyTaskReminderSettings()
    return type(task) == "table" and task.doneWeek == settings.weekKey
end

function WipedQoL:IsWeeklyTaskReminderVisible()
    local settings = self:GetWeeklyTaskReminderSettings()
    return settings.enabled == true and (settings.hiddenByCompleted ~= true or self:IsWeeklyTaskReminderConfigActive()) and not IsMythicPlusActive() and not IsRaidEncounterActive()
end

function WipedQoL:IsWeeklyTaskReminderConfigActive()
    return self.mainWindow and self.mainWindow:IsShown() and self.activeTab == "weeklyTasks"
end

function WipedQoL:UpdateWeeklyTaskReminderAutoHide(settings)
    settings = settings or self:GetWeeklyTaskReminderSettings()
    local wasHidden = settings.hiddenByCompleted == true
    settings.hiddenByCompleted = settings.autoHideCompleted and AreAllWeeklyTasksDone(settings) or false
    if settings.hiddenByCompleted and not wasHidden and not self:IsWeeklyTaskReminderConfigActive() then
        StartWeeklyTaskReminderAutoHideAnimation(self.weeklyTaskReminderFrame)
    end
    if settings.hiddenByCompleted ~= wasHidden and self.RefreshWeeklyTaskReminderOptionsUI then
        self:RefreshWeeklyTaskReminderOptionsUI()
    end
end

function WipedQoL:AddWeeklyTask(text)
    text = TrimText(text)
    if text == "" then
        return false
    end

    local settings = self:GetWeeklyTaskReminderSettings()
    settings.hiddenByCompleted = false
    settings.tasks[#settings.tasks + 1] = {
        text = text,
        doneWeek = nil,
    }
    local task = settings.tasks[#settings.tasks]
    self.weeklyTaskReminderOffset = math.max(#settings.tasks - MAX_VISIBLE_ROWS, 0)
    if self.weeklyTaskReminderFrame and self.weeklyTaskReminderFrame:IsShown() then
        StartWeeklyTaskReminderAnimation(self.weeklyTaskReminderFrame, "insertAnimation", task)
    end
    self:RefreshWeeklyTaskReminder()
    return true
end

function WipedQoL:RemoveWeeklyTask(index)
    local settings = self:GetWeeklyTaskReminderSettings()
    index = tonumber(index)
    if not index or not settings.tasks[index] then
        return
    end

    local task = settings.tasks[index]
    if self.weeklyTaskReminderFrame and self.weeklyTaskReminderFrame:IsShown() then
        if self.weeklyTaskReminderFrame.deleteAnimation and self.weeklyTaskReminderFrame.deleteAnimation.task == task then
            return
        end
        StartWeeklyTaskReminderAnimation(self.weeklyTaskReminderFrame, "deleteAnimation", task)
        self:RefreshWeeklyTaskReminder()
        return
    end

    table.remove(settings.tasks, index)
    self:UpdateWeeklyTaskReminderAutoHide(settings)
    self.weeklyTaskReminderOffset = math.min(self.weeklyTaskReminderOffset or 0, math.max(#settings.tasks - MAX_VISIBLE_ROWS, 0))
    self:RefreshWeeklyTaskReminder()
end

function WipedQoL:SetWeeklyTaskDone(index, done)
    local settings = self:GetWeeklyTaskReminderSettings()
    index = tonumber(index)
    local task = index and settings.tasks[index]
    if type(task) ~= "table" then
        return
    end

    task.doneWeek = done == true and settings.weekKey or nil
    if self.weeklyTaskReminderFrame and self.weeklyTaskReminderFrame:IsShown() then
        StartWeeklyTaskReminderAnimation(self.weeklyTaskReminderFrame, "checkAnimation", task)
    end
    self:UpdateWeeklyTaskReminderAutoHide(settings)
    self:RefreshWeeklyTaskReminder()
end

function WipedQoL:ResetWeeklyTaskReminderProgress()
    local settings = self:GetWeeklyTaskReminderSettings()
    settings.hiddenByCompleted = false
    for _, task in ipairs(settings.tasks) do
        if type(task) == "table" then
            task.doneWeek = nil
        end
    end
    self:RefreshWeeklyTaskReminder()
end

function WipedQoL:SetWeeklyTaskReminderEnabled(enabled)
    local settings = self:GetWeeklyTaskReminderSettings()
    settings.enabled = enabled == true
    if settings.enabled then
        settings.hiddenByCompleted = false
    end
    self:RefreshWeeklyTaskReminder()

    if self.RefreshWeeklyTaskReminderOptionsUI then
        self:RefreshWeeklyTaskReminderOptionsUI()
    end
end

function WipedQoL:SetWeeklyTaskReminderAutoHideCompleted(enabled)
    local settings = self:GetWeeklyTaskReminderSettings()
    settings.autoHideCompleted = enabled == true
    if settings.autoHideCompleted then
        self:UpdateWeeklyTaskReminderAutoHide(settings)
    else
        settings.hiddenByCompleted = false
    end
    self:RefreshWeeklyTaskReminder()
    if self.RefreshWeeklyTaskReminderOptionsUI then
        self:RefreshWeeklyTaskReminderOptionsUI()
    end
end

function WipedQoL:SetWeeklyTaskReminderLocked(locked)
    local settings = self:GetWeeklyTaskReminderSettings()
    settings.locked = locked == true
    if self.RefreshWeeklyTaskReminderOptionsUI then
        self:RefreshWeeklyTaskReminderOptionsUI()
    end
end

function WipedQoL:SetWeeklyTaskReminderAttachToQuestTracker(enabled)
    local settings = self:GetWeeklyTaskReminderSettings()
    settings.attachToQuestTracker = enabled == true
    if settings.attachToQuestTracker then
        self:HookWeeklyTaskReminderQuestTrackerFrames()
    end
    self:ApplyWeeklyTaskReminderPosition()
    if self.RefreshWeeklyTaskReminderOptionsUI then
        self:RefreshWeeklyTaskReminderOptionsUI()
    end
end

function WipedQoL:GetWeeklyTaskReminderQuestTrackerDock()
    return self:GetWeeklyTaskReminderSettings().questTrackerDock or "RIGHT"
end

function WipedQoL:SetWeeklyTaskReminderQuestTrackerDock(dock)
    local settings = self:GetWeeklyTaskReminderSettings()
    settings.questTrackerDock = NormalizeQuestTrackerDock(dock)
    self:ApplyWeeklyTaskReminderPosition()
    if self.RefreshWeeklyTaskReminderOptionsUI then
        self:RefreshWeeklyTaskReminderOptionsUI()
    end
end

function WipedQoL:GetWeeklyTaskReminderQuestTrackerHorizontalOffset()
    return self:GetWeeklyTaskReminderSettings().questTrackerHorizontalOffset or 0
end

function WipedQoL:SetWeeklyTaskReminderQuestTrackerHorizontalOffset(offset)
    local settings = self:GetWeeklyTaskReminderSettings()
    settings.questTrackerHorizontalOffset = ClampQuestTrackerOffset(offset)
    self:ApplyWeeklyTaskReminderPosition()
    if self.RefreshWeeklyTaskReminderOptionsUI then
        self:RefreshWeeklyTaskReminderOptionsUI()
    end
end

function WipedQoL:GetWeeklyTaskReminderQuestTrackerVerticalOffset()
    return self:GetWeeklyTaskReminderSettings().questTrackerVerticalOffset or 0
end

function WipedQoL:SetWeeklyTaskReminderQuestTrackerVerticalOffset(offset)
    local settings = self:GetWeeklyTaskReminderSettings()
    settings.questTrackerVerticalOffset = ClampQuestTrackerOffset(offset)
    self:ApplyWeeklyTaskReminderPosition()
    if self.RefreshWeeklyTaskReminderOptionsUI then
        self:RefreshWeeklyTaskReminderOptionsUI()
    end
end

function WipedQoL:SaveWeeklyTaskReminderPosition()
    local frame = self.weeklyTaskReminderFrame
    if not frame then
        return
    end

    local settings = self:GetWeeklyTaskReminderSettings()
    if settings.attachToQuestTracker then
        return
    end

    local point, _, relativePoint, x, y = frame:GetPoint(1)
    settings.point = point or "CENTER"
    settings.relativePoint = relativePoint or "CENTER"
    settings.x = x or 0
    settings.y = y or 0
end

function WipedQoL:ApplyWeeklyTaskReminderPosition()
    local frame = self.weeklyTaskReminderFrame
    if not frame then
        return
    end

    local settings = self:GetWeeklyTaskReminderSettings()
    if settings.attachToQuestTracker then
        self:HookWeeklyTaskReminderQuestTrackerFrames()
        local tracker = GetQuestTrackerFrame()
        if tracker then
            ApplyQuestTrackerDock(
                frame,
                tracker,
                settings.questTrackerDock,
                settings.questTrackerHorizontalOffset,
                settings.questTrackerVerticalOffset
            )
            return
        end
    end

    frame:ClearAllPoints()
    frame:SetPoint(settings.point, UIParent, settings.relativePoint, settings.x, settings.y)
end

function WipedQoL:HookWeeklyTaskReminderQuestTrackerFrames()
    for _, frameName in ipairs(QUEST_TRACKER_FRAME_NAMES) do
        HookQuestTrackerFrame(_G[frameName])
    end
    for _, frameName in ipairs(QUEST_TRACKER_CONTENT_FRAME_NAMES) do
        HookQuestTrackerFrame(_G[frameName])
    end
end

function WipedQoL:CreateWeeklyTaskReminderRow(parent, index)
    local row = CreateFrame("Button", nil, parent, "BackdropTemplate")
    row:SetHeight(ROW_HEIGHT)
    row:RegisterForClicks("AnyUp")
    row:EnableMouseWheel(true)
    SkinFrame(row, { light = true })

    row.checkBox = CreateFrame("Button", nil, row, "BackdropTemplate")
    row.checkBox:SetSize(16, 16)
    row.checkBox:SetPoint("LEFT", row, "LEFT", 8, 0)
    SkinFrame(row.checkBox, { light = true })
    AddClassColorBorder(row.checkBox)

    row.checkTexture = row.checkBox:CreateTexture(nil, "OVERLAY")
    row.checkTexture:SetPoint("CENTER", row.checkBox, "CENTER", 0, 0)
    row.checkTexture:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
    row.checkTexture:SetVertexColor(0.35, 1, 0.45, 0.95)
    row.checkTexture:Hide()

    row.checkBox:SetScript("OnClick", function()
        if row.taskIndex then
            WipedQoL:SetWeeklyTaskDone(row.taskIndex, not WipedQoL:IsWeeklyTaskDone(row.task))
        end
    end)

    row.text = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    row.text:SetPoint("LEFT", row.checkBox, "RIGHT", 8, 0)
    row.text:SetPoint("RIGHT", row, "RIGHT", -34, 0)
    row.text:SetJustifyH("LEFT")
    row.text:SetWordWrap(false)
    ApplyFont(row.text, 12, "")

    row.strike = row:CreateTexture(nil, "OVERLAY")
    row.strike:SetTexture("Interface\\Buttons\\WHITE8X8")
    row.strike:SetVertexColor(0.9, 0.9, 0.9, 0.65)
    row.strike:SetPoint("LEFT", row.text, "LEFT", 0, 0)
    row.strike:SetHeight(1)

    row.delete = CreateFrame("Button", nil, row, "UIPanelCloseButton")
    row.delete:SetSize(22, 22)
    row.delete:SetPoint("RIGHT", row, "RIGHT", -4, 0)
    local helpers = addonTable.UI
    if helpers and helpers.SkinCloseButton then
        helpers.SkinCloseButton(row.delete)
    end
    row.delete:SetScript("OnClick", function()
        if row.taskIndex then
            WipedQoL:RemoveWeeklyTask(row.taskIndex)
        end
    end)

    row:SetScript("OnClick", function(self, button)
        if button == "LeftButton" and self.taskIndex then
            WipedQoL:SetWeeklyTaskDone(self.taskIndex, not WipedQoL:IsWeeklyTaskDone(self.task))
        end
    end)
    row:SetScript("OnMouseWheel", function(_, delta)
        if parent and parent:GetScript("OnMouseWheel") then
            parent:GetScript("OnMouseWheel")(parent, delta)
        end
    end)

    row.index = index
    return row
end

function WipedQoL:CreateWeeklyTaskReminderFrame()
    if self.weeklyTaskReminderFrame then
        return self.weeklyTaskReminderFrame
    end

    local settings = self:GetWeeklyTaskReminderSettings()
    local frame = CreateFrame("Frame", "WipedQoLWeeklyTaskReminderFrame", UIParent, "BackdropTemplate")
    frame:SetSize(settings.width or DEFAULT_WIDTH, 160)
    frame:SetFrameStrata("LOW")
    frame:SetFrameLevel(20)
    frame:SetMovable(true)
    frame:RegisterForDrag("LeftButton")
    frame:SetClampedToScreen(true)
    frame:EnableMouse(true)
    frame:EnableMouseWheel(true)
    frame.rows = {}
    frame:Hide()
    SkinFrame(frame)

    WipedQoL:CreateTopAccentBar(frame)

    frame.title = CreateLabel(frame, TRANSLATE("Weekly Task Reminder"), 14)
    frame.title:SetPoint("TOPLEFT", frame, "TOPLEFT", 12, -10)
    frame.title:SetTextColor(1, 1, 1, 0.95)

    frame.counter = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    frame.counter:SetPoint("LEFT", frame.title, "RIGHT", 8, 0)
    frame.counter:SetTextColor(0.68, 0.68, 0.68, 1)
    ApplyFont(frame.counter, 11, "")

    local closeButton = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
    closeButton:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -6, -6)
    local helpers = addonTable.UI
    if helpers and helpers.SkinCloseButton then
        helpers.SkinCloseButton(closeButton)
    end
    frame.closeButton = closeButton
    closeButton:SetScript("OnClick", function()
        WipedQoL:SetWeeklyTaskReminderEnabled(false)
    end)

    frame.empty = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    frame.empty:SetPoint("TOPLEFT", frame.title, "BOTTOMLEFT", 0, -22)
    frame.empty:SetPoint("RIGHT", frame, "RIGHT", -12, 0)
    frame.empty:SetJustifyH("LEFT")
    frame.empty:SetTextColor(0.62, 0.62, 0.62, 1)
    frame.empty:SetText(TRANSLATE("No weekly tasks yet."))
    ApplyFont(frame.empty, 12, "")

    frame:SetScript("OnDragStart", function(reminder)
        local settings = WipedQoL:GetWeeklyTaskReminderSettings()
        if settings.locked or settings.attachToQuestTracker then
            return
        end
        reminder:StartMoving()
    end)
    frame:SetScript("OnDragStop", function(reminder)
        reminder:StopMovingOrSizing()
        WipedQoL:SaveWeeklyTaskReminderPosition()
    end)
    frame:SetScript("OnMouseWheel", function(_, delta)
        local settings = WipedQoL:GetWeeklyTaskReminderSettings()
        local tasks = settings.tasks or {}
        local taskCount = #tasks
        local maxOffset = math.max(taskCount - MAX_VISIBLE_ROWS, 0)
        if maxOffset <= 0 then
            return
        end

        WipedQoL.weeklyTaskReminderOffset = math.max(
            0,
            math.min((WipedQoL.weeklyTaskReminderOffset or 0) - (delta > 0 and 1 or -1), maxOffset)
        )
        WipedQoL:RefreshWeeklyTaskReminder()
    end)

    self.weeklyTaskReminderFrame = frame
    self:ApplyWeeklyTaskReminderPosition()
    SyncWeeklyTaskReminderFrameLayers(frame)
    return frame
end

function WipedQoL:RefreshWeeklyTaskReminder()
    local settings = self:GetWeeklyTaskReminderSettings()
    local frame = self.weeklyTaskReminderFrame
    local hiddenByCompleted = settings.hiddenByCompleted and not self:IsWeeklyTaskReminderConfigActive()
    local animatingAutoHide = hiddenByCompleted and frame and frame.autoHideAnimation

    if frame and not hiddenByCompleted and frame.autoHideAnimation then
        frame.autoHideAnimation = nil
        frame:SetAlpha(1)
        frame:SetScale(1)
        frame:SetClampedToScreen(true)
        HideWeeklyTaskReminderAutoHideCheck()
        if not frame.deleteAnimation and not frame.insertAnimation and not frame.checkAnimation then
            frame:SetScript("OnUpdate", nil)
        end
    end

    if not settings.enabled or (hiddenByCompleted and not animatingAutoHide) or IsMythicPlusActive() or IsRaidEncounterActive() then
        if frame then
            frame.autoHideAnimation = nil
            frame:SetAlpha(1)
            frame:SetScale(1)
            frame:SetClampedToScreen(true)
            HideWeeklyTaskReminderAutoHideCheck()
            frame:Hide()
        end
        return
    end

    frame = self:CreateWeeklyTaskReminderFrame()
    if not animatingAutoHide then
        self:ApplyWeeklyTaskReminderPosition()
    end
    frame:EnableMouse(true)

    local tasks = settings.tasks or {}
    local maxOffset = math.max(#tasks - MAX_VISIBLE_ROWS, 0)
    self.weeklyTaskReminderOffset = math.max(0, math.min(self.weeklyTaskReminderOffset or 0, maxOffset))
    local offset = self.weeklyTaskReminderOffset
    local visibleCount = math.min(#tasks - offset, MAX_VISIBLE_ROWS)
    local doneCount = 0
    for _, task in ipairs(tasks) do
        if self:IsWeeklyTaskDone(task) then
            doneCount = doneCount + 1
        end
    end

    if maxOffset > 0 then
        frame.counter:SetText(string.format("%d/%d  %d-%d", doneCount, #tasks, offset + 1, offset + visibleCount))
    else
        frame.counter:SetText(string.format("%d/%d", doneCount, #tasks))
    end
    frame.empty:SetShown(#tasks == 0)

    local previous
    for index = 1, MAX_VISIBLE_ROWS do
        local row = frame.rows[index]
        if not row then
            row = self:CreateWeeklyTaskReminderRow(frame, index)
            frame.rows[index] = row
        end

        local taskIndex = offset + index
        local task = tasks[taskIndex]
        if task then
            local insertProgress = GetInsertProgress(frame, task)
            local deleteProgress = GetDeleteProgress(frame, task)
            local rowProgress = insertProgress * (1 - deleteProgress)
            local rowHeight = math.max(1, ROW_HEIGHT * rowProgress)
            row.task = task
            row.taskIndex = taskIndex
            row:ClearAllPoints()
            row:SetPoint("LEFT", frame, "LEFT", 12, 0)
            row:SetPoint("RIGHT", frame, "RIGHT", -12, 0)
            row:SetHeight(rowHeight)
            row:SetAlpha(math.max(0.05, rowProgress))
            if previous then
                row:SetPoint("TOP", previous, "BOTTOM", 0, -ROW_GAP)
            else
                row:SetPoint("TOP", frame.title, "BOTTOM", 0, -18)
            end
            row.text:SetText(task.text or "")
            local done = self:IsWeeklyTaskDone(task)
            SetCheckBoxChecked(row, done, GetCheckPulseProgress(frame, task))
            SetTaskTextState(row, done)
            row:Show()
            previous = row
        else
            row.task = nil
            row.taskIndex = nil
            row:Hide()
        end
    end

    local height = EMPTY_HEIGHT
    if visibleCount > 0 then
        height = 56
        for index = 1, MAX_VISIBLE_ROWS do
            local task = tasks[offset + index]
            if task then
                height = height + (ROW_HEIGHT * GetInsertProgress(frame, task) * (1 - GetDeleteProgress(frame, task)))
                if index < visibleCount then
                    height = height + ROW_GAP
                end
            end
        end
        height = height + 10

        if #tasks == 1 and frame.insertAnimation and frame.insertAnimation.task == tasks[1] then
            local progress = GetInsertProgress(frame, tasks[1])
            local fullHeight = 56 + ROW_HEIGHT + 10
            height = EMPTY_HEIGHT + ((fullHeight - EMPTY_HEIGHT) * progress)
        elseif #tasks == 1 and frame.deleteAnimation and frame.deleteAnimation.task == tasks[1] then
            local progress = GetDeleteProgress(frame, tasks[1])
            local fullHeight = 56 + ROW_HEIGHT + 10
            height = fullHeight + ((EMPTY_HEIGHT - fullHeight) * progress)
        end
    end
    if animatingAutoHide then
        local animation = frame.autoHideAnimation
        local elapsed = tonumber(animation.elapsed) or 0
        local collapseProgress = EaseOut(math.max(0, math.min(elapsed / AUTO_HIDE_COLLAPSE_DURATION, 1)))
        local checkProgress = math.max(0, math.min((elapsed - AUTO_HIDE_COLLAPSE_DURATION) / AUTO_HIDE_CHECK_DURATION, 1))
        local scale = math.max(0.01, 1 - collapseProgress)
        local width = animation.startWidth or settings.width or DEFAULT_WIDTH
        height = animation.startHeight or height
        frame:SetAlpha(math.max(0, 1 - collapseProgress))
        frame:SetScale(scale)
        frame:ClearAllPoints()
        frame:SetPoint("CENTER", UIParent, "CENTER", (animation.centerOffsetX or 0) / scale, (animation.centerOffsetY or 0) / scale)
        frame:SetSize(width, height)
        if checkProgress > 0 then
            UpdateWeeklyTaskReminderAutoHideCheck(animation, checkProgress)
        else
            HideWeeklyTaskReminderAutoHideCheck()
        end
    else
        frame:SetAlpha(1)
        frame:SetScale(1)
        frame:SetClampedToScreen(true)
        frame:SetSize(settings.width or DEFAULT_WIDTH, height)
    end

    SyncWeeklyTaskReminderFrameLayers(frame)
    frame:Show()
end

function WipedQoL:SetupWeeklyTaskReminder()
    self:HookWeeklyTaskReminderQuestTrackerFrames()
    HookEditModeManagerFrame()

    if not self.weeklyTaskReminderEventFrame then
        local eventFrame = CreateFrame("Frame")
        eventFrame:SetScript("OnEvent", function(_, event)
            WipedQoL:HookWeeklyTaskReminderQuestTrackerFrames()
            HookEditModeManagerFrame()
            if event == "ENCOUNTER_START" then
                WipedQoL.weeklyTaskReminderInEncounter = true
            elseif event == "ENCOUNTER_END" or event == "PLAYER_ENTERING_WORLD" or event == "ZONE_CHANGED_NEW_AREA" then
                WipedQoL.weeklyTaskReminderInEncounter = nil
            end
            if WipedQoL:GetWeeklyTaskReminderSettings().enabled ~= true then
                return
            end

            if event == "EDIT_MODE_EXIT"
                or event == "EDIT_MODE_LAYOUTS_UPDATED"
                or event == "EDIT_MODE_SYSTEM_SETTINGS_CHANGED"
                or event == "UI_SCALE_CHANGED"
                or event == "DISPLAY_SIZE_CHANGED" then
                QueueQuestTrackerLayoutReanchors()
            end

            if C_Timer and C_Timer.After then
                C_Timer.After(0.1, function()
                    WipedQoL:RefreshWeeklyTaskReminder()
                end)
            else
                WipedQoL:RefreshWeeklyTaskReminder()
            end
        end)
        RegisterEventSafely(eventFrame, "PLAYER_ENTERING_WORLD")
        RegisterEventSafely(eventFrame, "ADDON_LOADED")
        RegisterEventSafely(eventFrame, "CHALLENGE_MODE_START")
        RegisterEventSafely(eventFrame, "CHALLENGE_MODE_COMPLETED")
        RegisterEventSafely(eventFrame, "CHALLENGE_MODE_RESET")
        RegisterEventSafely(eventFrame, "QUEST_LOG_UPDATE")
        RegisterEventSafely(eventFrame, "SUPER_TRACKING_CHANGED")
        RegisterEventSafely(eventFrame, "ZONE_CHANGED_NEW_AREA")
        RegisterEventSafely(eventFrame, "ENCOUNTER_START")
        RegisterEventSafely(eventFrame, "ENCOUNTER_END")
        RegisterEventSafely(eventFrame, "EDIT_MODE_EXIT")
        RegisterEventSafely(eventFrame, "EDIT_MODE_LAYOUTS_UPDATED")
        RegisterEventSafely(eventFrame, "EDIT_MODE_SYSTEM_SETTINGS_CHANGED")
        RegisterEventSafely(eventFrame, "UI_SCALE_CHANGED")
        RegisterEventSafely(eventFrame, "DISPLAY_SIZE_CHANGED")
        self.weeklyTaskReminderEventFrame = eventFrame
    end

    self:RefreshWeeklyTaskReminderWeek()
    self:RefreshWeeklyTaskReminder()
end
