local addonName, addon = ...

addon.LootTracker = addon.LootTracker or {}
addon.LootTracker.Upgrade = addon.LootTracker.Upgrade or {}

local Upgrade = addon.LootTracker.Upgrade

local SPECIAL_BONUS_IDS = {
    [178708] = 6917,
    [178715] = 6923,
    [243308] = 13503,
}

local function GetItemLevelBonusID(diff)
    diff = tonumber(diff)

    if not diff or diff == 0 then
        return nil
    end

    -- Blizzard's item-level bonus range used by the current data:
    -- -100..200 maps linearly around 1472.
    if diff >= -100 and diff <= 200 then
        return 1472 + diff
    end

    -- Extended positive range.
    if diff >= 201 and diff <= 900 then
        return 2929 + diff
    end

    return nil
end

function Upgrade:IsUpgradeable(itemID)
    if not C_Item
        or not C_Item.GetItemInfoInstant
    then
        return false
    end

    local _, _, _, _, _, classID, subClassID =
        C_Item.GetItemInfoInstant(itemID)

    if not classID then
        return false
    end

    if classID == Enum.ItemClass.Armor
        and subClassID ==
            Enum.ItemArmorSubclass.Cosmetic
    then
        return false
    end

    return classID == Enum.ItemClass.Armor
        or classID == Enum.ItemClass.Weapon
end

function Upgrade:BuildItemLink(
    itemID,
    specID,
    upgradeEntry,
    storedBonusIDs
)
    itemID = tonumber(itemID)

    if not itemID then
        return nil
    end

    if not upgradeEntry
        or not self:IsUpgradeable(itemID)
    then
        return "item:" .. tostring(itemID)
    end

    local _, _, baseItemLevel =
        C_Item.GetDetailedItemLevelInfo(
            itemID
        )

    if not baseItemLevel then
        return "item:" .. tostring(itemID)
    end

    local bonusIDs = {}

    if storedBonusIDs then
        for _, bonusID in ipairs(
            storedBonusIDs
        ) do
            table.insert(
                bonusIDs,
                bonusID
            )
        end
    end

    local diff =
        tonumber(upgradeEntry.ilvl)
        - tonumber(baseItemLevel)

    local levelBonusID =
        GetItemLevelBonusID(diff)

    if levelBonusID then
        table.insert(
            bonusIDs,
            levelBonusID
        )
    end

    if upgradeEntry.bonusId then
        table.insert(
            bonusIDs,
            upgradeEntry.bonusId
        )
    end

    if SPECIAL_BONUS_IDS[itemID] then
        table.insert(
            bonusIDs,
            SPECIAL_BONUS_IDS[itemID]
        )
    end

    -- Match the seasonal item quality context used by KeystoneLoot.
    table.insert(
        bonusIDs,
        1674
    )

    local _, _, _, equipLoc =
        C_Item.GetItemInfoInstant(
            itemID
        )

    if equipLoc == "INVTYPE_FINGER"
        or equipLoc == "INVTYPE_NECK"
    then
        table.insert(
            bonusIDs,
            13534
        )
    end

    local playerLevel =
        UnitLevel("player")
        or 80

    specID =
        tonumber(specID)
        or 0

    local numBonusIDs =
        #bonusIDs

    local bonusString =
        table.concat(
            bonusIDs,
            ":"
        )

    local enchant = ""
    local gem1 = ""
    local gem2 = ""
    local gem3 = ""
    local gem4 = ""

    local extras =
        string.format(
            "%s:%s:%s:%s:%s",
            enchant,
            gem1,
            gem2,
            gem3,
            gem4
        )

    return string.format(
        "item:%d:%s:::%d:%d:::%d:%s",
        itemID,
        extras,
        playerLevel,
        specID,
        numBonusIDs,
        bonusString
    )
end


-- Return the canonical saved upgrade entry for a tracked item.
-- This prevents a saved track bonus from being mixed with a different
-- preview selected in another Loot Tracker view.
function Upgrade:GetTrackedUpgradeEntry(tracked)
    if not tracked then
        return nil, false
    end

    local tracks = addon.LootTracker and addon.LootTracker.UpgradeTracks
    local trackType = tostring(tracked.trackType or ""):upper()
    local key = tracked.trackKey
    local rank = tonumber(tracked.trackRank) or 1

    if tracks and key then
        if trackType == "DUNGEON" and tracks.GetDungeonEntry then
            local entry = tracks:GetDungeonEntry(key, rank)
            if entry then
                return entry, true
            end
        elseif trackType == "RAID" and tracks.GetRaidEntry then
            local entry = tracks:GetRaidEntry(key, rank)
            if entry then
                return entry, true
            end
        end
    end

    if tracked.itemLevel then
        return {
            ilvl = tonumber(tracked.itemLevel),
            groupLabel = tracked.trackLabel or trackType or "TRACK",
            label = tostring(tracked.itemLevel),
        }, false
    end

    return nil, false
end

-- Build a link for My Loot / ALL ITEM LEVELS using only the state saved
-- on that item. Exact raid/dungeon tracks are rebuilt from UpgradeTracks,
-- so stale bonusIDs cannot fight the saved track.
function Upgrade:BuildTrackedItemLink(itemID, specID, tracked)
    local entry, canonical = self:GetTrackedUpgradeEntry(tracked)

    if canonical then
        return self:BuildItemLink(itemID, specID, entry, nil), entry
    end

    return self:BuildItemLink(
        itemID,
        specID,
        entry,
        tracked and tracked.bonusIDs or nil
    ), entry
end

-- Build a link for a temporary per-view preview. The selected preview is
-- authoritative, so saved track bonusIDs are deliberately NOT included.
-- Otherwise, e.g. a saved Champion bonus can conflict with a Myth preview.
function Upgrade:BuildPreviewItemLink(itemID, specID, previewEntry)
    return self:BuildItemLink(itemID, specID, previewEntry, nil)
end
