local _, ns = ...
local L = ns.L or {}
local ASSET_ROOT = "Interface\\AddOns\\Vaultloom\\Assets\\"
local SPEC_SWITCHER_RING_TEXTURE = ASSET_ROOT .. "dock_ring_clean.tga"

local ProfessionKnowledgeBadgeRuntime = {
    enabled = false,
    hooksReady = false,
    loadHookRegistered = false,
    widgets = {},
}

function ProfessionKnowledgeBadgeRuntime:HideAllBadges()
    for _, badge in pairs(self.widgets or {}) do
        badge:Hide()
    end
end

function ProfessionKnowledgeBadgeRuntime:EnsureBadge(slotIndex, parentButton)
    if not parentButton then
        return nil
    end

    local badge = self.widgets[slotIndex]
    if not badge then
        badge = CreateFrame("Button", nil, parentButton)
        badge:SetSize(20, 20)
        badge:SetFrameStrata(parentButton:GetFrameStrata() or "HIGH")
        badge:SetFrameLevel((parentButton:GetFrameLevel() or 0) + 8)
        badge:EnableMouse(true)
        badge:SetHitRectInsets(-2, -2, -2, -2)

        badge.fill = badge:CreateTexture(nil, "BACKGROUND")
        badge.fill:SetPoint("TOPLEFT", 2, -2)
        badge.fill:SetPoint("BOTTOMRIGHT", -2, 2)
        badge.fill:SetTexture("Interface\\Buttons\\WHITE8X8")
        badge.fill:SetColorTexture(0.07, 0.07, 0.09, 0.94)

        badge.fillMask = badge:CreateMaskTexture(nil, "ARTWORK")
        badge.fillMask:SetTexture("Interface\\CHARACTERFRAME\\TempPortraitAlphaMask", "CLAMPTOBLACKADDITIVE", "CLAMPTOBLACKADDITIVE")
        badge.fillMask:SetPoint("TOPLEFT", badge.fill, "TOPLEFT", 0, 0)
        badge.fillMask:SetPoint("BOTTOMRIGHT", badge.fill, "BOTTOMRIGHT", 0, 0)
        badge.fill:AddMaskTexture(badge.fillMask)

        badge.ring = badge:CreateTexture(nil, "OVERLAY")
        badge.ring:SetPoint("TOPLEFT", 0, 0)
        badge.ring:SetPoint("BOTTOMRIGHT", 0, 0)
        badge.ring:SetTexture(SPEC_SWITCHER_RING_TEXTURE)
        badge.ring:SetVertexColor(0.88, 0.76, 0.30, 0.98)

        badge.text = badge:CreateFontString(nil, "OVERLAY")
        if type(badge.text.SetFont) == "function" then
            badge.text:SetFont(STANDARD_TEXT_FONT, 8, "OUTLINE")
        end
        badge.text:SetPoint("CENTER", 0, 0)
        badge.text:SetTextColor(1, 0.94, 0.78, 0.98)
        badge.text:SetShadowOffset(1, -1)
        badge.text:SetShadowColor(0, 0, 0, 1)

        badge:SetScript("OnEnter", function(selfBadge)
            if not selfBadge.points or selfBadge.points <= 0 then
                return
            end
            GameTooltip:SetOwner(selfBadge, "ANCHOR_RIGHT")
            GameTooltip:AddLine(selfBadge.professionName or (L.SUBTAB_SYSTEMS_PROFESSIONS or "Professions"), 1, 0.82, 0.22, true)
            GameTooltip:AddLine(
                string.format(
                    L.PROFESSION_KNOWLEDGE_BADGE_TOOLTIP or "%d unspent profession knowledge points.",
                    tonumber(selfBadge.points) or 0
                ),
                1,
                1,
                1,
                true
            )
            GameTooltip:Show()
        end)
        badge:SetScript("OnLeave", function()
            GameTooltip:Hide()
        end)

        self.widgets[slotIndex] = badge
    end

    badge:SetParent(parentButton)
    badge:ClearAllPoints()
    badge:SetPoint("RIGHT", parentButton, "TOPLEFT", 4, -5)
    return badge
end

function ProfessionKnowledgeBadgeRuntime:Refresh()
    if self.enabled ~= true then
        self:HideAllBadges()
        return
    end

    local state = ns.BuildCurrentProfessionKnowledgeState and ns:BuildCurrentProfessionKnowledgeState() or nil
    local pointsBySlot = {}
    if type(state) == "table" then
        for _, entry in ipairs(state.professions or {}) do
            local slotIndex = tonumber(entry and entry.slotIndex)
            if slotIndex and slotIndex > 0 then
                pointsBySlot[slotIndex] = entry
            end
        end
    end

    for slotIndex = 1, 2 do
        local parentButton = _G[string.format("PrimaryProfession%dSpellButtonBottom", slotIndex)]
        local entry = pointsBySlot[slotIndex]
        local badge = parentButton and self:EnsureBadge(slotIndex, parentButton) or self.widgets[slotIndex]
        if badge and parentButton and entry and (tonumber(entry.points) or 0) > 0 then
            local points = tonumber(entry.points) or 0
            badge.points = points
            badge.professionName = entry.name
            badge.text:SetText(points > 99 and "99+" or tostring(points))
            badge:Show()
        elseif badge then
            badge:Hide()
        end
    end
end

function ProfessionKnowledgeBadgeRuntime:QueueRefresh(delay)
    if self.refreshQueued then
        return
    end

    self.refreshQueued = true
    if C_Timer and type(C_Timer.After) == "function" then
        C_Timer.After(delay or 0.08, function()
            ProfessionKnowledgeBadgeRuntime.refreshQueued = false
            ProfessionKnowledgeBadgeRuntime:Refresh()
        end)
    else
        self.refreshQueued = false
        self:Refresh()
    end
end

function ProfessionKnowledgeBadgeRuntime:ListenEvents(listen)
    if not self.eventFrame then
        self.eventFrame = CreateFrame("Frame")
        self.eventFrame:SetScript("OnEvent", function()
            ProfessionKnowledgeBadgeRuntime:QueueRefresh(0.10)
        end)
    end

    self.eventFrame:UnregisterAllEvents()
    if not listen then
        return
    end

    self.eventFrame:RegisterEvent("TRAIT_TREE_CURRENCY_INFO_UPDATED")
    self.eventFrame:RegisterEvent("TRAIT_CONFIG_UPDATED")
    self.eventFrame:RegisterEvent("SKILL_LINES_CHANGED")
end

function ProfessionKnowledgeBadgeRuntime:HookProfessionBook()
    if self.hooksReady or not ProfessionsBookFrame then
        return self.hooksReady
    end

    ProfessionsBookFrame:HookScript("OnShow", function()
        if ProfessionKnowledgeBadgeRuntime.enabled == true then
            ProfessionKnowledgeBadgeRuntime:ListenEvents(true)
            ProfessionKnowledgeBadgeRuntime:QueueRefresh(0.12)
        end
    end)
    ProfessionsBookFrame:HookScript("OnHide", function()
        ProfessionKnowledgeBadgeRuntime:ListenEvents(false)
        ProfessionKnowledgeBadgeRuntime:HideAllBadges()
    end)

    self.hooksReady = true
    if self.enabled == true and ProfessionsBookFrame:IsShown() then
        self:ListenEvents(true)
        self:QueueRefresh(0.08)
    end

    return true
end

function ProfessionKnowledgeBadgeRuntime:EnsureHooks()
    if self.hooksReady then
        return true
    end

    if not self.loadWatchFrame then
        self.loadWatchFrame = CreateFrame("Frame")
        self.loadWatchFrame:SetScript("OnEvent", function(selfFrame)
            if ProfessionKnowledgeBadgeRuntime:EnsureHooks() then
                selfFrame:UnregisterEvent("ADDON_LOADED")
            end
        end)
    end

    if not self.loadHookRegistered and type(hooksecurefunc) == "function" and type(ProfessionsBook_LoadUI) == "function" then
        hooksecurefunc("ProfessionsBook_LoadUI", function()
            ProfessionKnowledgeBadgeRuntime:HookProfessionBook()
        end)
        self.loadHookRegistered = true
    end

    if not self.hooksReady then
        self.loadWatchFrame:RegisterEvent("ADDON_LOADED")
    end

    if ProfessionsBookFrame then
        return self:HookProfessionBook()
    end

    return self.loadHookRegistered
end

function ProfessionKnowledgeBadgeRuntime:SetEnabled(enabled)
    self.enabled = enabled == true
    self:EnsureHooks()

    if self.enabled ~= true then
        self:ListenEvents(false)
        self:HideAllBadges()
        return
    end

    if self.hooksReady and ProfessionsBookFrame and ProfessionsBookFrame:IsShown() then
        self:ListenEvents(true)
        self:QueueRefresh(0.08)
    end
end

ns:RegisterFeatureDefinition("profession_knowledge_badges", {
    id = "profession_knowledge_badges",
    order = 31,
    category = "blizzard",
    title = function() return L.FEATURE_PROFESSION_KNOWLEDGE_BADGES_TITLE or "Profession Knowledge Badges" end,
    description = function() return L.FEATURE_PROFESSION_KNOWLEDGE_BADGES_DESCRIPTION or "Adds custom unspent knowledge badges to the Blizzard profession book." end,
    note = function() return L.FEATURE_PROFESSION_KNOWLEDGE_BADGES_NOTE or "Shows a small custom badge with the available point count next to each primary profession." end,
    previewLabel = function() return L.FEATURE_PROFESSION_KNOWLEDGE_BADGES_PREVIEW_LABEL or "Profession Book" end,
    previewNote = function() return L.FEATURE_PROFESSION_KNOWLEDGE_BADGES_PREVIEW_NOTE or "Custom point badges next to the Blizzard profession icons when unspent knowledge is available." end,
    requiresReload = false,
    apply = function(isEnabled)
        ProfessionKnowledgeBadgeRuntime:SetEnabled(isEnabled == true)
    end,
})
local ProfessionKnowledgeUIRuntime = {
    hooksReady = false,
}

function ProfessionKnowledgeUIRuntime:EnsureSubTabBadge(button)
    if not button then
        return nil
    end

    local badge = button.vlProfessionKnowledgeBadge
    if badge then
        return badge
    end

    badge = CreateFrame("Frame", nil, button, BACKDROP_TEMPLATE)
    badge:SetSize(14, 14)
    badge:SetFrameLevel((button:GetFrameLevel() or 0) + 6)
    if badge.SetBackdrop then
        badge:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            tile = false,
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 },
        })
        badge:SetBackdropColor(0.08, 0.08, 0.10, 0.96)
        badge:SetBackdropBorderColor(0.76, 0.64, 0.24, 0.90)
    end
    badge:SetPoint("RIGHT", button, "RIGHT", -7, 0)

    badge.text = badge:CreateFontString(nil, "OVERLAY")
    if type(badge.text.SetFont) == "function" then
        badge.text:SetFont(STANDARD_TEXT_FONT, 10, "OUTLINE")
    end
    badge.text:SetPoint("CENTER", 0, 0)
    badge.text:SetText("+")
    badge.text:SetTextColor(1, 0.94, 0.78, 0.98)
    badge:Hide()

    button.vlProfessionKnowledgeBadge = badge
    return badge
end

function ProfessionKnowledgeUIRuntime:EnsureChip(card, index)
    if not card then
        return nil
    end

    card.vlProfessionKnowledgeChips = card.vlProfessionKnowledgeChips or {}
    local chip = card.vlProfessionKnowledgeChips[index]
    if chip then
        return chip
    end

    chip = CreateFrame("Frame", nil, card, BACKDROP_TEMPLATE)
    chip:SetHeight(18)
    chip:SetFrameLevel((card:GetFrameLevel() or 0) + 6)
    if chip.SetBackdrop then
        chip:SetBackdrop({
            bgFile = "Interface\\Buttons\\WHITE8X8",
            edgeFile = "Interface\\Buttons\\WHITE8X8",
            tile = false,
            edgeSize = 1,
            insets = { left = 1, right = 1, top = 1, bottom = 1 },
        })
        chip:SetBackdropColor(0.08, 0.08, 0.10, 0.96)
        chip:SetBackdropBorderColor(0.56, 0.48, 0.22, 0.84)
    end

    chip.text = chip:CreateFontString(nil, "OVERLAY")
    if type(chip.text.SetFont) == "function" then
        chip.text:SetFont(STANDARD_TEXT_FONT, 10, "OUTLINE")
    end
    chip.text:SetPoint("LEFT", 7, 0)
    chip.text:SetPoint("RIGHT", -7, 0)
    chip.text:SetJustifyH("CENTER")
    chip.text:SetTextColor(1, 0.94, 0.78, 0.96)
    chip:Hide()

    card.vlProfessionKnowledgeChips[index] = chip
    return chip
end

function ProfessionKnowledgeUIRuntime:RefreshSubTabBadge(ui)
    if not ui or not ui.frame or type(ui.frame.subTabs) ~= "table" then
        return
    end

    for _, button in ipairs(ui.frame.subTabs) do
        local badge = button.vlProfessionKnowledgeBadge
        if badge then
            badge:Hide()
        end
    end
end

function ProfessionKnowledgeUIRuntime:RefreshProfessionChips(ui)
    if not ui or not ui.frame or not ui.frame.pveWeeklyCard then
        return
    end

    local card = ui.frame.pveWeeklyCard
    if card.summary3 then
        card.summary3:SetText("")
    end

    for index = 1, 2 do
        local chip = card.vlProfessionKnowledgeChips and card.vlProfessionKnowledgeChips[index] or nil
        if chip then
            chip:Hide()
        end
    end
end

function ProfessionKnowledgeUIRuntime:EnsureHooks()
    if self.hooksReady then
        return true
    end

    if type(ns.UI) ~= "table" or type(hooksecurefunc) ~= "function" then
        return false
    end

    if type(ns.UI.RefreshSubTabs) == "function" then
        hooksecurefunc(ns.UI, "RefreshSubTabs", function(ui)
            ProfessionKnowledgeUIRuntime:RefreshSubTabBadge(ui)
        end)
    end
    if type(ns.UI.RefreshContent) == "function" then
        hooksecurefunc(ns.UI, "RefreshContent", function(ui)
            ProfessionKnowledgeUIRuntime:RefreshSubTabBadge(ui)
            ProfessionKnowledgeUIRuntime:RefreshProfessionChips(ui)
        end)
    end
    if type(ns.UI.RefreshProfessionWeeklyCard) == "function" then
        hooksecurefunc(ns.UI, "RefreshProfessionWeeklyCard", function(ui)
            ProfessionKnowledgeUIRuntime:RefreshProfessionChips(ui)
        end)
    end

    if not self.eventFrame then
        self.eventFrame = CreateFrame("Frame")
        self.eventFrame:SetScript("OnEvent", function()
            if ns.UI and ns.UI.frame and ns.UI.frame:IsShown() then
                ProfessionKnowledgeUIRuntime:RefreshSubTabBadge(ns.UI)
                ProfessionKnowledgeUIRuntime:RefreshProfessionChips(ns.UI)
            end
        end)
        self.eventFrame:RegisterEvent("TRAIT_TREE_CURRENCY_INFO_UPDATED")
        self.eventFrame:RegisterEvent("TRAIT_CONFIG_UPDATED")
        self.eventFrame:RegisterEvent("SKILL_LINES_CHANGED")
        self.eventFrame:RegisterEvent("CURRENCY_DISPLAY_UPDATE")
    end

    self.hooksReady = true
    if ns.UI and ns.UI.frame and ns.UI.frame:IsShown() then
        self:RefreshSubTabBadge(ns.UI)
        self:RefreshProfessionChips(ns.UI)
    end
    return true
end

local function initializeProfessionKnowledgeUiRuntime()
    if ProfessionKnowledgeUIRuntime:EnsureHooks() then
        return
    end

    if ProfessionKnowledgeUIRuntime.initFrame then
        return
    end

    local initFrame = CreateFrame("Frame")
    ProfessionKnowledgeUIRuntime.initFrame = initFrame
    local elapsedTotal = 0
    initFrame:SetScript("OnUpdate", function(selfFrame, elapsed)
        elapsedTotal = elapsedTotal + (tonumber(elapsed) or 0)
        if elapsedTotal < 0.40 then
            return
        end

        elapsedTotal = 0
        if ProfessionKnowledgeUIRuntime:EnsureHooks() then
            selfFrame:SetScript("OnUpdate", nil)
        end
    end)
end

if C_Timer and type(C_Timer.After) == "function" then
    C_Timer.After(0, initializeProfessionKnowledgeUiRuntime)
else
    initializeProfessionKnowledgeUiRuntime()
end
