-- ============================================================================
-- HDG_PowerCrafting.lua - DAG-Based Recursive Recipe Resolution
-- ============================================================================
-- Handles recursive crafting dependency resolution:
-- - Builds dependency graphs for crafted reagents
-- - Detects which sub-components player can craft
-- - Calculates total raw materials needed
-- - Generates crafting order (leaf nodes first via topological sort)
-- - Handles circular dependencies gracefully
-- ============================================================================

HDG_PowerCrafting = {}

-- ============================================================================
-- CONSTANTS
-- ============================================================================

local MAX_RECURSION_DEPTH = 10 -- Prevent infinite loops
local CRAFTED_SOURCE = "Crafted"
local dependencyGraphCache = {} -- reagent signature → cached graph result

-- ============================================================================
-- ITEM ID LOOKUP INDEX (built lazily)
-- HDG_ProfessionsDB is keyed by recipeID, but we need to lookup by itemID
-- ============================================================================

local itemIDIndex = nil     -- [itemID] = recipeID (preferred recipe)
local allRecipesIndex = nil -- [itemID] = { recipeID1, recipeID2, ... } (all recipes)

local function BuildItemIDIndex()
    if itemIDIndex then return end
    itemIDIndex = {}
    allRecipesIndex = {}

    if not HDG_ProfessionsDB then return end

    -- First pass: collect all recipes for each itemID
    for recipeID, entry in pairs(HDG_ProfessionsDB) do
        if entry.itemID then
            if not allRecipesIndex[entry.itemID] then
                allRecipesIndex[entry.itemID] = {}
            end
            table.insert(allRecipesIndex[entry.itemID], recipeID)
        end
    end

    -- Second pass: select preferred recipe for each itemID
    -- Priority: Mining > other professions > Alchemy Transmutes
    for itemID, recipeIDs in pairs(allRecipesIndex) do
        local preferredRecipeID = nil
        local preferredPriority = 0 -- Higher = better

        for _, recipeID in ipairs(recipeIDs) do
            local entry = HDG_ProfessionsDB[recipeID]
            local priority = 1 -- Default

            if entry.profession == "Mining" then
                priority = 10 -- Highest priority for smelting
            elseif entry.profession == "Alchemy" and entry.name and entry.name:match("^Transmute:") then
                priority = 0  -- Lowest priority for transmutes
            elseif entry.profession ~= "Alchemy" then
                priority = 5  -- Medium priority for other professions
            end

            if priority > preferredPriority then
                preferredPriority = priority
                preferredRecipeID = recipeID
            end
        end

        itemIDIndex[itemID] = preferredRecipeID
    end
end

-- Get recipe entry by output itemID (returns preferred recipe)
local function GetRecipeByItemID(itemID)
    BuildItemIDIndex()
    local recipeID = itemIDIndex and itemIDIndex[itemID]
    if recipeID then
        return HDG_ProfessionsDB[recipeID]
    end
    return nil
end

-- Public version for other modules
function HDG_PowerCrafting.GetRecipeByItemID(itemID)
    return GetRecipeByItemID(itemID)
end

-- Get ALL recipes that produce a given itemID (for showing alternatives)
function HDG_PowerCrafting.GetAllRecipesForItem(itemID)
    BuildItemIDIndex()
    if not allRecipesIndex or not allRecipesIndex[itemID] then
        return {}
    end

    local recipes = {}
    for _, recipeID in ipairs(allRecipesIndex[itemID]) do
        local entry = HDG_ProfessionsDB[recipeID]
        if entry then
            table.insert(recipes, {
                recipeID = recipeID,
                name = entry.name,
                profession = entry.profession,
                isPreferred = (recipeID == itemIDIndex[itemID]),
                slots = entry.slots
            })
        end
    end

    -- Sort: preferred first, then by profession
    table.sort(recipes, function(a, b)
        if a.isPreferred ~= b.isPreferred then
            return a.isPreferred
        end
        return (a.profession or "") < (b.profession or "")
    end)

    return recipes
end

-- ============================================================================
-- DEPENDENCY GRAPH BUILDER
-- ============================================================================

-- Generate deterministic cache key from recipe reagents
local function GetReagentSignature(decorRecipe)
    if not decorRecipe or not decorRecipe.reagents then return nil end
    local parts = {}
    for reagentID, reagentData in pairs(decorRecipe.reagents) do
        parts[#parts + 1] = reagentID .. ":" .. (reagentData.qty or 1)
    end
    table.sort(parts)
    return table.concat(parts, ",")
end

-- Check if an item is a crafted reagent
function HDG_PowerCrafting.IsCraftedReagent(itemID)
    if not itemID then return false end
    local source = HDG_ReagentsDB and HDG_ReagentsDB[itemID]
    -- Handle new format "Crafted" or legacy format
    if type(source) == "string" then
        return source == CRAFTED_SOURCE or source:match("^Crafted")
    end
    return false
end

-- Get the recipe for a crafted item (if known)
-- Uses HDG_ProfessionsDB v2 (slot-based format, keyed by recipeID)
-- Only returns "basic" slot reagents (required materials)
-- Quality variants are already deduplicated in extraction (stored in slot.variants)
function HDG_PowerCrafting.GetCraftedRecipe(itemID)
    local entry = GetRecipeByItemID(itemID)
    if not entry then return nil end

    -- New v2 slot-based format
    if entry.slots then
        local reagents = {}
        for _, slot in ipairs(entry.slots) do
            -- Only include "basic" slots (required materials)
            -- Skip "modifying", "finishing", "automatic" (all optional)
            if slot.type == "basic" and slot.itemID then
                reagents[slot.itemID] = slot.qty or 1
            end
        end
        if next(reagents) then
            return reagents
        end
    end

    return nil
end

-- Build dependency graph for a decor recipe
-- Returns: { nodes = {}, edges = {}, leafNodes = {}, craftOrder = {} }
function HDG_PowerCrafting.BuildDependencyGraph(decorRecipe)
    if not decorRecipe or not decorRecipe.reagents then
        return nil, "Invalid recipe"
    end

    -- Check memoization cache (graphs are deterministic for same reagents)
    local cacheKey = GetReagentSignature(decorRecipe)
    if cacheKey and dependencyGraphCache[cacheKey] then
        return dependencyGraphCache[cacheKey]
    end

    local graph = {
        nodes = {},        -- itemID -> { id, name, qty, source, depth }
        edges = {},        -- { from = parentID, to = childID }
        leafNodes = {},    -- Items that don't need further crafting
        craftOrder = {},   -- Topological order (leaf first)
        rawMaterials = {}, -- Final raw materials needed
    }

    local visited = {}
    local inStack = {} -- For cycle detection

    -- Recursive function to build graph
    local function addNode(itemID, qty, depth, parentID)
        if depth > MAX_RECURSION_DEPTH then
            return false, "Max recursion depth exceeded"
        end

        -- Cycle detection
        if inStack[itemID] then
            -- Circular dependency detected - treat as leaf
            if not graph.nodes[itemID] then
                local name = HDG_PowerCrafting.GetItemName(itemID)
                graph.nodes[itemID] = {
                    id = itemID,
                    name = name,
                    qty = qty,
                    source = "Circular",
                    depth = depth,
                    isCircular = true,
                }
                graph.leafNodes[itemID] = true
            else
                graph.nodes[itemID].qty = graph.nodes[itemID].qty + qty
            end
            return true
        end

        inStack[itemID] = true

        -- Check if already processed
        if visited[itemID] then
            -- Add to existing quantity
            graph.nodes[itemID].qty = graph.nodes[itemID].qty + qty

            -- Also update raw materials if this is a leaf node
            if graph.rawMaterials[itemID] then
                graph.rawMaterials[itemID].qty = graph.rawMaterials[itemID].qty + qty
            end

            -- Add edge if there's a parent
            if parentID then
                table.insert(graph.edges, { from = parentID, to = itemID, qty = qty })
            end

            inStack[itemID] = nil
            return true
        end

        visited[itemID] = true

        -- Get item info
        local name = HDG_PowerCrafting.GetItemName(itemID)
        local source = HDG_ReagentsDB and HDG_ReagentsDB[itemID] or "Unknown"

        -- Create node
        graph.nodes[itemID] = {
            id = itemID,
            name = name,
            qty = qty,
            source = source,
            depth = depth,
        }

        -- Add edge if there's a parent
        if parentID then
            table.insert(graph.edges, { from = parentID, to = itemID, qty = qty })
        end

        -- Check if this is a crafted item with known recipe
        local craftRecipe = HDG_PowerCrafting.GetCraftedRecipe(itemID)
        local isCrafted = HDG_PowerCrafting.IsCraftedReagent(itemID)

        if isCrafted and craftRecipe and next(craftRecipe) then
            -- This is a crafted item - recurse into its components
            graph.nodes[itemID].isCrafted = true

            for subItemID, subQty in pairs(craftRecipe) do
                local success, err = addNode(subItemID, subQty * qty, depth + 1, itemID)
                if not success then
                    inStack[itemID] = nil
                    return false, err
                end
            end
        else
            -- This is a leaf node (gathering, vendor, or unknown crafted)
            graph.leafNodes[itemID] = true

            -- Add to raw materials
            if graph.rawMaterials[itemID] then
                graph.rawMaterials[itemID].qty = graph.rawMaterials[itemID].qty + qty
            else
                graph.rawMaterials[itemID] = {
                    id = itemID,
                    name = name,
                    qty = qty,
                    source = source,
                }
            end
        end

        inStack[itemID] = nil
        return true
    end

    -- Process all reagents in the decor recipe
    for reagentID, reagentData in pairs(decorRecipe.reagents) do
        local qty = reagentData.qty or 1
        local success, err = addNode(reagentID, qty, 0, nil)
        if not success then
            return nil, err
        end
    end

    -- Generate topological sort order (leaf nodes first)
    graph.craftOrder = HDG_PowerCrafting.TopologicalSort(graph)

    -- Cache the result for subsequent calls with same reagents
    if cacheKey then
        dependencyGraphCache[cacheKey] = graph
    end

    return graph
end

function HDG_PowerCrafting.InvalidateGraphCache()
    dependencyGraphCache = {}
end

-- ============================================================================
-- TOPOLOGICAL SORT (Kahn's Algorithm)
-- ============================================================================

function HDG_PowerCrafting.TopologicalSort(graph)
    if not graph or not graph.nodes then return {} end

    local result = {}
    local inDegree = {}
    local queue = {}

    -- Calculate in-degree for each node
    for itemID, _ in pairs(graph.nodes) do
        inDegree[itemID] = 0
    end

    for _, edge in ipairs(graph.edges) do
        if inDegree[edge.to] then
            inDegree[edge.to] = inDegree[edge.to] + 1
        end
    end

    -- Find all nodes with in-degree 0 (leaf nodes)
    for itemID, degree in pairs(inDegree) do
        if degree == 0 then
            table.insert(queue, itemID)
        end
    end

    -- Process queue
    while #queue > 0 do
        local current = table.remove(queue, 1)
        table.insert(result, current)

        -- Find all edges FROM this node (nodes that depend on it)
        for _, edge in ipairs(graph.edges) do
            if edge.to == current then
                -- This is an edge TO current, so edge.from depends on current
                local from = edge.from
                if inDegree[from] then
                    inDegree[from] = inDegree[from] - 1
                    if inDegree[from] == 0 then
                        table.insert(queue, from)
                    end
                end
            end
        end
    end

    return result
end

-- ============================================================================
-- RAW MATERIALS CALCULATION
-- ============================================================================

-- Get total raw materials needed for a crafting queue
function HDG_PowerCrafting.CalculateRawMaterials(shoppingList)
    if not shoppingList then return {} end

    local totalRawMaterials = {}

    for recipeKey, quantity in pairs(shoppingList) do
        local recipe = HDG_DecorDB and HDG_DecorDB[tonumber(recipeKey)]
        if recipe and quantity > 0 then
            local graph = HDG_PowerCrafting.BuildDependencyGraph(recipe)
            if graph and graph.rawMaterials then
                for itemID, matData in pairs(graph.rawMaterials) do
                    if totalRawMaterials[itemID] then
                        totalRawMaterials[itemID].qty = totalRawMaterials[itemID].qty + (matData.qty * quantity)
                    else
                        totalRawMaterials[itemID] = {
                            id = itemID,
                            name = matData.name,
                            qty = matData.qty * quantity,
                            source = matData.source,
                        }
                    end
                end
            end
        end
    end

    return totalRawMaterials
end

-- ============================================================================
-- CRAFTING ORDER GENERATION
-- ============================================================================

-- Get ordered crafting steps for a recipe
-- Returns array of { itemID, name, qty, profession, step }
function HDG_PowerCrafting.GetCraftingOrder(decorRecipe)
    local graph = HDG_PowerCrafting.BuildDependencyGraph(decorRecipe)
    if not graph then return {} end

    local steps = {}
    local stepNum = 1

    -- Process in topological order
    for _, itemID in ipairs(graph.craftOrder) do
        local node = graph.nodes[itemID]
        if node and node.isCrafted then
            table.insert(steps, {
                step = stepNum,
                itemID = itemID,
                name = node.name,
                qty = node.qty,
                source = node.source,
            })
            stepNum = stepNum + 1
        end
    end

    return steps
end

-- ============================================================================
-- UTILITY FUNCTIONS
-- ============================================================================

-- Get item name - delegates to HDG_Data for caching, with local DB fallbacks
function HDG_PowerCrafting.GetItemName(itemID)
    -- Try HDG_Data cache first (shared with other modules)
    if HDG_Data and HDG_Data.GetCachedItemName then
        local name = HDG_Data.GetCachedItemName(itemID)
        if name and name ~= "Loading..." then
            return name
        end
    end

    -- Check DecorDB reagents as fallback
    if HDG_DecorDB then
        for _, recipe in pairs(HDG_DecorDB) do
            if recipe.reagents and recipe.reagents[itemID] then
                return recipe.reagents[itemID].name
            end
        end
    end

    -- Return placeholder - HDG_Data will async load and cache it
    return "Item #" .. tostring(itemID)
end

-- Check if player can craft a specific intermediate item
function HDG_PowerCrafting.CanPlayerCraft(itemID)
    -- This would need to check player's profession recipes
    -- For now, return nil (unknown)
    -- Future: integrate with C_TradeSkillUI to check known recipes
    return nil
end

-- Get detailed material breakdown with hierarchy
function HDG_PowerCrafting.GetMaterialBreakdown(decorRecipe)
    local graph = HDG_PowerCrafting.BuildDependencyGraph(decorRecipe)
    if not graph then return nil end

    local breakdown = {
        directMaterials = {},       -- First-level reagents
        intermediateMaterials = {}, -- Crafted reagents
        rawMaterials = {},          -- Final leaf materials
        craftingSteps = {},         -- Ordered crafting steps
    }

    -- Direct materials (from recipe)
    if decorRecipe.reagents then
        for itemID, data in pairs(decorRecipe.reagents) do
            local source = HDG_ReagentsDB and HDG_ReagentsDB[itemID] or "Unknown"
            breakdown.directMaterials[itemID] = {
                id = itemID,
                name = data.name,
                qty = data.qty,
                source = source,
                isCrafted = (source == CRAFTED_SOURCE),
            }
        end
    end

    -- Intermediate materials (crafted reagents that need sub-crafting)
    for itemID, node in pairs(graph.nodes) do
        if node.isCrafted and node.depth > 0 then
            breakdown.intermediateMaterials[itemID] = {
                id = itemID,
                name = node.name,
                qty = node.qty,
                depth = node.depth,
            }
        end
    end

    -- Raw materials
    breakdown.rawMaterials = graph.rawMaterials

    -- Crafting steps
    breakdown.craftingSteps = HDG_PowerCrafting.GetCraftingOrder(decorRecipe)

    return breakdown
end
