
CowmeraRCDB = CowmeraRCDB or {}
CowmeraRCDB.loot = CowmeraRCDB.loot or {}

local STAT_LABELS = {
    ITEM_MOD_STRENGTH_SHORT = "Strength",
    ITEM_MOD_AGILITY_SHORT = "Agility",
    ITEM_MOD_INTELLECT_SHORT = "Intellect",
    ITEM_MOD_STAMINA_SHORT = "Stamina",
    ITEM_MOD_CRIT_RATING_SHORT = "Critical Strike",
    ITEM_MOD_HASTE_RATING_SHORT = "Haste",
    ITEM_MOD_MASTERY_RATING_SHORT = "Mastery",
    ITEM_MOD_VERSATILITY = "Versatility",
    ITEM_MOD_VERSATILITY_SHORT = "Versatility",
    ITEM_MOD_LEECH_RATING_SHORT = "Leech",
    ITEM_MOD_SPEED_RATING_SHORT = "Speed",
    ITEM_MOD_AVOIDANCE_RATING_SHORT = "Avoidance",
    ITEM_MOD_CR_SPEED_SHORT = "Speed",
    ITEM_MOD_CR_AVOIDANCE_SHORT = "Avoidance",
    ITEM_MOD_CR_LIFESTEAL_SHORT = "Leech",
    ITEM_MOD_CR_STURDINESS_SHORT = "Indestructible",
    ITEM_MOD_DAMAGE_PER_SECOND_SHORT = "WDps",
    RESISTANCE0_NAME = "Armor",
}

local function rcprint(msg)
    print("|cff22c55e[CowmeraRC]|r " .. tostring(msg))
end

local function countEntries()
    return #(CowmeraRCDB.loot or {})
end

local function GetStatSortRank(key, label)
    local t = tostring(label or key or ""):lower()
    if t == "armor" then return 1 end
    if t == "stamina" then return 2 end
    if t == "strength" or t == "agility" or t == "intellect" then return 3 end
    if t == "critical strike" or t == "crit" then return 4 end
    if t == "haste" then return 5 end
    if t == "mastery" or t == "master" then return 6 end
    if t == "versatility" then return 7 end
    if t == "wdps" then return 8 end
    if t == "speed" then return 9 end
    if t == "avoidance" then return 10 end
    if t == "leech" then return 11 end
    if t == "indestructible" or t == "indestructable" then return 12 end
    return 99
end

local function NormalizeStats(statsRaw)
    local stats = {}
    if type(statsRaw) ~= "table" then
        return stats
    end
    for statKey, statValue in pairs(statsRaw) do
        if not tostring(statKey):match("^EMPTY_SOCKET") then
            table.insert(stats, {
                key = statKey,
                label = STAT_LABELS[statKey] or statKey,
                value = statValue,
            })
        end
    end
    table.sort(stats, function(a, b)
        local ar = GetStatSortRank(a.key, a.label)
        local br = GetStatSortRank(b.key, b.label)
        if ar ~= br then return ar < br end
        return tostring(a.label or "") < tostring(b.label or "")
    end)
    return stats
end

local function ExtractNameFromLink(itemLink)
    if type(itemLink) ~= "string" then
        return ""
    end
    return itemLink:match("|h%[(.-)%]|h") or itemLink:match("%[(.-)%]") or ""
end

local function DedupLines(lines)
    local out, seen = {}, {}
    for _, line in ipairs(lines or {}) do
        local text = tostring(line or ""):gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):gsub("^%s+", ""):gsub("%s+$", "")
        if text ~= "" and not seen[text] then
            seen[text] = true
            table.insert(out, text)
        end
    end
    return out
end

local function ExtractTextsRecursive(value, out)
    if type(value) == "string" then
        local text = value:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):gsub("^%s+", ""):gsub("%s+$", "")
        if text ~= "" then
            table.insert(out, text)
        end
        return
    end

    if type(value) ~= "table" then
        return
    end

    if type(value.leftText) == "string" then table.insert(out, value.leftText) end
    if type(value.rightText) == "string" then table.insert(out, value.rightText) end
    if type(value.text) == "string" then table.insert(out, value.text) end
    if type(value.tooltipText) == "string" then table.insert(out, value.tooltipText) end
    if type(value.localizedText) == "string" then table.insert(out, value.localizedText) end

    for _, child in pairs(value) do
        if type(child) == "table" or type(child) == "string" then
            ExtractTextsRecursive(child, out)
        end
    end
end

local function CollectTooltipLines(itemLink, itemName)
    local lines = {}
    if not itemLink or not C_TooltipInfo or not C_TooltipInfo.GetHyperlink then
        return lines
    end

    local ok, tooltipData = pcall(C_TooltipInfo.GetHyperlink, itemLink)
    if not ok or type(tooltipData) ~= "table" then
        return lines
    end

    local rawTexts = {}
    ExtractTextsRecursive(tooltipData.lines or {}, rawTexts)

    local normalized = {}
    for _, text in ipairs(rawTexts) do
        if type(text) == "string" then
            text = text:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):gsub("^%s+", ""):gsub("%s+$", "")
            if text ~= "" and text ~= tostring(itemName or "") and text ~= ExtractNameFromLink(itemLink) and not text:match("^Item Level ") then
                table.insert(normalized, text)
            end
        end
    end

    return DedupLines(normalized)
end

local function ExtractTooltipText(itemLink, itemName)
    local tooltipLines = CollectTooltipLines(itemLink, itemName)
    local flavorText, equipText, useText, effectText = "", "", "", ""
    local misc = {}
    local activeBucket = nil

    local function append(bucket, line)
        if bucket == "equip" then
            equipText = equipText ~= "" and (equipText .. "\n" .. line) or line
        elseif bucket == "use" then
            useText = useText ~= "" and (useText .. "\n" .. line) or line
        elseif bucket == "effect" then
            effectText = effectText ~= "" and (effectText .. "\n" .. line) or line
        elseif bucket == "flavor" then
            flavorText = flavorText ~= "" and (flavorText .. "\n" .. line) or line
        else
            table.insert(misc, line)
        end
    end

    local function isBoilerplate(lower, line)
        if lower:match("^sell price") or lower:match("^durability") or lower:match("^classes:") or lower:match("^requires") then return true end
        if lower:match("^socket bonus:") or lower:match("^prismatic socket") or lower:match("^empty socket") then return true end
        if lower:match("^upgrade level:") or lower:match("^item level ") then return true end
        if lower:match("^%+%d+") and (lower:find("strength") or lower:find("agility") or lower:find("intellect") or lower:find("stamina") or lower:find("critical strike") or lower:find("haste") or lower:find("mastery") or lower:find("versatility") or lower:find("speed") or lower:find("avoidance") or lower:find("leech") or lower:find("indestructible") or lower:find("armor")) then
            return true
        end
        return false
    end

    local function startsEffect(lower)
        return lower:match("^chance on hit:")
            or lower:match("^proc:")
            or lower:match("^your ")
            or lower:match("^when ")
            or lower:match("^while ")
            or lower:match("^attacks ")
            or lower:match("^spells ")
            or lower:match("^abilities ")
            or lower:match("^dealing ")
            or lower:match("^healing ")
            or lower:match("^taking ")
            or lower:match("^on kill:")
            or lower:match("^on taking damage:")
    end

    for _, line in ipairs(tooltipLines) do
        local clean = tostring(line or ""):gsub("^%s+", ""):gsub("%s+$", "")
        if clean ~= "" then
            local lower = string.lower(clean)
            local startsNew = false

            if clean:match('^".*"$') then
                activeBucket = "flavor"
                append(activeBucket, clean)
                startsNew = true
            elseif lower:match("^equip:") then
                activeBucket = "equip"
                append(activeBucket, clean)
                startsNew = true
            elseif lower:match("^use:") then
                activeBucket = "use"
                append(activeBucket, clean)
                startsNew = true
            elseif startsEffect(lower) then
                activeBucket = "effect"
                append(activeBucket, clean)
                startsNew = true
            elseif isBoilerplate(lower, clean) then
                activeBucket = nil
                startsNew = true
            end

            if not startsNew then
                if activeBucket and not lower:match("^unique") and not lower:match("^bind") then
                    append(activeBucket, clean)
                else
                    activeBucket = nil
                    table.insert(misc, clean)
                end
            end
        end
    end

    return {
        tooltipLines = tooltipLines,
        flavorText = flavorText,
        equipText = equipText,
        useText = useText,
        effectText = effectText,
        tooltipText = table.concat(misc, "\n"),
    }
end

local function BuildStatsText(stats)
    local lines = {}
    for _, stat in ipairs(stats or {}) do
        local value = tonumber(stat.value) or 0
        table.insert(lines, string.format("+%s %s", tostring(value), tostring(stat.label or stat.key or "")))
    end
    return table.concat(lines, "\n")
end

local function BuildRealItemData(itemLink)
    local itemID = nil
    if GetItemInfoInstant then
        itemID = select(1, GetItemInfoInstant(itemLink))
    end
    local itemName, _, itemQuality, _, _, itemType, itemSubType, _, itemEquipLoc = GetItemInfo(itemLink)
    if (not itemName or itemName == "" or itemName == UNKNOWN or itemName == "Unknown") and C_Item and C_Item.GetItemNameByID and itemID then
        itemName = C_Item.GetItemNameByID(itemID)
    end
    if not itemName or itemName == "" or itemName == UNKNOWN or itemName == "Unknown" then
        itemName = ExtractNameFromLink(itemLink)
    end

    local itemLevel = 0
    if C_Item and C_Item.GetDetailedItemLevelInfo then
        itemLevel = C_Item.GetDetailedItemLevelInfo(itemLink) or 0
    end

    local statsRaw = {}
    if C_Item and C_Item.GetItemStats then
        statsRaw = C_Item.GetItemStats(itemLink) or {}
    end
    local stats = NormalizeStats(statsRaw)
    local statsText = BuildStatsText(stats)
    local tooltip = ExtractTooltipText(itemLink, itemName)

    local detailLines = {}
    if statsText ~= "" then table.insert(detailLines, statsText) end
    if tooltip.equipText ~= "" then table.insert(detailLines, tooltip.equipText) end
    if tooltip.useText ~= "" then table.insert(detailLines, tooltip.useText) end
    if tooltip.effectText ~= "" then table.insert(detailLines, tooltip.effectText) end
    if tooltip.tooltipText ~= "" then table.insert(detailLines, tooltip.tooltipText) end

    return {
        item = itemLink,
        itemID = itemID or 0,
        itemName = itemName or "",
        quality = itemQuality or 1,
        itemLevel = itemLevel or 0,
        itemType = itemType or "",
        subType = itemSubType or "",
        inventoryType = itemEquipLoc or "",
        statsText = statsText or "",
        flavorText = tooltip.flavorText or "",
        equipText = tooltip.equipText or "",
        useText = tooltip.useText or "",
        effectText = tooltip.effectText or "",
        tooltipText = tooltip.tooltipText or "",
        detailText = table.concat(detailLines, "\n"),
    }
end

local function normalizeHistoryEntry(name, history)
    local itemLink = history and history.lootWon or nil
    local itemID = nil
    if type(itemLink) == "string" then
        itemID = tonumber(string.match(itemLink, "item:(%d+)"))
    end

    local itemData = {}
    if itemLink and itemLink ~= "" then
        itemData = BuildRealItemData(itemLink)
        itemID = itemData.itemID or itemID
    end

    return {
        player = name or "Unknown",
        item = itemLink,
        itemID = itemID,
        itemName = itemData.itemName or "",
        quality = itemData.quality or 1,
        itemLevel = itemData.itemLevel or 0,
        itemType = itemData.itemType or "",
        subType = itemData.subType or "",
        inventoryType = itemData.inventoryType or "",
        statsText = itemData.statsText or "",
        flavorText = itemData.flavorText or "",
        equipText = itemData.equipText or "",
        useText = itemData.useText or "",
        effectText = itemData.effectText or "",
        tooltipText = itemData.tooltipText or "",
        detailText = itemData.detailText or "",
        date = history and history.date or "",
        time = history and history.time or "",
        instance = history and history.instance or "",
        boss = history and history.boss or "",
        response = history and history.response or "",
        responseID = history and history.responseID or "",
        votes = history and history.votes or 0,
        id = history and history.id or table.concat({
            tostring(history and history.date or ""),
            tostring(history and history.time or ""),
            tostring(name or "Unknown"),
            tostring(itemID or 0),
            tostring(history and history.instance or ""),
            tostring(history and history.boss or ""),
            tostring(history and history.responseID or "")
        }, "|"),
    }
end

local function buildLootKey(entry)
    if type(entry) ~= "table" then return "" end

    if entry.id and entry.id ~= "" then
        return tostring(entry.id)
    end

    return table.concat({
        tostring(entry.date or ""),
        tostring(entry.time or ""),
        tostring(entry.player or ""),
        tostring(entry.itemID or 0),
        tostring(entry.instance or ""),
        tostring(entry.boss or ""),
        tostring(entry.responseID or "")
    }, "|")
end

local function addEntry(entry)
    if type(entry) ~= "table" then return false end
    CowmeraRCDB.loot = CowmeraRCDB.loot or {}

    local newKey = buildLootKey(entry)
    if newKey ~= "" then
        for _, existing in ipairs(CowmeraRCDB.loot) do
            if buildLootKey(existing) == newKey then
                return false
            end
        end
    end

    table.insert(CowmeraRCDB.loot, entry)
    while #CowmeraRCDB.loot > 1000 do
        table.remove(CowmeraRCDB.loot, 1)
    end
    return true
end

local function importExistingHistory()
    local src = _G.RCLootCouncilLootDB
    local fr = src and src.factionrealm
    if type(fr) ~= "table" then
        rcprint("No RCLootCouncil history DB found.")
        return 0
    end

    local added = 0
    for playerName, entries in pairs(fr) do
        if type(entries) == "table" then
            for _, history in ipairs(entries) do
                if addEntry(normalizeHistoryEntry(playerName, history)) then
                    added = added + 1
                end
            end
        end
    end
    return added
end

local function hookRCLootHistory()
    if not _G.RCLootCouncil then
        return false, "RCLootCouncil not loaded."
    end

    local ok, LootHistory = pcall(function()
        return RCLootCouncil:GetModule("RCLootHistory", true)
    end)
    if not ok or not LootHistory then
        return false, "RCLootHistory module not found."
    end

    if LootHistory.__cowmeraRCHooked then
        return true, "Already hooked."
    end
    LootHistory.__cowmeraRCHooked = true

    local orig = LootHistory.OnHistoryReceived
    function LootHistory:OnHistoryReceived(name, history)
        if orig then
            orig(self, name, history)
        end
        addEntry(normalizeHistoryEntry(name, history))
    end

    return true, "Hook active."
end

local function tryHook()
    return hookRCLootHistory()
end

SLASH_COWMERARC1 = "/cowmerarc"
SLASH_COWMERARC2 = "/crc"
SlashCmdList["COWMERARC"] = function(msg)
    msg = tostring(msg or ""):lower():gsub("^%s+", ""):gsub("%s+$", "")

    if msg == "status" then
        local hooked = false
        if _G.RCLootCouncil then
            local ok, LootHistory = pcall(function()
                return RCLootCouncil:GetModule("RCLootHistory", true)
            end)
            hooked = ok and LootHistory and LootHistory.__cowmeraRCHooked or false
        end
        rcprint("entries: " .. tostring(countEntries()))
        rcprint("hooked: " .. tostring(hooked))
    elseif msg == "import" then
        local added = importExistingHistory()
        rcprint("imported " .. tostring(added) .. " entr" .. (added == 1 and "y" or "ies"))
    elseif msg == "hook" then
        local ok, why = tryHook()
        rcprint((ok and "hooked" or "not hooked") .. ": " .. tostring(why))
    else
        rcprint("Commands:")
        rcprint("/cowmerarc status")
        rcprint("/cowmerarc import")
        rcprint("/cowmerarc hook")
    end
end

local f = CreateFrame("Frame")
f:RegisterEvent("ADDON_LOADED")
f:RegisterEvent("PLAYER_LOGIN")
f:SetScript("OnEvent", function(_, event, addonName)
    if event == "ADDON_LOADED" and addonName == "RCLootCouncil" then
        C_Timer.After(1, function()
            local ok, msg = tryHook()
            rcprint(ok and "bridge active" or (msg or "bridge inactive"))
        end)
    elseif event == "PLAYER_LOGIN" then
        C_Timer.After(2, function()
            local ok, msg = tryHook()
            rcprint(ok and "bridge active" or (msg or "bridge inactive"))
        end)
    end
end)

C_Timer.After(3, function()
    local ok = select(1, tryHook())
    if ok then
        rcprint("bridge active")
    end
end)
