--[[
    Commander 3.0 - API Polyfills

    Creates modern C_* namespace APIs for Classic clients.
    This allows command code to use Retail-style APIs everywhere.

    Philosophy: Write modern code, polyfill for Classic.
]]

local ADDON_NAME, C3 = ...

-- Skip if running on Retail with native support
if C3.Version:IsRetail() then
    C3:Debug("Polyfills: Skipped (Retail)")
    return
end

C3:Debug("Polyfills: Loading for", C3.Version:GetFlavor())

-------------------------------------------------------------------------------
-- C_Container Polyfill
-- Modern container API using old GetContainerItemInfo
-------------------------------------------------------------------------------

if not C_Container then
    C_Container = {}

    C_Container.GetContainerNumSlots = function(containerIndex)
        return _G.GetContainerNumSlots(containerIndex)
    end

    C_Container.GetContainerNumFreeSlots = function(containerIndex)
        return _G.GetContainerNumFreeSlots(containerIndex)
    end

    C_Container.GetContainerItemInfo = function(containerIndex, slotIndex)
        local icon, itemCount, locked, quality, readable, lootable, itemLink, isFiltered, noValue, itemID, isBound

        -- Different return signatures across versions
        if C3.Version:IsClassicEra() then
            -- Classic Era: texture, count, locked, quality, readable, lootable, link
            icon, itemCount, locked, quality, readable, lootable, itemLink = _G.GetContainerItemInfo(containerIndex, slotIndex)
        else
            -- TBC/Wrath/Cata: texture, count, locked, quality, readable, lootable, link, isFiltered, noValue, itemID, isBound
            icon, itemCount, locked, quality, readable, lootable, itemLink, isFiltered, noValue, itemID, isBound = _G.GetContainerItemInfo(containerIndex, slotIndex)
        end

        if not icon then
            return nil
        end

        -- Parse itemID from link if not provided
        if not itemID and itemLink then
            itemID = tonumber(itemLink:match("item:(%d+)"))
        end

        -- Get item name from link
        local itemName
        if itemLink then
            itemName = itemLink:match("%[(.+)%]")
        end

        return {
            iconFileID = icon,
            stackCount = itemCount or 1,
            isLocked = locked or false,
            quality = quality or 0,
            isReadable = readable or false,
            hasLoot = lootable or false,
            hyperlink = itemLink,
            isFiltered = isFiltered or false,
            hasNoValue = noValue or false,
            itemID = itemID,
            isBound = isBound or false,
            itemName = itemName,
        }
    end

    C_Container.GetContainerItemID = function(containerIndex, slotIndex)
        local info = C_Container.GetContainerItemInfo(containerIndex, slotIndex)
        return info and info.itemID
    end

    C_Container.GetContainerItemLink = function(containerIndex, slotIndex)
        return _G.GetContainerItemLink(containerIndex, slotIndex)
    end

    C_Container.UseContainerItem = function(containerIndex, slotIndex, target, reagentBankOpen)
        _G.UseContainerItem(containerIndex, slotIndex, target)
    end

    C_Container.PickupContainerItem = function(containerIndex, slotIndex)
        _G.PickupContainerItem(containerIndex, slotIndex)
    end

    C_Container.GetContainerItemCooldown = function(containerIndex, slotIndex)
        local start, duration, enable = _G.GetContainerItemCooldown(containerIndex, slotIndex)
        return start or 0, duration or 0, enable or 0
    end

    -- Constants
    C_Container.ContainerIDToInventoryID = function(containerIndex)
        return containerIndex + 19 -- CONTAINER_BAG_OFFSET
    end

    -- Backpack is always bag 0
    Enum = Enum or {}
    Enum.BagIndex = Enum.BagIndex or {
        Backpack = 0,
        Bag_1 = 1,
        Bag_2 = 2,
        Bag_3 = 3,
        Bag_4 = 4,
    }

    C3:Debug("Polyfill: C_Container created")
end

-------------------------------------------------------------------------------
-- C_Spell Polyfill
-- Modern spell API
-------------------------------------------------------------------------------

if not C_Spell then
    C_Spell = {}

    C_Spell.GetSpellInfo = function(spellIdentifier)
        local name, rank, icon, castTime, minRange, maxRange, spellID, originalIcon

        if type(spellIdentifier) == "number" then
            name, rank, icon, castTime, minRange, maxRange, spellID, originalIcon = _G.GetSpellInfo(spellIdentifier)
        else
            name, rank, icon, castTime, minRange, maxRange, spellID, originalIcon = _G.GetSpellInfo(spellIdentifier)
        end

        if not name then
            return nil
        end

        return {
            name = name,
            iconID = icon,
            originalIconID = originalIcon or icon,
            castTime = castTime or 0,
            minRange = minRange or 0,
            maxRange = maxRange or 0,
            spellID = spellID,
            rank = rank,
        }
    end

    C_Spell.GetSpellName = function(spellIdentifier)
        local name = _G.GetSpellInfo(spellIdentifier)
        return name
    end

    C_Spell.GetSpellTexture = function(spellIdentifier)
        local _, _, icon = _G.GetSpellInfo(spellIdentifier)
        return icon
    end

    C_Spell.GetSpellCooldown = function(spellIdentifier)
        local start, duration, enabled, modRate = _G.GetSpellCooldown(spellIdentifier)
        return {
            startTime = start or 0,
            duration = duration or 0,
            isEnabled = enabled == 1,
            modRate = modRate or 1,
        }
    end

    C_Spell.IsSpellUsable = function(spellIdentifier)
        local usable, noMana = _G.IsUsableSpell(spellIdentifier)
        return usable, noMana
    end

    C_Spell.GetSpellLink = function(spellIdentifier)
        return _G.GetSpellLink(spellIdentifier)
    end

    C_Spell.IsSpellKnown = function(spellID, isPet)
        if isPet then
            return _G.IsSpellKnown(spellID, true)
        end
        return _G.IsSpellKnown(spellID)
    end

    C_Spell.GetSpellSubtext = function(spellIdentifier)
        local _, rank = _G.GetSpellInfo(spellIdentifier)
        return rank
    end

    C3:Debug("Polyfill: C_Spell created")
end

-------------------------------------------------------------------------------
-- C_Spell Method Polyfills (for Retail where C_Spell exists but methods differ)
-------------------------------------------------------------------------------

-- On Retail, C_Spell exists but some methods like IsSpellKnown may not
if C_Spell then
    if not C_Spell.IsSpellKnown then
        C_Spell.IsSpellKnown = function(spellID, isPet)
            if isPet then
                return _G.IsSpellKnown(spellID, true)
            end
            return _G.IsSpellKnown(spellID)
        end
        C3:Debug("Polyfill: C_Spell.IsSpellKnown added")
    end

    if not C_Spell.IsSpellUsable then
        C_Spell.IsSpellUsable = function(spellIdentifier)
            local usable, noMana = _G.IsUsableSpell(spellIdentifier)
            return usable, noMana
        end
        C3:Debug("Polyfill: C_Spell.IsSpellUsable added")
    end
end

-------------------------------------------------------------------------------
-- C_MountJournal Polyfill (Stub)
-- Classic doesn't have a mount journal, so this returns empty/nil
-- The DataSource layer handles finding mounts from spellbook/bags
-------------------------------------------------------------------------------

if not C_MountJournal then
    C_MountJournal = {}

    C_MountJournal.GetMountIDs = function()
        return {} -- Empty - mounts come from DataSource in Classic
    end

    C_MountJournal.GetMountInfoByID = function(mountID)
        return nil -- No mount journal in Classic
    end

    C_MountJournal.GetMountInfoExtraByID = function(mountID)
        return nil
    end

    C_MountJournal.GetNumMounts = function()
        return 0
    end

    C_MountJournal.SummonByID = function(mountID)
        -- Stub - actual mount summoning handled by DataSource + Action
    end

    C_MountJournal.Dismiss = function()
        Dismount()
    end

    C3:Debug("Polyfill: C_MountJournal stub created")
end

-------------------------------------------------------------------------------
-- C_Item Polyfill
-------------------------------------------------------------------------------

if not C_Item then
    C_Item = {}

    C_Item.GetItemInfo = function(itemIdentifier)
        local name, link, quality, ilvl, reqLevel, class, subclass, maxStack,
              equipSlot, texture, vendorPrice, classID, subclassID, bindType,
              expansionID, setID, isCraftingReagent = _G.GetItemInfo(itemIdentifier)

        if not name then
            return nil
        end

        return {
            itemName = name,
            itemLink = link,
            itemQuality = quality,
            itemLevel = ilvl or 0,
            itemMinLevel = reqLevel or 0,
            itemType = class,
            itemSubType = subclass,
            itemStackCount = maxStack or 1,
            itemEquipLoc = equipSlot,
            itemTexture = texture,
            sellPrice = vendorPrice or 0,
            classID = classID,
            subclassID = subclassID,
            bindType = bindType,
            expansionID = expansionID,
            itemSetID = setID,
            isCraftingReagent = isCraftingReagent or false,
        }
    end

    C_Item.GetItemInfoInstant = function(itemIdentifier)
        local itemID, itemType, itemSubType, itemEquipLoc, icon, classID, subclassID = _G.GetItemInfoInstant(itemIdentifier)
        return itemID, itemType, itemSubType, itemEquipLoc, icon, classID, subclassID
    end

    C_Item.GetItemName = function(itemLocation)
        -- itemLocation is a table in retail, we just support basic use
        if type(itemLocation) == "number" then
            local name = _G.GetItemInfo(itemLocation)
            return name
        end
        return nil
    end

    C_Item.GetItemQuality = function(itemLocation)
        if type(itemLocation) == "number" then
            local _, _, quality = _G.GetItemInfo(itemLocation)
            return quality
        end
        return nil
    end

    C_Item.IsItemDataCached = function(itemIdentifier)
        local name = _G.GetItemInfo(itemIdentifier)
        return name ~= nil
    end

    C_Item.RequestLoadItemData = function(itemIdentifier)
        -- Trigger item cache load
        _G.GetItemInfo(itemIdentifier)
    end

    C_Item.IsUsableItem = function(itemIdentifier)
        if _G.IsUsableItem then
            return _G.IsUsableItem(itemIdentifier)
        end
        return true, false -- Assume usable if API doesn't exist
    end

    C_Item.GetItemCooldown = function(itemIdentifier)
        if _G.GetItemCooldown then
            local start, duration, enable = _G.GetItemCooldown(itemIdentifier)
            return start or 0, duration or 0, enable or 1
        end
        return 0, 0, 1 -- No cooldown if API doesn't exist
    end

    C3:Debug("Polyfill: C_Item created")
end

-------------------------------------------------------------------------------
-- C_CurrencyInfo Polyfill
-------------------------------------------------------------------------------

if not C_CurrencyInfo then
    C_CurrencyInfo = {}

    C_CurrencyInfo.GetCurrencyInfo = function(currencyType)
        if not _G.GetCurrencyInfo then
            return nil
        end

        local name, currentAmount, texture, earnedThisWeek, weeklyMax, totalMax, isDiscovered, rarity = _G.GetCurrencyInfo(currencyType)

        if not name then
            return nil
        end

        return {
            name = name,
            quantity = currentAmount or 0,
            iconFileID = texture,
            quantityEarnedThisWeek = earnedThisWeek or 0,
            maxWeeklyQuantity = weeklyMax or 0,
            maxQuantity = totalMax or 0,
            discovered = isDiscovered or false,
            quality = rarity or 1,
            currencyID = currencyType,
        }
    end

    C_CurrencyInfo.GetCurrencyListSize = function()
        if _G.GetCurrencyListSize then
            return _G.GetCurrencyListSize()
        end
        return 0
    end

    C3:Debug("Polyfill: C_CurrencyInfo created")
end

-------------------------------------------------------------------------------
-- C_Timer Polyfill (if somehow missing)
-------------------------------------------------------------------------------

if not C_Timer then
    C_Timer = {}

    local timerFrame = CreateFrame("Frame")
    local timers = {}

    timerFrame:SetScript("OnUpdate", function(self, elapsed)
        local now = GetTime()
        for id, timer in pairs(timers) do
            if now >= timer.expires then
                if timer.callback then
                    timer.callback()
                end
                if timer.repeating then
                    timer.expires = now + timer.duration
                else
                    timers[id] = nil
                end
            end
        end
    end)

    local timerID = 0

    C_Timer.After = function(duration, callback)
        timerID = timerID + 1
        timers[timerID] = {
            expires = GetTime() + duration,
            callback = callback,
            repeating = false,
        }
    end

    C_Timer.NewTicker = function(duration, callback, iterations)
        timerID = timerID + 1
        local id = timerID
        local count = 0

        timers[id] = {
            expires = GetTime() + duration,
            duration = duration,
            callback = function()
                count = count + 1
                callback()
                if iterations and count >= iterations then
                    timers[id] = nil
                end
            end,
            repeating = true,
        }

        return {
            Cancel = function()
                timers[id] = nil
            end,
        }
    end

    C_Timer.NewTimer = function(duration, callback)
        timerID = timerID + 1
        local id = timerID

        timers[id] = {
            expires = GetTime() + duration,
            callback = callback,
            repeating = false,
        }

        return {
            Cancel = function()
                timers[id] = nil
            end,
        }
    end

    C3:Debug("Polyfill: C_Timer created")
end

-------------------------------------------------------------------------------
-- Utility: debugprofilestop polyfill
-------------------------------------------------------------------------------

if not debugprofilestop then
    -- Fallback using GetTime (less precise but functional)
    local startTime = GetTime() * 1000000

    debugprofilestop = function()
        return (GetTime() * 1000000) - startTime
    end

    C3:Debug("Polyfill: debugprofilestop created")
end

C3:Debug("Polyfills: Complete")
