--[[
============================================================
Decor Vendor Addon
© 2026 MidniteDestiny. All Rights Reserved.
============================================================

This file is part of the Decor Vendor addon.

All code, structure, and design are the intellectual
property of MidniteDestiny unless otherwise stated.

You may NOT:
• Copy, reproduce, or redistribute this code
• Modify and redistribute this code
• Use this code in other addons or projects

without explicit permission from the author.

This addon is distributed for personal use only.

============================================================
]]
local addonName, dv = ...

-------------------------------------------------
-- Patch / Interface Availability Gate
-------------------------------------------------

function dv.GetClientInterface()
    local DVD = _G.DecorVendorData

    if DVD and DVD.GetClientInterface then
        return DVD.GetClientInterface()
    end

    local _, _, _, tocVersion = GetBuildInfo()
    return tonumber(tocVersion) or 0
end

function dv.IsDataAvailableForClient(data)
    local DVD = _G.DecorVendorData

    if DVD and DVD.IsDataAvailableForClient then
        return DVD.IsDataAvailableForClient(data)
    end

    return true
end

function dv.IsItemAvailableForClient(itemID)
    local DVD = _G.DecorVendorData

    if DVD and DVD.IsItemAvailableForClient then
        return DVD.IsItemAvailableForClient(itemID)
    end

    return true
end
-------------------------------------------------
-- Accordion Header Helper
-- Opens one header and collapses the others in the same tab.
-------------------------------------------------

function dv.ToggleAccordionHeader(tab, openKey)
    dv.collapsedHeaders = dv.collapsedHeaders or {}

    local wasOpen = dv.collapsedHeaders[openKey] == false

    -- Collapse all headers for the current tab first.
    if tab == "vendors" then
        for _, group in ipairs(dv.npcs or {}) do
            if group.name then
                dv.collapsedHeaders[group.name] = true
            end
        end

    elseif tab == "professions" then
        for _, profession in ipairs(dv.professions or {}) do
            if profession.name then
                dv.collapsedHeaders["prof_" .. profession.name] = true
            end
        end

    elseif tab == "quests" then
        for _, group in ipairs(dv.quests or {}) do
            if group.name then
                dv.collapsedHeaders["quest_" .. group.name] = true
            end
        end

    elseif tab == "achievements" then
        for _, group in ipairs(dv.achievements or {}) do
            if group.name then
                dv.collapsedHeaders["ach_" .. group.name] = true
            end
        end

    elseif tab == "bossdrops" then
        for _, group in ipairs(dv.bossdrops or {}) do
            if group.name then
                dv.collapsedHeaders["boss_" .. group.name] = true
            end
        end
    end

    -- If it was already open, leave everything collapsed.
    -- If it was closed, open this one.
    if not wasOpen then
        dv.collapsedHeaders[openKey] = false
    end

    if BuildVendorUI then
        BuildVendorUI()
    elseif dv.BuildVendorUI then
        dv.BuildVendorUI()
    end
end

 function dv.HasAnySelection(tbl)
    if not tbl then return false end
    for _, v in pairs(tbl) do
        if v then return true end
    end
    return false
end

function dv.GetHeaderWidth()
    local frame = dv.frame

    if not frame then
        return 560
    end

    if dv.currentTab == "professions" then
        return frame:GetWidth() - 40
    end

    local sidebarWidth = 0

    if frame.sidebar then
        sidebarWidth = frame.sidebar:GetWidth()
    elseif dv.sidebar then
        sidebarWidth = dv.sidebar:GetWidth()
    end

    return frame:GetWidth() - sidebarWidth - 40
end

 function dv.BuildProfessionLookup()
    dv.itemToProfession = {}
    for _, profession in ipairs(dv.professions or {}) do
        for _, recipe in ipairs(profession.items or {}) do
            dv.itemToProfession[recipe.id] = profession.name
        end
    end
end

function dv:GetItemData(itemID)
    return self.ActiveItems[itemID]
end

function dv.IsQuestEffectivelyCompleted(quest)
    if not quest then return false end

    -- 🔥 QUEST COMPLETION (ACCOUNT-WIDE)
    if type(quest.id) == "number" then
        if C_QuestLog.IsQuestFlaggedCompletedOnAccount then
            if C_QuestLog.IsQuestFlaggedCompletedOnAccount(quest.id) then
                return true
            end
        else
            if C_QuestLog.IsQuestFlaggedCompleted(quest.id) then
                return true
            end
        end
    end

    -- 🔥 ITEM COLLECTION (ALREADY ACCOUNT-WIDE)
    if quest.itemID then
        if type(quest.itemID) == "table" then
            for _, itemID in ipairs(quest.itemID) do
                if dv.IsItemCollected(itemID) then
                    return true
                end
            end
        elseif type(quest.itemID) == "number" then
            if dv.IsItemCollected(quest.itemID) then
                return true
            end
        end
    end

    return false
end

function dv.IsItemCollected(itemID)
    local itemData = dv.ActiveItems[itemID]
    local noxp = itemData and itemData.noxp

    -- Correct cache usage
    if noxp then
        if vendorSettings.completedDropNoXP[itemID] then return true end
    else
        if vendorSettings.completedDrop[itemID] then return true end
    end

    if dv.collectionCache[itemID] ~= nil then
        return dv.collectionCache[itemID]
    end

    local decorID = itemData and itemData.decorID
    if not decorID then
        dv.collectionCache[itemID] = false
        return false
    end

    local info = C_HousingCatalog.GetCatalogEntryInfoByRecordID(1, decorID, true)
    local isCollected = false

    if info then
        if noxp then
            -- ONLY these rules for NOXP
            if info.quantity > 0 or info.remainingRedeemable > 0 or info.numPlaced > 0 then
                isCollected = true
            end
        else
            -- ONLY this rule for normal items
            if info.firstAcquisitionBonus == 0 then
                isCollected = true
            end
        end
    end

    if isCollected then
        if noxp then
            vendorSettings.completedDropNoXP[itemID] = true
        else
            vendorSettings.completedDrop[itemID] = true
        end

        dv.collectionCache[itemID] = true
        return true
    end

    dv.collectionCache[itemID] = false
    return false
end

function dv.CountProfessionItems(profession)
    local total = 0
    local completed = 0

    for _, item in ipairs(profession.items or {}) do
        total = total + 1

        local learned = false
        if item.spell then
            learned = IsSpellKnown(item.spell) or IsPlayerSpell(item.spell)
        end

        local collected = false
        if item.id and dv.IsItemCollected then
            collected = dv.IsItemCollected(item.id)
        end

        if learned or collected then
            completed = completed + 1
        end
    end

    return completed, total
end

 function dv.IsAchievementComplete(achievementID)
    if not achievementID then return false end

    if vendorSettings.completedAchievs[achievementID] then
        return true
    end

    local _, _, _, completed = GetAchievementInfo(achievementID)
    if completed then
        vendorSettings.completedAchievs[achievementID] = true
    end

    return completed or false
end

-- ============================================================
-- Tab scroll frames helper
-- ============================================================
 function dv.CreateTabScrollFrame(tabFrame)
    local scroll = CreateFrame("ScrollFrame", nil, tabFrame, "UIPanelScrollFrameTemplate")
    scroll:SetPoint("TOPLEFT", 0, -26)
    scroll:SetPoint("BOTTOMRIGHT", -20, 0)

    local child = CreateFrame("Frame", nil, scroll)
    child:SetWidth(1)
    child:SetHeight(1)
    scroll:SetScrollChild(child)

    scroll:SetScript("OnSizeChanged", function(self, width)
        child:SetWidth(width)
    end)

    return scroll, child
end
