local GC = select(2, ...)

local LootProcessing = {}
GC.LootProcessing = LootProcessing

local private = {}

-- Libs
local LibToast = LibStub("LibToast-1.0")
local LSM = LibStub:GetLibrary("LibSharedMedia-3.0")
local LibParse = LibStub:GetLibrary("LibParse")

-- Wow APIs
local GetItemInfo, IsInGroup, UnitGUID, GetBestMapForUnit,
      PlaySoundFile, SendAddonMessage = GetItemInfo, IsInGroup,
                                        UnitGUID,
                                        C_Map.GetBestMapForUnit,
                                        PlaySoundFile,
                                        C_ChatInfo.SendAddonMessage

-- Lua APIs
local tonumber, tostring, floor, pairs, gsub = tonumber, tostring, floor,
                                               pairs, gsub

local GallywixCoins_GroupLoot = GallywixCoins_GroupLoot

--[[------------------------------------------------------------------------
    Main item processing entry point
--------------------------------------------------------------------------]]
function LootProcessing.HandleItemLooted(itemLink, itemID, quantity, source)
    GC.Debug.Log("handleItemLooted itemID=%s", itemID)
    GC.Debug.Log("*****Source: " .. tostring(source))
    GC.Debug.Log(
        "  " .. tostring(itemID) .. ": " .. tostring(itemLink) .. " x" ..
            tostring(quantity))
    GC.Debug.Log("  " .. tostring(itemID) .. ": " ..
                     tostring(gsub(tostring(itemLink), "\124", "\124\124")))

    local isRunning = GC.Session.IsRunning()
    -- If not running and alwaysOnNotifications is disabled, bail out
    if not isRunning and not GC.GetFromDb("notification", "alwaysOnNotifications") then return end

    -- Settings
    local qualityFilter =
        tonumber(GC.GetFromDb("notification", "qualityFilter")) or 1
    local priceSource = GC.PriceSources.ResolveForItem(itemID, GC.GetFromDb(
                                                           "pricesource",
                                                           "source"))
    GC.Debug.Log("Resolved price source: " .. tostring(priceSource))

    local ignoreSoulboundItems = GC.GetFromDb("general", "ignoreSoulboundItems")
    local addItem2List = true
    local disenchanted = false
    local showLootedItemValueGroup = GC.GetFromDb("display",
                                                  "showLootedItemValueGroup")
    local addGroupDropsToLootedItemList =
        GC.GetFromDb("display", "addGroupDropsToLootedItemList")
    local showGroupLootAlerts = GC.GetFromDb("display", "showGroupLootAlerts")
    GC.Debug.Log("showGroupLootAlerts: " .. tostring(showGroupLootAlerts))

    local showGroupLoot = source and
                              (showLootedItemValueGroup or
                                  addGroupDropsToLootedItemList)
    GC.Debug.Log("showGroupLoot = %s", tostring(showGroupLoot))

    -- Check item quality
    local quality = select(3, GetItemInfo(itemID)) or 0
    local effectiveFilter = tonumber(qualityFilter) or 1
    if quality < effectiveFilter then
        GC.Debug.Log("  " .. tostring(itemID) .. ": item quality (" ..
                         tostring(quality) .. ") < filter (" ..
                         tostring(effectiveFilter) .. ") -> ignore item")
        return
    end
    GC.Debug.Log("  " .. tostring(itemID) .. ": item quality (" ..
                     tostring(quality) .. ") >= filter (" ..
                     tostring(effectiveFilter) .. ")")

    -- Blacklist check
    if GC.IsItemBlacklisted(itemID) then
        GC.Debug.Log("  " .. tostring(itemID) .. ": blacklisted -> ignore")
        return
    end

    -- Overwrite link for base items only
    if GC.GetFromDb("general", "ignoreRandomEnchants") then
        itemLink = select(2, GetItemInfo(itemID))
    end

    -- Special handling for poor quality items
    if quality == 0 then
        if GC.GetFromDb("general", "ignoreGrayItems") then
            GC.Debug.Log("  " .. tostring(itemID) .. ": poor quality and ignoreGrayItems is on -> ignore")
            return
        end
        GC.Debug.Log("  " .. tostring(itemID) ..
                         ": poor quality -> price source 'VendorSell'")
    end

    -- Get single item value
    local singleItemValue = GC.PriceSources.GetItemValue(itemID, priceSource) or
                                0
    GC.Debug.Log("SIV price source: " .. tostring(singleItemValue))
    GC.Debug.Log("PriceSource: " .. tostring(priceSource))
    GC.Debug.Log("  " .. tostring(itemID) .. ": single item value: " ..
                     tostring(singleItemValue))

    -- Special handling for soulbound items and disenchant value
    if (singleItemValue == 0 or singleItemValue == nil) and quality > 0 then
        local blizVendorPrice = select(11, GetItemInfo(itemID)) or 0
        local ignoreSoulbound = GC.GetFromDb("general", "ignoreSoulboundItems")

        if ignoreSoulbound then
            addItem2List = false
            singleItemValue = blizVendorPrice
            GC.Debug.Log("ignoreSoulboundItems is on. No output to loot window.")
        else
            addItem2List = true
            singleItemValue = blizVendorPrice
            GC.Debug.Log("ignoreSoulboundItems is off. Showing VendorSell: " ..
                             tostring(blizVendorPrice))
        end

        if GC.GetFromDb("pricesource", "useDisenchantValue", "TSM_REQUIRED") then
            singleItemValue = GC.PriceSources.GetItemValue(itemID, "Destroy") or
                                  0
            disenchanted = true
            GC.Debug.Log("  " .. tostring(itemID) ..
                             ": single item value (de): " ..
                             tostring(singleItemValue))
        end
    end

    local itemValue = singleItemValue * quantity

    if isRunning then
        private.IncLootedItemCounter(quantity, source)
        private.AddItemValue2LootedItemValue(itemValue, source)
        if GC.Util.IsModern then private.IncWoWTokenPercentage(source) end

        -- Track loot for session history
        GC.SessionHistory.AddLootItem(itemID, itemLink, quantity, itemValue)

        if addItem2List == true then
            itemValue = singleItemValue
            GC.Debug.Log("itemValue: " .. tostring(itemValue))
            GC.Debug.Log("quality: " .. tostring(quality))
            GC.UI.AddItem2LootCollectedList(itemID, itemLink, quantity, itemValue,
                                            source, disenchanted)
            LogLoot(itemID, itemLink, itemValue)
        else
            GC.Debug.Log("soulbound item ignored: " .. tostring(itemID))
        end
    end

    -- Gold alert thresholds (Always run if user opted in for always-on)
    private.ProcessGoldAlerts(itemID, itemLink, singleItemValue, quantity,
                              quality, source, showGroupLoot)

    if isRunning then
        GC.UI.RefreshUIs()
    end

    -- Module callbacks
    if not source then
        local modules = GC.GetModules()
        if modules then
            for _, data in pairs(modules) do
                if data and data.callback and data.callback.itemDrop then
                    data.callback.itemDrop(itemID, singleItemValue)
                end
            end
        end
    end

    -- Handle party loot propagation
    if IsInGroup() and not source then
        if GC.GetFromDb("display", "showGroupLootAlerts") ~= false then
            private.SendAddonMsg(itemLink, itemID, quantity)
        end
    end
end

--[[------------------------------------------------------------------------
    Gold Alert Threshold processing
--------------------------------------------------------------------------]]
function private.ProcessGoldAlerts(itemID, itemLink, singleItemValue, quantity,
                                   quality, source, showGroupLoot)
    local goldValue = floor((singleItemValue * quantity) / 10000)
    local threshold = tonumber(GC.GetFromDb("notification", "goldAlertThreshold")) or 1000

    if goldValue < threshold then return end

    -- Party loot suffix
    local partyLootSuffix = ""
    if source then partyLootSuffix = " (|cFF2DA6ED" .. source .. "|r)" end

    -- Increment noteworthy counter
    private.IncNoteworthyItemCounter(quantity, source)

    -- Print to configured output channels
    if not source or showGroupLoot then
        local formattedValue = GC.Util.MoneyToString(singleItemValue) or 0
        local qtyValue = singleItemValue * tonumber(quantity)
        local formatValue = GC.Util.MoneyToString(qtyValue) or 0

        local icon = GetItemIcon(itemID) or 134400
        local iconText = "|T" .. icon .. ":18:18:0:0|t "

        GC:Pour(iconText .. quantity .. "x " .. itemLink .. " (" .. formatValue .. ")" .. partyLootSuffix)

        if tonumber(quantity) > 1 then
            GC.UI.UpdateLastNoteworthyItemUI(itemLink, quantity,
                                             singleItemValue, formatValue)
        else
            GC.UI.UpdateLastNoteworthyItemUI(itemLink, quantity,
                                             singleItemValue, formattedValue)
        end

        -- Toast notification
        if GC.GetFromDb("notification", "enableToasts") then
            -- GetItemInfo is asynchronous, so we use a closure or immediate data if available
            local name, _, _, _, _, _, _, _, _, texturePath = GetItemInfo(itemID)
            if name then
                LibToast:Spawn("GallywixCoins", name, texturePath, quality,
                               quantity, formatValue, source)
            else
                -- Fallback if not in cache: use itemLink name
                local itemName = itemLink:match("%[(.-)%]") or "Valuable Item"
                LibToast:Spawn("GallywixCoins", itemName, 134400, quality,
                               quantity, formatValue, source)
            end
        end
    end

    -- Update mapID if changed
    if GC.Session.GetCurrentSession("mapID") ~= GetBestMapForUnit("player") then
        GC.Debug.Log("  current vs. session mapID: %s vs. %s",
                     GetBestMapForUnit("player"),
                     GC.Session.GetCurrentSession("mapID"))
        GC.Session.SetCurrentSession("mapID", GetBestMapForUnit("player"))
    end

    -- Play alert sound
    private.PlayGATSound(source)
end

--[[------------------------------------------------------------------------
    Play the appropriate GAT alert sound
--------------------------------------------------------------------------]]
function private.PlayGATSound(source)
    if not GC.GetFromDb("notification", "playSoundEnabled") then return end

    -- Only play for noteworthy items (already filtered in caller)
    -- If source is provided, it's group/profession loot
    if source and not GC.GetFromDb("display", "showGroupLootAlerts") then
        return
    end

    local soundName = GC.db.profile.notification.soundName or "Auction Window Open"
    local soundChannel = GC.db.profile.notification.soundChannel or "Master"
    if soundName == "-- No Sound" then return end

    local soundFile = LSM:Fetch("sound", soundName)
    if soundFile then
        -- PlaySoundFile works with FileID (integers) or Paths (strings)
        PlaySoundFile(soundFile, soundChannel)
    else
        -- Absolute fallback: Auction Window Open
        PlaySound(8459, soundChannel)
    end
end

--[[------------------------------------------------------------------------
    Handle looted currency (gold/silver/copper drops)
--------------------------------------------------------------------------]]
function LootProcessing.HandleCurrencyLooted(lootedCopper)
    local totalLootedCurrency = (GC.Session.GetCurrentSession(
                                    "totalLootedCurrency") or 0) + lootedCopper
    GC.Session.SetCurrentSession("totalLootedCurrency", totalLootedCurrency)

    GC.Debug.Log("  handle currency: add " .. tostring(lootedCopper) ..
                     " copper -> new total: " .. tostring(totalLootedCurrency))

    GC.UI.RefreshUIs()
end

--[[------------------------------------------------------------------------
    Send addon message for group loot propagation
--------------------------------------------------------------------------]]
function private.SendAddonMsg(...)
    if GallywixCoins_GroupLoot then return end

    local json = ""
    for n = 1, select('#', ...) do
        if json ~= "" then json = json .. "\001" end
        json = json .. LibParse:JSONEncode(select(n, ...))
    end

    if json ~= "" then json = json .. "\001" end
    json = json .. LibParse:JSONEncode(UnitGUID("player"))

    if GC.GetFromDb("display", "showGroupLootAlerts") == true then
        SendAddonMessage(GC.CONST.PARTYLOOT_MSGPREFIX, json, "RAID")
    end
end

--[[------------------------------------------------------------------------
    Counter and value helper functions
--------------------------------------------------------------------------]]
function private.IncLootedItemCounter(quantity, source)
    if source then return end
    local lootedItemCounter =
        (GC.Session.GetCurrentSession("lootedItemCounter") or 0) + quantity
    GC.Session.SetCurrentSession("lootedItemCounter", lootedItemCounter)
    GC.Debug.Log("    looted items counter: add " .. tostring(quantity) ..
                     " -> new total: " .. tostring(lootedItemCounter))
end

function private.AddItemValue2LootedItemValue(itemValue, source)
    local totalItemValue = GC.Session.GetCurrentSession("liv") or 0

    if not source then
        totalItemValue = totalItemValue + itemValue
        GC.Debug.Log("    looted items value: add " .. tostring(itemValue) ..
                         " -> new total: " .. tostring(totalItemValue))
    end

    GC.Session.SetCurrentSession("liv", totalItemValue or 0)

    if GC.UI.ShowLiteWindow then
        GC.UI.UpdateLiteWindowUI()
    end

    -- Group value
    local totalItemValueGroup = GC.Session.GetCurrentSession("livGroup") or 0
    totalItemValueGroup = totalItemValueGroup + itemValue
    GC.Debug.Log("    group: looted items value: add " .. tostring(itemValue) ..
                     " -> new total: " .. tostring(totalItemValueGroup))
    GC.Session.SetCurrentSession("livGroup", totalItemValueGroup or 0)
end

function private.IncNoteworthyItemCounter(quantity, source)
    if source then return end
    local noteworthyItemCounter = (GC.Session.GetCurrentSession(
                                      "noteworthyItemCounter") or 0) + quantity
    GC.Session.SetCurrentSession("noteworthyItemCounter", noteworthyItemCounter)
    GC.Debug.Log("    noteworthy items counter: add " .. tostring(quantity) ..
                     " -> new total: " .. tostring(noteworthyItemCounter))
end

function private.IncWoWTokenPercentage(source)
    if source then return end

    local wowToken = GC.UI.GetTokenPrice()
    local totalItemValue = GC.Session.GetCurrentSession("liv") or 0

    if wowToken and wowToken > 0 then
        local percentage = totalItemValue / wowToken * 100
        GC.Session.SetCurrentSession("wowTokenPercentage", percentage)
        GC.Debug.Log(" WoW token price: " .. tostring(wowToken) ..
                         ", totalItemValue: " .. tostring(totalItemValue) ..
                         ", percentage: " .. percentage)
    else
        GC.Debug.Log(
            "WoW token price unavailable, skipping token percentage update.")
    end
end

--[[------------------------------------------------------------------------
    Blacklist check
--------------------------------------------------------------------------]]
function GC.IsItemBlacklisted(itemID)
    if not GC.GetFromDb("blacklist", "tsmGroupEnabled", "TSM_REQUIRED") then
        return GC.CONST.ITEM_FILTER_BLACKLIST[tostring(itemID)]
    end
    return GC.TSM.IsItemInGroup(itemID, GC.GetFromDb("blacklist", "tsmGroup"))
end

