-- Start Up for Dungeon Library
local ADDON_NAME, NS = ...
local L = LibStub("AceLocale-3.0"):GetLocale("MDungeonTeleports")
MDungeonTeleports.customFont = GameFontNormal:GetFont()

-- Save Function
local function SavePortalLibraryPosition()
    local DungeonFrame = PVEFrame
    if DungeonFrame then
        local frameScale = PortalLibrary:GetEffectiveScale()
        local parentScale = DungeonFrame:GetEffectiveScale()

        local PortalLibraryLeft = PortalLibrary:GetLeft() * frameScale
        local PortalLibraryRight = PortalLibrary:GetRight() * frameScale
        local PortalLibraryTop = PortalLibrary:GetTop() * frameScale
        local dungeonLeft = DungeonFrame:GetLeft() * parentScale
        local dungeonRight = DungeonFrame:GetRight() * parentScale
        local dungeonTop = DungeonFrame:GetTop() * parentScale

        local anchorToRight = (PortalLibraryLeft >= dungeonRight - 10)

        local offsetX, anchor
        if anchorToRight then
            offsetX = (PortalLibraryRight - dungeonRight) / parentScale
            anchor = "TOPRIGHT"
        else
            offsetX = (PortalLibraryLeft - dungeonLeft) / parentScale
            anchor = "TOPLEFT"
        end
        local offsetY = (PortalLibraryTop - dungeonTop) / parentScale

        MDungeonTeleports.PortalLibraryPos = {x = offsetX, y = offsetY, anchor = anchor}
    end
end

-- Load Function
local function LoadandReattatchPortalLibraryPosition()
    local DungeonFrame = PVEFrame
    if DungeonFrame and MDungeonTeleports.PortalLibraryPos then
        local pos = MDungeonTeleports.PortalLibraryPos

        local frameScale = PortalLibrary:GetEffectiveScale()
        local parentScale = DungeonFrame:GetEffectiveScale()

        local adjustedX = pos.x * parentScale / frameScale
        local adjustedY = pos.y * parentScale / frameScale

        PortalLibrary:ClearAllPoints()
        local anchor = pos.anchor or "TOPLEFT"
        PortalLibrary:SetPoint(anchor, DungeonFrame, anchor, adjustedX, adjustedY)
    end
end

-- Forward declaration
local UpdatePortalLibraryLayout
local CreateButtons

-- MARK: Login Start Up
local function OnPlayerLogin()
    CreateButtons()
    LoadandReattatchPortalLibraryPosition()
    UpdatePortalLibraryLayout()
    MDTUpdatePortalLibraryColors()
    MDTUpdatePortalLibraryBorder()
    MDTUpdateFontPL()
    MDTShiftDragToggleDL()
end

local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_LOGIN")
loginFrame:SetScript(
    "OnEvent",
    function(self, event)
        if event == "PLAYER_LOGIN" then
            OnPlayerLogin()
        end
    end
)

-- MARK: Visibility Toggle
function MDTPortalLibrary_UpdateVisibility(show)
    local portalLibraryFrame = _G["PortalLibrary"]
    if portalLibraryFrame then
        portalLibraryFrame:SetShown(show)
    end
end

-- MARK: Portal Library Frame
local PortalLibrary = CreateFrame("Frame", "PortalLibrary", PVEFrame)
PortalLibrary:SetSize(384, 630)
PortalLibrary:SetPoint("CENTER", PVEFrame, "CENTER", 900, 0)
PortalLibrary:SetMovable(true)
PortalLibrary:EnableMouse(true)
PortalLibrary:RegisterForDrag("LeftButton")
PortalLibrary:SetClampedToScreen(true)

PortalLibrary:SetScript(
    "OnDragStart",
    function(self)
        self.isBeingDragged = true
        self:StartMoving()
    end
)

PortalLibrary:SetScript(
    "OnDragStop",
    function(self)
        if self.isBeingDragged then
            self:StopMovingOrSizing()
            SavePortalLibraryPosition()
            self.isBeingDragged = false
            LoadandReattatchPortalLibraryPosition()
        end
    end
)

-- Border thickness
local borderThickness = 2

-- Portal Library Backdrop and Borders
local PortalLibraryBackdrop = PortalLibrary:CreateTexture(nil, "BACKGROUND")
PortalLibraryBackdrop:SetAllPoints(PortalLibrary)
PortalLibraryBackdrop:SetColorTexture(
    MDungeonTeleports.backgroundColor.r,
    MDungeonTeleports.backgroundColor.g,
    MDungeonTeleports.backgroundColor.b,
    MDungeonTeleports.backgroundColor.a
)

local borderTop = PortalLibrary:CreateTexture(nil, "OVERLAY")
borderTop:SetColorTexture(
    MDungeonTeleports.borderColor.r,
    MDungeonTeleports.borderColor.g,
    MDungeonTeleports.borderColor.b,
    MDungeonTeleports.borderColor.a
)
borderTop:SetPoint("TOPLEFT", PortalLibrary, "TOPLEFT", borderThickness, 0)
borderTop:SetPoint("TOPRIGHT", PortalLibrary, "TOPRIGHT", -borderThickness, 0)
borderTop:SetHeight(borderThickness)

local borderBottom = PortalLibrary:CreateTexture(nil, "OVERLAY")
borderBottom:SetColorTexture(
    MDungeonTeleports.borderColor.r,
    MDungeonTeleports.borderColor.g,
    MDungeonTeleports.borderColor.b,
    MDungeonTeleports.borderColor.a
)
borderBottom:SetPoint("BOTTOMLEFT", PortalLibrary, "BOTTOMLEFT", borderThickness, 0)
borderBottom:SetPoint("BOTTOMRIGHT", PortalLibrary, "BOTTOMRIGHT", -borderThickness, 0)
borderBottom:SetHeight(borderThickness)

local borderLeft = PortalLibrary:CreateTexture(nil, "OVERLAY")
borderLeft:SetColorTexture(
    MDungeonTeleports.borderColor.r,
    MDungeonTeleports.borderColor.g,
    MDungeonTeleports.borderColor.b,
    MDungeonTeleports.borderColor.a
)
borderLeft:SetPoint("TOPLEFT", PortalLibrary, "TOPLEFT", 0, 0)
borderLeft:SetPoint("BOTTOMLEFT", PortalLibrary, "BOTTOMLEFT", 0, 0)
borderLeft:SetWidth(borderThickness)

local borderRight = PortalLibrary:CreateTexture(nil, "OVERLAY")
borderRight:SetColorTexture(
    MDungeonTeleports.borderColor.r,
    MDungeonTeleports.borderColor.g,
    MDungeonTeleports.borderColor.b,
    MDungeonTeleports.borderColor.a
)
borderRight:SetPoint("TOPRIGHT", PortalLibrary, "TOPRIGHT", 0, 0)
borderRight:SetPoint("BOTTOMRIGHT", PortalLibrary, "BOTTOMRIGHT", 0, 0)
borderRight:SetWidth(borderThickness)

-- Portal Library Title
local PortalLibraryTitle = PortalLibrary:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
PortalLibraryTitle:SetPoint("TOP", PortalLibrary, "TOP", 0, -6)
PortalLibraryTitle:SetFont(MDungeonTeleports.customFont, 12, "OUTLINE")
PortalLibraryTitle:SetTextColor(1, 1, 1)
PortalLibraryTitle:SetText(L["DUNGEON_LIBRARY"])

-- MARK: Expansion Data Structure
local expansions = {
    {key = "MID", titleText = L["EXP_MID"], dataTable = MDT_PL_MID},
    {key = "TWW", titleText = L["EXP_TWW"], dataTable = MDT_PL_TWW},
    {key = "DF", titleText = L["EXP_DF"], dataTable = MDT_PL_DF},
    {key = "SL", titleText = L["EXP_SL"], dataTable = MDT_PL_SL},
    {key = "BFA", titleText = L["EXP_BFA"], dataTable = MDT_PL_BFA},
    {key = "Legion", titleText = L["EXP_LEG"], dataTable = MDT_PL_Legion},
    {key = "WOD", titleText = L["EXP_WOD"], dataTable = MDT_PL_WOD},
    {key = "MOP", titleText = L["EXP_MOP"], dataTable = MDT_PL_MOP},
    {key = "CATA", titleText = L["EXP_CATA"], dataTable = MDT_PL_CATA},
    {key = "WRATH", titleText = L["EXP_WRATH"], dataTable = MDT_PL_WRATH},
}

-- Create title font strings for each expansion
for _, expansion in ipairs(expansions) do
    local title = PortalLibrary:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
    title:SetFont(MDungeonTeleports.customFont, 12, "OUTLINE")
    title:SetTextColor(0.2, 0.8, 0.9)
    title:SetText(expansion.titleText)
    expansion.title = title
    expansion.buttons = {}
end

-- MARK: Time Formatting
local function FormatCooldownTime(seconds)
    if seconds >= 3600 then
        local hours = math.floor(seconds / 3600)
        return string.format("%dh", hours)
    elseif seconds >= 60 then
        local minutes = math.floor(seconds / 60)
        return string.format("%dm", minutes)
    else
        return string.format("%ds", math.floor(seconds))
    end
end

-- -MARK: Cooldown Text Function
local function UpdateCooldownText(button, spellID)
    if InCombatLockdown() then return end

    local cooldownFrame = button.cooldownText
    local cooldownOverlay = button.cooldownOverlay
    local spellCooldownInfo = C_Spell.GetSpellCooldown(spellID)

    if spellCooldownInfo then
        local startTime = spellCooldownInfo.startTime
        local duration = spellCooldownInfo.duration
        local isEnabled = spellCooldownInfo.isEnabled

        if not issecretvalue(startTime) and not issecretvalue(duration) then
            if startTime > 0 and duration > 0 and isEnabled then
                local remaining = max(0, (startTime + duration) - GetTime())

                if remaining > 5 then
                    cooldownFrame:SetText(FormatCooldownTime(remaining))
                    cooldownOverlay:Show()
                    return
                end
            end
        end
    end

    cooldownFrame:SetText("")
    cooldownOverlay:Hide()
end

-- MARK: Button Settings
local buttonSize = 32
local buttonSpacing = 5

local spellButtons = {}

-- MARK: Button Creation
local function CreateSpellButton(parent, data, expansion)
    local button = CreateFrame("Button", nil, parent, "SecureActionButtonTemplate")
    button:SetSize(buttonSize, buttonSize)

    button.data = data
    button.expansion = expansion

    local buttonTexture = button:CreateTexture(nil, "BACKGROUND")
    buttonTexture:SetAllPoints(button)
    buttonTexture:SetTexture(data.icon)
    buttonTexture:SetTexCoord(0.07, 0.93, 0.07, 0.93)
    button.buttonTexture = buttonTexture

    local cooldownOverlay = button:CreateTexture(nil, "ARTWORK")
    cooldownOverlay:SetAllPoints(button)
    cooldownOverlay:SetTexture("Interface\\Tooltips\\UI-Tooltip-Background")
    cooldownOverlay:SetVertexColor(0, 0, 0, 0.6)
    cooldownOverlay:Hide()
    button.cooldownOverlay = cooldownOverlay

    local hoverOverlay = button:CreateTexture(nil, "ARTWORK")
    hoverOverlay:SetAllPoints(button)
    hoverOverlay:SetColorTexture(0, 0, 0, 0.6)
    hoverOverlay:SetDrawLayer("ARTWORK", 1)
    hoverOverlay:Hide()
    button.hoverOverlay = hoverOverlay

    local buttonBorderThickness = 2
    local bbc = MDungeonTeleports.buttonBorderColor or { r = 0, g = 0, b = 0, a = 0.8 }

    local borderTop = button:CreateTexture(nil, "OVERLAY")
    borderTop:SetColorTexture(bbc.r, bbc.g, bbc.b, 1)
    borderTop:SetPoint("TOPLEFT", button, "TOPLEFT", buttonBorderThickness, 0)
    borderTop:SetPoint("TOPRIGHT", button, "TOPRIGHT", -buttonBorderThickness, 0)
    borderTop:SetHeight(buttonBorderThickness)
    button.borderTop = borderTop

    local borderBottom = button:CreateTexture(nil, "OVERLAY")
    borderBottom:SetColorTexture(bbc.r, bbc.g, bbc.b, 1)
    borderBottom:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", buttonBorderThickness, 0)
    borderBottom:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -buttonBorderThickness, 0)
    borderBottom:SetHeight(buttonBorderThickness)
    button.borderBottom = borderBottom

    local borderLeft = button:CreateTexture(nil, "OVERLAY")
    borderLeft:SetColorTexture(bbc.r, bbc.g, bbc.b, 1)
    borderLeft:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0)
    borderLeft:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", 0, 0)
    borderLeft:SetWidth(buttonBorderThickness)
    button.borderLeft = borderLeft

    local borderRight = button:CreateTexture(nil, "OVERLAY")
    borderRight:SetColorTexture(bbc.r, bbc.g, bbc.b, 1)
    borderRight:SetPoint("TOPRIGHT", button, "TOPRIGHT", 0, 0)
    borderRight:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 0, 0)
    borderRight:SetWidth(buttonBorderThickness)
    button.borderRight = borderRight

    local buttonText = button:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    buttonText:SetPoint("TOP", button, "BOTTOM", 1, 6)
    buttonText:SetFont(MDungeonTeleports.customFont, 11, "OUTLINE")
    buttonText:SetTextColor(1, 1, 1)
    buttonText:SetText(data.name)
    button.buttonText = buttonText

    local cooldownText = button:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
    cooldownText:SetPoint("CENTER", button, "CENTER", 1, 0)
    cooldownText:SetTextColor(1, 1, 1)
    cooldownText:SetFont(MDungeonTeleports.customFont, 14, "OUTLINE")
    button.cooldownText = cooldownText

    button:SetAttribute("type", "spell")
    button:SetAttribute("spell", data.spellID)

    table.insert(spellButtons, button)

        local function UpdateButtonTexture()
            if C_SpellBook.IsSpellInSpellBook(data.spellID) then
                buttonTexture:SetTexture(data.icon)
                buttonTexture:SetTexCoord(0.07, 0.93, 0.07, 0.93)
                buttonTexture:SetDesaturated(false)
            else
                for _, lock in ipairs(AltLocks) do
                    if lock.spellID == data.spellID then
                        if MDungeonTeleports.LockIcons then
                            buttonTexture:SetTexture("Interface\\AddOns\\MDungeonTeleports\\media\\ui\\lock.blp")
                        else
                            buttonTexture:SetTexture(lock.icon)
                        end
                        buttonTexture:SetTexCoord(0.07, 0.93, 0.07, 0.93)
                        buttonTexture:SetDesaturated(true)
                        return
                    end
                end
                buttonTexture:SetTexture(data.icon)
                buttonTexture:SetTexCoord(0.07, 0.93, 0.07, 0.93)
                buttonTexture:SetDesaturated(true)
            end
        end

    button.UpdateButtonTexture = UpdateButtonTexture
    UpdateButtonTexture()

    local function OnEvent(self, event)
        if event == "SPELLS_CHANGED" then
            UpdateButtonTexture()
            UpdateCooldownText(self, data.spellID)
        elseif event == "PLAYER_ENTERING_WORLD" then
            UpdateButtonTexture()
            UpdateCooldownText(self, data.spellID)
        elseif event == "CHALLENGE_MODE_COMPLETED" then
            C_Timer.After(2, function()
                UpdateButtonTexture()
                UpdateCooldownText(self, data.spellID)
            end)
        end
    end

    button:SetScript("OnEnter", function(self)
        if MDungeonTeleports.showTooltips then
            GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
            GameTooltip:SetSpellByID(data.spellID)
            GameTooltip:Show()
        end
        hoverOverlay:Show()
    end)

    button:SetScript("OnLeave", function(self)
        GameTooltip:Hide()
        hoverOverlay:Hide()
    end)

    button:RegisterEvent("CHALLENGE_MODE_COMPLETED")
    button:RegisterEvent("PLAYER_ENTERING_WORLD")
    button:RegisterEvent("SPELLS_CHANGED")
    button:RegisterForClicks("AnyUp", "AnyDown")
    button:SetScript("OnEvent", OnEvent)

    return button
end

function MDTUpdatePLButtonTexture()
    for _, expansion in ipairs(expansions) do
        for _, button in ipairs(expansion.buttons) do
            if button.UpdateButtonTexture then
                button:UpdateButtonTexture()
            end
        end
    end
end

-- MARK: Create Buttons for Each Expansion
CreateButtons = function()
    for _, expansion in ipairs(expansions) do
        for i, data in ipairs(expansion.dataTable) do
            data.index = i
            local button = CreateSpellButton(PortalLibrary, data, expansion)
            table.insert(expansion.buttons, button)
        end
    end
end

-- MARK: Dynamic Layout Function
UpdatePortalLibraryLayout = function()
    -- Initialize visibility settings if not present
    if not MDungeonTeleports.expansionVisibility then
        MDungeonTeleports.expansionVisibility = {}
        for _, expansion in ipairs(expansions) do
            MDungeonTeleports.expansionVisibility[expansion.key] = true
        end
    end

    local titleTopMargin = 22
    local expansionSpacing = 54
    local currentYOffset = -titleTopMargin
    
    for _, expansion in ipairs(expansions) do
        local isVisible = MDungeonTeleports.expansionVisibility[expansion.key]
        
        if isVisible then
            -- Position and show title
            expansion.title:ClearAllPoints()
            expansion.title:SetPoint("TOP", PortalLibrary, "TOP", 0, currentYOffset)
            if MDungeonTeleports.TextExpansionToggle ~= false then
                expansion.title:Show()
            end
            
            -- Position and show buttons
            local buttonYOffset = currentYOffset - 14
            local visibleButtons = {}
            for _, button in ipairs(expansion.buttons) do
                if MDungeonTeleports.hideMissingPortals and not C_SpellBook.IsSpellInSpellBook(button.data.spellID) then
                    button:Hide()
                else
                    table.insert(visibleButtons, button)
                end
            end

            if #visibleButtons > 0 then
                local numVisible = #visibleButtons
                local startX
                if MDungeonTeleports.centerDungeonPortals then
                    local totalWidth = numVisible * buttonSize + (numVisible - 1) * buttonSpacing
                    local frameWidth = PortalLibrary:GetWidth()
                    startX = (frameWidth - totalWidth) / 2
                else
                    startX = 10
                end
                for idx, button in ipairs(visibleButtons) do
                    local xOffset = startX + (idx - 1) * (buttonSize + buttonSpacing)
                    button:ClearAllPoints()
                    button:SetPoint("TOPLEFT", PortalLibrary, "TOPLEFT", xOffset, buttonYOffset)
                    button:Show()
                end
                currentYOffset = currentYOffset - expansionSpacing
            else
                -- All buttons hidden, hide title too
                expansion.title:Hide()
            end
        else
            -- Hide title and buttons
            expansion.title:Hide()
            for _, button in ipairs(expansion.buttons) do
                button:Hide()
            end
        end
    end
    
    -- Calculate and set frame height
    local visibleCount = 0
    for _, expansion in ipairs(expansions) do
        if MDungeonTeleports.expansionVisibility[expansion.key] then
            local hasVisible = false
            if MDungeonTeleports.hideMissingPortals then
                for _, button in ipairs(expansion.buttons) do
                    if C_SpellBook.IsSpellInSpellBook(button.data.spellID) then
                        hasVisible = true
                        break
                    end
                end
            else
                hasVisible = true
            end
            if hasVisible then
                visibleCount = visibleCount + 1
            end
        end
    end
    
    local frameHeight = titleTopMargin + 6 + (visibleCount * expansionSpacing)
    PortalLibrary:SetHeight(frameHeight)
end

-- MARK: Refresh Layout (global wrapper)
function MDTRefreshPortalLibraryLayout()
    UpdatePortalLibraryLayout()
end

-- MARK: Toggle Expansion Visibility
function MDTToggleExpansionVisibility(expansionKey)
    if not MDungeonTeleports.expansionVisibility then
        MDungeonTeleports.expansionVisibility = {}
        for _, expansion in ipairs(expansions) do
            MDungeonTeleports.expansionVisibility[expansion.key] = true
        end
    end
    
    MDungeonTeleports.expansionVisibility[expansionKey] = not MDungeonTeleports.expansionVisibility[expansionKey]
    UpdatePortalLibraryLayout()
end

-- MARK: Show All Expansions
function MDTShowAllExpansions()
    if not MDungeonTeleports.expansionVisibility then
        MDungeonTeleports.expansionVisibility = {}
    end
    
    for _, expansion in ipairs(expansions) do
        MDungeonTeleports.expansionVisibility[expansion.key] = true
    end
    UpdatePortalLibraryLayout()
end

-- MARK: Hide All Expansions
function MDTHideAllExpansions()
    if not MDungeonTeleports.expansionVisibility then
        MDungeonTeleports.expansionVisibility = {}
    end
    
    for _, expansion in ipairs(expansions) do
        MDungeonTeleports.expansionVisibility[expansion.key] = false
    end
    UpdatePortalLibraryLayout()
end

-- MARK: Shift Drag Toggle
function MDTShiftDragToggleDL()
    if MDungeonTeleports.ShiftDragToggle == true then
        PortalLibrary:SetScript("OnDragStart", function(self)
            if IsShiftKeyDown() then
                self.isBeingDragged = true
                self:StartMoving()
            end
        end)
    else
        PortalLibrary:SetScript("OnDragStart", function(self)
            self.isBeingDragged = true
            self:StartMoving()
        end)
    end
end

-- MARK: Backdrop and Border Colour Update
function MDTUpdatePortalLibraryColors()
    local bc = MDungeonTeleports.borderColor
    local bg = MDungeonTeleports.backgroundColor
    local bbc = MDungeonTeleports.buttonBorderColor or { r = 0.05, g = 0.05, b = 0.05, a = 0.8 }

    if PortalLibraryBackdrop then
        PortalLibraryBackdrop:SetColorTexture(bg.r, bg.g, bg.b, bg.a)
    end

    if borderTop then borderTop:SetColorTexture(bc.r, bc.g, bc.b, bc.a) end
    if borderBottom then borderBottom:SetColorTexture(bc.r, bc.g, bc.b, bc.a) end
    if borderLeft then borderLeft:SetColorTexture(bc.r, bc.g, bc.b, bc.a) end
    if borderRight then borderRight:SetColorTexture(bc.r, bc.g, bc.b, bc.a) end

    for _, expansion in ipairs(expansions) do
        for _, button in ipairs(expansion.buttons) do
            if button.borderTop then
                button.borderTop:SetColorTexture(bbc.r, bbc.g, bbc.b, 1)
            end
            if button.borderBottom then
                button.borderBottom:SetColorTexture(bbc.r, bbc.g, bbc.b, 1)
            end
            if button.borderLeft then
                button.borderLeft:SetColorTexture(bbc.r, bbc.g, bbc.b, 1)
            end
            if button.borderRight then
                button.borderRight:SetColorTexture(bbc.r, bbc.g, bbc.b, 1)
            end
        end
    end
end

-- MARK: Border Thickness Update
function MDTUpdatePortalLibraryBorder()
    local borderThickness = MDungeonTeleports.borderThickness or 2

    if borderThickness == 0 then 
        borderTop:Hide() borderBottom:Hide() borderLeft:Hide() borderRight:Hide()
    else 
        borderTop:Show() borderBottom:Show() borderLeft:Show() borderRight:Show() 
    end

    borderTop:SetHeight(borderThickness)
    borderTop:ClearAllPoints()
    borderTop:SetPoint("TOPLEFT", PortalLibrary, "TOPLEFT", borderThickness, 0)
    borderTop:SetPoint("TOPRIGHT", PortalLibrary, "TOPRIGHT", -borderThickness, 0)

    borderBottom:SetHeight(borderThickness)
    borderBottom:ClearAllPoints()
    borderBottom:SetPoint("BOTTOMLEFT", PortalLibrary, "BOTTOMLEFT", borderThickness, 0)
    borderBottom:SetPoint("BOTTOMRIGHT", PortalLibrary, "BOTTOMRIGHT", -borderThickness, 0)

    borderLeft:SetWidth(borderThickness)
    borderLeft:ClearAllPoints()
    borderLeft:SetPoint("TOPLEFT", PortalLibrary, "TOPLEFT", 0, 0)
    borderLeft:SetPoint("BOTTOMLEFT", PortalLibrary, "BOTTOMLEFT", 0, 0)

    borderRight:SetWidth(borderThickness)
    borderRight:ClearAllPoints()
    borderRight:SetPoint("TOPRIGHT", PortalLibrary, "TOPRIGHT", 0, 0)
    borderRight:SetPoint("BOTTOMRIGHT", PortalLibrary, "BOTTOMRIGHT", 0, 0)
end

-- MARK: Font Update
function MDTUpdateFontPL()
    local customFont = MDungeonTeleports.customFont
    local TextSizeTitle = MDungeonTeleports.TextSizeTitle or 12
    local TextFlags = MDungeonTeleports.customFontFlags or "OUTLINE"
    PortalLibraryTitle:SetFont(customFont, TextSizeTitle, TextFlags)

    local tx = MDungeonTeleports.expansionColor
    local tx2 = MDungeonTeleports.titleColor
    local tx3 = MDungeonTeleports.buttonTextColor

    PortalLibraryTitle:SetTextColor(tx2.r, tx2.g, tx2.b)

    local TextSizeButton = MDungeonTeleports.TextSizeButton or 11
    local TextSizeExpansion = MDungeonTeleports.TextSizeExpansion or 12

    -- Update all expansion titles
    for _, expansion in ipairs(expansions) do
        expansion.title:SetFont(customFont, TextSizeExpansion, TextFlags)
        expansion.title:SetTextColor(tx.r, tx.g, tx.b)
        
        -- Update all buttons for this expansion
        for _, button in ipairs(expansion.buttons) do
            local buttonText = button.buttonText
            if buttonText then
                buttonText:SetFont(customFont, TextSizeButton, TextFlags)
                local spellID = button.data and button.data.spellID
                if spellID and not C_SpellBook.IsSpellInSpellBook(spellID) and MDungeonTeleports.DarkenText then
                    buttonText:SetTextColor(0.5, 0.5, 0.5)
                else
                    buttonText:SetTextColor(tx3.r, tx3.g, tx3.b)
                end
            end
            local cooldownText = button.cooldownText
            if cooldownText then
                cooldownText:SetFont(customFont, 14, TextFlags)
            end
        end
    end

    local TitleToggle = MDungeonTeleports.TextTitleToggle
    if TitleToggle == nil then
        TitleToggle = true
    end
    if TitleToggle then
        PortalLibraryTitle:Show()
    else
        PortalLibraryTitle:Hide()
    end

    local ExpansionTextToggle = MDungeonTeleports.TextExpansionToggle
    if ExpansionTextToggle == nil then
        ExpansionTextToggle = true
    end
    for _, expansion in ipairs(expansions) do
        if expansion.title then
            local isExpansionVisible = MDungeonTeleports.expansionVisibility == nil or
                MDungeonTeleports.expansionVisibility[expansion.key] ~= false
            local hasVisibleButtons = true
            if isExpansionVisible and MDungeonTeleports.hideMissingPortals then
                hasVisibleButtons = false
                for _, button in ipairs(expansion.buttons) do
                    if C_SpellBook.IsSpellInSpellBook(button.data.spellID) then
                        hasVisibleButtons = true
                        break
                    end
                end
            end
            if ExpansionTextToggle and isExpansionVisible and hasVisibleButtons then
                expansion.title:Show()
            else
                expansion.title:Hide()
            end
        end
    end

    local ButtonTextToggle = MDungeonTeleports.TextButtonToggle
    if ButtonTextToggle == nil then
        ButtonTextToggle = true
    end
    for _, button in ipairs(spellButtons) do
        if button.buttonText then
            local expansion = button.expansion
            local isExpansionVisible = expansion == nil or MDungeonTeleports.expansionVisibility == nil or
                MDungeonTeleports.expansionVisibility[expansion.key] ~= false
            local isButtonVisible = not (MDungeonTeleports.hideMissingPortals and
                not C_SpellBook.IsSpellInSpellBook(button.data.spellID))
            if ButtonTextToggle and isExpansionVisible and isButtonVisible then
                button.buttonText:Show()
            else
                button.buttonText:Hide()
            end
        end
    end
end

local function SetButtonEventsEnabled(enabled)
    for _, expansion in ipairs(expansions) do
        for _, button in ipairs(expansion.buttons) do
            if enabled then
                button:RegisterEvent("SPELL_UPDATE_COOLDOWN")
                button:RegisterEvent("SPELLS_CHANGED")
            else
                button:UnregisterEvent("SPELL_UPDATE_COOLDOWN")
                button:UnregisterEvent("SPELLS_CHANGED")
            end
        end
    end
end

local combatFrame = CreateFrame("Frame")
combatFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
combatFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
combatFrame:SetScript("OnEvent", function(self, event)
    if event == "PLAYER_REGEN_DISABLED" then
        SetButtonEventsEnabled(false)
    elseif event == "PLAYER_REGEN_ENABLED" then
        SetButtonEventsEnabled(true)
    end
end)