local addonName, addon = ...

addon.UI = addon.UI or {}
local UI = addon.UI
local LT = addon.LootTracker
local LootDB = LT.DB
local Query = LT.Query
local Tracks = LT.UpgradeTracks

local SOURCE_KEYS = "KEYS"
local SOURCE_RAIDS = "RAIDS"
local SOURCE_CRAFTED = "CRAFTED"

local function UIlang() return addon.Localization and addon.Localization:GetLanguage() or "en" end
local MENU_CHECK = "|TInterface\\Buttons\\UI-CheckBox-Check:16:16:0:0|t "

local SLOT_NAMES = {
    [-1] = "ALL SLOTS",
    [0] = "HEAD",
    [1] = "NECK",
    [2] = "SHOULDER",
    [3] = "BACK",
    [4] = "CHEST",
    [5] = "WRIST",
    [6] = "HANDS",
    [7] = "WAIST",
    [8] = "LEGS",
    [9] = "FEET",
    [10] = "WEAPON",
    [11] = "OFF HAND",
    [12] = "FINGER",
    [13] = "TRINKET",
    [14] = "OTHER",
}

local SOURCE_NAMES = {
    [SOURCE_KEYS] = "MYTHIC+",
    [SOURCE_RAIDS] = "RAIDS",
    [SOURCE_CRAFTED] = "CRAFTED",
}

local DIFFICULTY_NAMES = {
    lfr = "LFR",
    normal = "NORMAL",
    heroic = "HEROIC",
    mythic = "MYTHIC",
}

local function CharacterDisplay(character)
    if not character then
        return "NO CHARACTER"
    end

    if character.realm and character.realm ~= "" then
        return (character.name or "?") .. " - " .. character.realm
    end

    return character.name or "?"
end

local function SafeItemLink(itemID, fallbackLink)
    if fallbackLink and fallbackLink ~= "" then
        return fallbackLink
    end

    if C_Item and C_Item.GetItemLinkByID then
        return C_Item.GetItemLinkByID(itemID)
    end

    return nil
end

function UI:GetLootTrackerCharacter()
    if not addon.GameData then
        return nil
    end

    return addon.GameData:GetCharacter(self.LootCharacterKey)
end

function UI:EnsureLootTrackerSelection()
    local gameData = addon.GameData
    if not gameData then
        return
    end

    local characters = gameData:GetCharacters()
    local selected = self.LootCharacterKey
        and gameData:GetCharacter(self.LootCharacterKey)
        or nil

    if not selected then
        local currentKey = gameData:GetCurrentCharacterKey()
        selected = currentKey and gameData:GetCharacter(currentKey) or nil

        if selected then
            self.LootCharacterKey = selected.key or currentKey
        elseif characters[1] then
            selected = characters[1]
            self.LootCharacterKey = selected.key
        end
    end

    if selected then
        local specs = gameData:GetSpecs(self.LootCharacterKey)
        local validSpec = self.LootSpecID
            and gameData:GetSpec(self.LootCharacterKey, self.LootSpecID)
            or nil

        if not validSpec then
            local preferredSpecID = selected.currentSpecID
            if preferredSpecID and gameData:GetSpec(self.LootCharacterKey, preferredSpecID) then
                self.LootSpecID = preferredSpecID
            elseif specs[1] then
                self.LootSpecID = specs[1].id
            else
                self.LootSpecID = nil
            end
        end
    else
        self.LootCharacterKey = nil
        self.LootSpecID = nil
    end

    if self.LootSource ~= SOURCE_KEYS
        and self.LootSource ~= SOURCE_RAIDS
        and self.LootSource ~= SOURCE_CRAFTED
    then
        self.LootSource = SOURCE_KEYS
    end

    if self.LootSlotID == nil then
        self.LootSlotID = -1
    end

    if not self.LootCraftType then
        self.LootCraftType = "ALL"
    end

    if not DIFFICULTY_NAMES[self.LootRaidDifficulty] then
        self.LootRaidDifficulty = "normal"
    end

    if self.LootViewMode ~= "myloot" then
        self.LootViewMode = "browse"
    end

    if not self.LootTrackKey then
        self.LootTrackKey = "champion"
        self.LootTrackRank = 1
    end

    local trackData = self:GetSelectedLootTrackData()
    if not trackData and self.LootSource == SOURCE_KEYS then
        self.LootTrackKey = "champion"
        self.LootTrackRank = 1
    elseif not trackData and self.LootSource == SOURCE_RAIDS then
        self.LootTrackKey = self.LootRaidDifficulty
        self.LootTrackRank = 1
    end
end

function UI:GetSelectedLootTrackData()
    if self.LootSource == SOURCE_CRAFTED then
        return nil
    end

    local rank = tonumber(self.LootTrackRank) or 1

    if self.LootSource == SOURCE_RAIDS then
        local key = self.LootRaidDifficulty or "normal"
        local entry = Tracks:GetRaidEntry(key, rank)
        if not entry then
            rank = 1
            entry = Tracks:GetRaidEntry(key, rank)
        end

        if not entry then
            return nil
        end

        return {
            type = "RAID",
            key = key,
            rank = rank,
            entry = entry,
        }
    end

    local key = self.LootTrackKey or "champion"
    local entry = Tracks:GetDungeonEntry(key, rank)
    if not entry then
        key = "champion"
        rank = 1
        entry = Tracks:GetDungeonEntry(key, rank)
    end

    if not entry then
        return nil
    end

    return {
        type = "DUNGEON",
        key = key,
        rank = rank,
        entry = entry,
    }
end

function UI:GetLootTrackedState(itemID)
    if not self.LootCharacterKey or not self.LootSpecID then
        return nil
    end

    return LootDB:GetTrackedItem(self.LootCharacterKey, self.LootSpecID, itemID)
end

function UI:ShowLootItemTooltip(button, itemLink, tracked, trackEntry)
    if not button then
        return
    end

    GameTooltip:SetOwner(button, "ANCHOR_RIGHT")

    local itemID = button.ItemID
    local link = SafeItemLink(itemID, itemLink)

    if link then
        GameTooltip:SetHyperlink(link)
    elseif itemID and GameTooltip.SetItemByID then
        GameTooltip:SetItemByID(itemID)
    else
        GameTooltip:SetText(self:T("ITEM", "ITEM") .. " " .. tostring(itemID or "?"))
    end

    local localizedInfo = Query:GetItemInfo(itemID)
    self:AddItemNameEquivalentToTooltip(itemID, localizedInfo and localizedInfo.name or nil)

    if trackEntry then
        local label = trackEntry.label
            or (trackEntry.ilvl and tostring(trackEntry.ilvl))
            or ""

        if label ~= "" then
            GameTooltip:AddLine(" ")
            GameTooltip:AddLine(self:LT("TARGET") .. ": " .. label, 1, 0.72, 0.12)
        end
    end

    if tracked then
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine(self:LT("TRACKED") .. ": " .. tostring(tracked.priority or "WANTED"), 0.78, 0.34, 1)
        if tracked.voidCore then
            GameTooltip:AddLine(self:T("VOID_CORE", "VOID CORE"), 0.68, 0.42, 1)
        end
        if tracked.obtained then
            GameTooltip:AddLine(self:T("OBTAINED", "OBTAINED"), 0.3, 1, 0.3)
        end
    else
        GameTooltip:AddLine(" ")
        GameTooltip:AddLine(self:T("CLICK_ADD", "CLICK TO ADD TO MY LOOT"), 0.68, 0.62, 0.72)
    end

    GameTooltip:Show()
end

function UI:OpenLootPriorityMenu(anchor, itemID, sourceData, bossData, trackData)
    if not anchor or not itemID then
        return
    end

    local tracked = self:GetLootTrackedState(itemID)
    local entries = {}

    local function ApplyPriority(priority)
        local itemInfo = Query:GetItemInfo(itemID)
        local entry = trackData and trackData.entry or nil

        -- TrackItem is intentionally used for both new and already tracked items.
        -- This keeps source/slot metadata synchronized when the same item is selected
        -- from another browser (especially the crafted catalogue).
        LootDB:TrackItem(
            self.LootCharacterKey,
            self.LootSpecID,
            itemID,
            {
                priority = priority,
                sourceType = sourceData and sourceData.sourceType or (tracked and tracked.sourceType) or self.LootSource,
                sourceID = sourceData and (sourceData.sourceID or sourceData.id) or (tracked and tracked.sourceID) or nil,
                sourceName = sourceData and (sourceData.name or sourceData.sourceName) or (tracked and tracked.sourceName) or nil,
                bossID = bossData and (bossData.bossID or bossData.id) or (tracked and tracked.bossID) or nil,
                bossName = bossData and (bossData.name or bossData.bossName) or (tracked and tracked.bossName) or nil,
                slotID = sourceData and tonumber(sourceData.slotID)
                    or (tracked and tonumber(tracked.slotID))
                    or (itemInfo and itemInfo.slotID)
                    or Query:GetItemSlotID(itemID),
                itemLevel = entry and entry.ilvl or (tracked and tracked.itemLevel) or nil,
                trackType = trackData and trackData.type or (tracked and tracked.trackType) or nil,
                trackKey = trackData and trackData.key or (tracked and tracked.trackKey) or nil,
                trackRank = trackData and trackData.rank or (tracked and tracked.trackRank) or nil,
                trackLabel = entry and (entry.groupLabel or entry.label) or (tracked and tracked.trackLabel) or nil,
                bonusIDs = entry and entry.bonusId and { entry.bonusId } or (tracked and tracked.bonusIDs) or nil,
                professionID = sourceData and sourceData.professionID or (tracked and tracked.professionID) or nil,
                professionName = sourceData and sourceData.professionName or (tracked and tracked.professionName) or nil,
            }
        )

        self:RefreshLootTracker()
        if self.RefreshEquipmentLootTest then
            self:RefreshEquipmentLootTest()
        end
    end

    for _, priority in ipairs({
        LootDB.PRIORITY_WANTED,
        LootDB.PRIORITY_HIGH,
        LootDB.PRIORITY_BIS,
    }) do
        table.insert(entries, {
            text = (tracked and tracked.priority == priority and MENU_CHECK or "") .. priority,
            func = function()
                ApplyPriority(priority)
            end,
        })
    end

    table.insert(entries, { text = " ", disabled = true })
    table.insert(entries, {
        text = ((tracked and tracked.voidCore) and MENU_CHECK or "")
            .. self:T("VOID_CORE_ROLL", "VOID CORE ROLL"),
        func = function()
            local current = LootDB:GetTrackedItem(self.LootCharacterKey, self.LootSpecID, itemID)
            if not current then
                -- The secondary flag can be selected before a priority. In that case
                -- create the tracked item as WANTED; a later priority selection keeps
                -- the independent Void Core marker intact.
                ApplyPriority(LootDB.PRIORITY_WANTED)
                current = LootDB:GetTrackedItem(self.LootCharacterKey, self.LootSpecID, itemID)
            end
            if current then
                LootDB:SetVoidCore(self.LootCharacterKey, self.LootSpecID, itemID, not current.voidCore)
            end
        end,
    })

    if tracked then
        table.insert(entries, {
            text = "|cffff5a5a" .. self:T("REMOVE_FROM_MY_LOOT", "REMOVE FROM MY LOOT") .. "|r",
            func = function()
                LootDB:UntrackItem(self.LootCharacterKey, self.LootSpecID, itemID)
                self:RefreshLootTracker()
            end,
        })
    end

    self:OpenBlizzardContextMenu(anchor, entries)
end

function UI:GetLootSelectorText(kind)
    if kind == "character" then
        return CharacterDisplay(self:GetLootTrackerCharacter())
    elseif kind == "spec" then
        local spec = addon.GameData and addon.GameData:GetSpec(self.LootCharacterKey, self.LootSpecID)
        return spec and spec.name or "NO SPEC"
    elseif kind == "source" then
        return self:LT(SOURCE_NAMES[self.LootSource] or "MYTHIC+")
    elseif kind == "slot" then
        return self:LT(SLOT_NAMES[tonumber(self.LootSlotID) or -1] or "ALL SLOTS")
    elseif kind == "material" then
        local crafting = addon.LootTracker and addon.LootTracker.Crafting
        local names = crafting and crafting.ITEM_TYPE_NAMES or {}
        return self:LT(names[self.LootCraftType or "ALL"] or "ALL TYPES")
    elseif kind == "difficulty" then
        return self:LT(DIFFICULTY_NAMES[self.LootRaidDifficulty] or "NORMAL")
    elseif kind == "track" then
        local data = self:GetSelectedLootTrackData()
        if not data or not data.entry then
            return "N/A"
        end
        return (data.entry.groupLabel or data.key or "TRACK") .. "  " .. (data.entry.label or "")
    end

    return "SELECT"
end

function UI:GetLootSelectorEntries(kind)
    local entries = {}

    if kind == "character" then
        for _, character in ipairs(addon.GameData:GetCharacters()) do
            local characterKey = character.key
            table.insert(entries, {
                text = CharacterDisplay(character),
                func = function()
                    self.LootCharacterKey = characterKey
                    self.LootSpecID = nil
                    self:EnsureLootTrackerSelection()
                    self:RefreshLootTracker()
                end,
            })
        end

    elseif kind == "spec" then
        for _, spec in ipairs(addon.GameData:GetSpecs(self.LootCharacterKey)) do
            local specID = spec.id
            table.insert(entries, {
                text = spec.name or tostring(specID),
                func = function()
                    self.LootSpecID = specID
                    self:RefreshLootTracker()
                end,
            })
        end

    elseif kind == "source" then
        for _, source in ipairs({ SOURCE_KEYS, SOURCE_RAIDS, SOURCE_CRAFTED }) do
            table.insert(entries, {
                text = self:LT(SOURCE_NAMES[source]),
                func = function()
                    self.LootSource = source
                    self.LootTrackRank = 1
                    if source == SOURCE_RAIDS then
                        self.LootTrackKey = self.LootRaidDifficulty or "normal"
                    elseif source == SOURCE_KEYS then
                        self.LootTrackKey = "champion"
                    end
                    self:RefreshLootTracker()
                end,
            })
        end

    elseif kind == "slot" then
        for slotID = -1, 14 do
            local id = slotID
            table.insert(entries, {
                text = self:LT(SLOT_NAMES[id] or ("SLOT " .. tostring(id))),
                func = function()
                    self.LootSlotID = id
                    self:RefreshLootTracker()
                end,
            })
        end

    elseif kind == "material" then
        local crafting = addon.LootTracker and addon.LootTracker.Crafting
        local order = crafting and crafting.ITEM_TYPE_ORDER or { "ALL" }
        local names = crafting and crafting.ITEM_TYPE_NAMES or { ALL = "ALL TYPES" }

        for _, typeKey in ipairs(order) do
            local value = typeKey
            table.insert(entries, {
                text = self:LT(names[value] or value),
                func = function()
                    self.LootCraftType = value
                    self:RefreshLootTracker()
                end,
            })
        end

    elseif kind == "difficulty" then
        for _, difficulty in ipairs({ "lfr", "normal", "heroic", "mythic" }) do
            local value = difficulty
            table.insert(entries, {
                text = self:LT(DIFFICULTY_NAMES[value]),
                func = function()
                    self.LootRaidDifficulty = value
                    self.LootTrackKey = value
                    self.LootTrackRank = 1
                    self:RefreshLootTracker()
                end,
            })
        end

    elseif kind == "track" then
        if self.LootSource == SOURCE_RAIDS then
            local difficulty = self.LootRaidDifficulty or "normal"
            local track = Tracks.raid[difficulty] or {}
            for rank, entry in ipairs(track) do
                local selectedRank = rank
                table.insert(entries, {
                    text = (entry.groupLabel or DIFFICULTY_NAMES[difficulty]) .. "  " .. (entry.label or tostring(entry.ilvl or "")),
                    func = function()
                        self.LootTrackKey = difficulty
                        self.LootTrackRank = selectedRank
                        self:RefreshLootTracker()
                    end,
                })
            end
        else
            for _, key in ipairs(Tracks.dungeonOrder or {}) do
                local track = Tracks.dungeon[key] or {}
                for rank, entry in ipairs(track) do
                    local selectedKey = key
                    local selectedRank = rank
                    table.insert(entries, {
                        text = (entry.groupLabel or string.upper(key)) .. "  " .. (entry.label or tostring(entry.ilvl or "")),
                        func = function()
                            self.LootTrackKey = selectedKey
                            self.LootTrackRank = selectedRank
                            self:RefreshLootTracker()
                        end,
                    })
                end
            end
        end
    end

    return entries
end
