-- Book-Quests.lua -- Spell-Item ID catching-solving module --
-- WanderBook: Forge your path through Azeroth with your own goals. --
-- Made by Blakmane. May 2025. --

local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_LOGIN")
f:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")

-- Runtime caches
local spellNameCache = {}
local combatSpellsSeen = {}
local itemNameCache = {}
local itemScanDone = false
local saveEnabled = true

local activeProcWatch = false
local procWatchStart = 0
local procWindowDuration = 10

-- SavedVariables table (auto-created if needed)
IDCatcherDB = IDCatcherDB or { spells = {}, items = {}, lastScanIndex = 1 }

local function StartProcWatch()
    activeProcWatch = true
    procWatchStart = GetTime()
end

-- Helper: learn and store spell
local function LearnSpell(id)
    if not IDCatcherDB.spells[id] then
        local info = C_Spell.GetSpellInfo(id)
        if info and info.name then
            IDCatcherDB.spells[id] = info.name
        end
    end
end

-- Helper: learn and store item
local function LearnItem(id)
    if not IDCatcherDB.items[id] then
        local name = C_Item.GetItemNameByID(id)
        if name then
            IDCatcherDB.items[id] = name
        end
    end
end

-- Cache player spellbook
local function BuildSpellNameCache()
    local numSkillLines = C_SpellBook.GetNumSpellBookSkillLines()
    for skillLineIndex = 1, numSkillLines do
        local skillLineInfo = C_SpellBook.GetSpellBookSkillLineInfo(skillLineIndex)
        if skillLineInfo then
            local offset = skillLineInfo.itemIndexOffset
            local numSlots = skillLineInfo.numSpellBookItems
            for i = offset + 1, offset + numSlots do
                local info = C_SpellBook.GetSpellBookItemInfo(i, Enum.SpellBookSpellBank.Player)
                if info and info.spellID then
                    local spellID = info.spellID
                    local spellInfo = C_Spell.GetSpellInfo(spellID)
                    if spellInfo and spellInfo.name then
                        local name = spellInfo.name:lower()
                        spellNameCache[name] = spellNameCache[name] or {}
                        table.insert(spellNameCache[name], spellID)
                        if saveEnabled then LearnSpell(spellID) end
                    end
                end
            end
        end
    end
end

-- Inventory and equipped item scan
local function ScanEquippedAndBagItems()
    itemNameCache = {}
    for slot = 0, 19 do
        local itemLoc = ItemLocation:CreateFromEquipmentSlot(slot)
        if C_Item.DoesItemExist(itemLoc) then
            local name = C_Item.GetItemName(itemLoc)
            local id = C_Item.GetItemID(itemLoc)
            if name and id then
                local lower = name:lower()
                itemNameCache[lower] = itemNameCache[lower] or {}
                table.insert(itemNameCache[lower], id)
                if saveEnabled then LearnItem(id) end
            end
        end
    end

    for bag = 0, 4 do
        local numSlots = C_Container.GetContainerNumSlots(bag)
        for slot = 1, numSlots do
            local itemLoc = ItemLocation:CreateFromBagAndSlot(bag, slot)
            if C_Item.DoesItemExist(itemLoc) then
                local name = C_Item.GetItemName(itemLoc)
                local id = C_Item.GetItemID(itemLoc)
                if name and id then
                    local lower = name:lower()
                    itemNameCache[lower] = itemNameCache[lower] or {}
                    table.insert(itemNameCache[lower], id)
                    if saveEnabled then LearnItem(id) end
                end
            end
        end
    end
    itemScanDone = true
end

-- /idmatch command
local function HandleIDMatch(msg)
    local id = tonumber(msg)
    local linkMatch = msg:match("|h%[(.-)%]|h")
    local bracketMatch = msg:match("%[(.-)%]")
    local raw = linkMatch or bracketMatch or msg
    local targetName = raw:lower():gsub("^\"(.-)\"$", "%1")
    local found = {}

    -- Exact ID match
    if id then
        local spell = C_Spell.GetSpellInfo(id)
        if spell and spell.name then
            print("Spell ID " .. id .. " = " .. spell.name)
            return
        end
        local name = C_Item.GetItemNameByID(id)
        if name then
            print("Item ID " .. id .. " = " .. name)
            return
        end
        print("No name found for ID: " .. id)
        return
    end

    -- Spellbook cache
    if spellNameCache[targetName] then
        for _, sid in ipairs(spellNameCache[targetName]) do
            table.insert(found, sid)
        end
    end

    -- Combat-seen spells
    for sid in pairs(combatSpellsSeen) do
        local info = C_Spell.GetSpellInfo(sid)
        if info and info.name and info.name:lower() == targetName and not tContains(found, sid) then
            table.insert(found, sid)
            if saveEnabled then LearnSpell(sid) end
        end
    end

    -- Auras
    for i = 1, 40 do
        for _, type in ipairs({ "HELPFUL", "HARMFUL" }) do
            local aura = C_UnitAuras.GetAuraDataByIndex("player", i, type)
            if not aura then break end
            if aura.name and aura.name:lower() == targetName and aura.spellId and not tContains(found, aura.spellId) then
                table.insert(found, aura.spellId)
                if saveEnabled then LearnSpell(aura.spellId) end
            end
        end
    end

    -- Items
    if not itemScanDone then
        ScanEquippedAndBagItems()
    end
    if itemNameCache[targetName] then
        for _, iid in ipairs(itemNameCache[targetName]) do
            table.insert(found, iid)
        end
    end
    
    -- Include saved spell IDs
    for sid, name in pairs(IDCatcherDB.spells) do
        if name:lower() == targetName and not tContains(found, sid) then
            table.insert(found, sid)
        end
    end

    -- Include saved item IDs
    for iid, name in pairs(IDCatcherDB.items) do
        if name:lower() == targetName and not tContains(found, iid) then
            table.insert(found, iid)
        end
    end

    -- Output
    if #found > 0 then
        print("Matches for \"" .. targetName .. "\": " .. table.concat(found, ", "))
    else
        print("No matches found for: " .. targetName)
    end
end

-- /idmatchsave command
local function HandleSaveControl(msg)
    local cmd = msg:lower()
    if cmd == "on" then
        saveEnabled = true
        print("IDCatcher: Saving ENABLED")
    elseif cmd == "off" then
        saveEnabled = false
        print("IDCatcher: Saving DISABLED")
    elseif cmd == "export" then
        print("Stored Spells:")
        for k, v in pairs(IDCatcherDB.spells) do print(k, v) end
        print("Stored Items:")
        for k, v in pairs(IDCatcherDB.items) do print(k, v) end
    else
        print("Usage: /idmatchsave on | off | export")
    end
end

local massScanInProgress = false
local currentScanIndex = IDCatcherDB.lastScanIndex or 1
local maxScanID = 400000
local scanBatchSize = 500

local function ProcessMassScanBatch()
    if currentScanIndex > maxScanID then
        --print("IDCatcher: Mass scan complete.")
        massScanInProgress = false
        return
    end

    local endIndex = math.min(currentScanIndex + scanBatchSize - 1, maxScanID)

    for id = currentScanIndex, endIndex do
        if not IDCatcherDB.items[id] then
            local name = C_Item.GetItemNameByID(id)
            if name then
                local lower = name:lower()
                itemNameCache[lower] = itemNameCache[lower] or {}
                if not tContains(itemNameCache[lower], id) then
                    table.insert(itemNameCache[lower], id)
                end
                IDCatcherDB.items[id] = name
            end
        end
    end

    currentScanIndex = endIndex + 1
    IDCatcherDB.lastScanIndex = currentScanIndex
    C_Timer.After(0.1, ProcessMassScanBatch)
end

SLASH_IDMATCHMASS1 = '/idmatchmassscan'
SlashCmdList["IDMATCHMASS"] = function()
    if massScanInProgress then
        print("IDCatcher: Mass scan already running.")
        return
    end
    print("IDCatcher: Starting mass item ID scan...")
    massScanInProgress = true
    currentScanIndex = IDCatcherDB.lastScanIndex or 1
    ProcessMassScanBatch()
end

-- Passive background scan on login
--[[if not massScanInProgress then
    print("|cfffe35eeThe WanderBook seems focused in learning some IDs...")
    massScanInProgress = true
    currentScanIndex = IDCatcherDB.lastScanIndex or 1
    C_Timer.After(5, ProcessMassScanBatch) -- small delay after login
end]]

SLASH_IDMATCHSCAN1 = '/idmatchscan'
SlashCmdList["IDMATCHSCAN"] = function()
    itemScanDone = false
    ScanEquippedAndBagItems()
    print("IDCatcher: Rescanned items.")
end

function IDCatcher_ResolveItemIDByName(name)
    if not name then return nil end
    local norm = name:lower():gsub("^%s*(.-)%s*$", "%1")

    if not itemScanDone then
        ScanEquippedAndBagItems()
    end

    -- Primary: runtime scan
    if itemNameCache[norm] and #itemNameCache[norm] > 0 then
        return itemNameCache[norm][1]
    end

    -- Fallback: saved DB
    for iid, dbName in pairs(IDCatcherDB.items or {}) do
        if dbName:lower() == norm then
            return iid
        end
    end

    return nil
end

function IDCatcher_ResolveSpellIDByName(name)
    if not name then return nil end
    local norm = name:lower():gsub("^%s*(.-)%s*$", "%1")

    BuildSpellNameCache()

    -- Primary: runtime scan
    if spellNameCache[norm] and #spellNameCache[norm] > 0 then
        return spellNameCache[norm][1]
    end

    -- Fallback: saved DB
    for sid, dbName in pairs(IDCatcherDB.spells or {}) do
        if dbName:lower() == norm then
            return sid
        end
    end

    return nil
end

-- Event handler
f:SetScript("OnEvent", function(_, event, ...)
    if event == "PLAYER_LOGIN" then
        BuildSpellNameCache()
        SLASH_IDMATCH1 = '/idmatch'
        SlashCmdList["IDMATCH"] = HandleIDMatch
        SLASH_IDMATCHSAVE1 = '/idmatchsave'
        SlashCmdList["IDMATCHSAVE"] = HandleSaveControl
    elseif event == "COMBAT_LOG_EVENT_UNFILTERED" then
        local _, _, _, _, _, _, _, _, _, _, _, spellID = CombatLogGetCurrentEventInfo()
        if spellID and type(spellID) == "number" then
            combatSpellsSeen[spellID] = true
            if saveEnabled then LearnSpell(spellID) end
        end
        if activeProcWatch and (GetTime() - procWatchStart) < procWindowDuration then
            local _, subevent, _, sourceGUID, _, _, _, _, _, _, _, spellID, spellName = CombatLogGetCurrentEventInfo()
            if sourceGUID == UnitGUID("player") then
                --print("Proc window detected:", subevent, spellID, spellName)
            end
        end
    end
end)

SLASH_STARTPROC1 = "/startproc"
SlashCmdList.STARTPROC = function()
    print("Starting proc watch...")
    StartProcWatch()
end
