local PT = AhUI
if not PT then return end

PT.RecipeList = PT.RecipeList or {}
local M = PT.RecipeList
local C = M.Core

M.Calculation = M.Calculation or {}
local Calc = M.Calculation

local function DeepCopy(value, seen)
    if type(value) ~= "table" then
        return value
    end

    seen = seen or {}
    if seen[value] then
        return seen[value]
    end

    local out = {}
    seen[value] = out

    for k, v in pairs(value) do
        out[DeepCopy(k, seen)] = DeepCopy(v, seen)
    end

    return out
end

local function CopyChoices(choices)
    local out = {}
    local seen = {}

    for index, opt in ipairs(choices or {}) do
        local itemID = opt and tonumber(opt.itemID)
        if itemID and itemID > 0 and not seen[itemID] then
            seen[itemID] = true
            table.insert(out, {
                itemID = itemID,
                tier = opt.tier ~= nil and opt.tier or index,
            })
        end
    end

    table.sort(out, function(a, b)
        local at = tonumber(a.tier) or 0
        local bt = tonumber(b.tier) or 0
        if at == bt then
            return (a.itemID or 0) < (b.itemID or 0)
        end
        return at < bt
    end)

    return out
end

local function GetPrice(itemID)
    if not C or not C.GetAuctionatorPriceByItemID then
        return nil
    end

    return C.GetAuctionatorPriceByItemID(itemID)
end

local function SelectCheapestChoice(choices)
    local selected = nil
    local selectedPrice = nil

    for _, opt in ipairs(CopyChoices(choices)) do
        local price = GetPrice(opt.itemID)
        if price and price > 0 and (not selectedPrice or price < selectedPrice) then
            selected = opt
            selectedPrice = price
        end
    end

    return selected, selectedPrice
end

local function SelectByTier(choices, highest)
    local selected = nil

    for _, opt in ipairs(CopyChoices(choices)) do
        if not selected then
            selected = opt
        elseif highest then
            if (tonumber(opt.tier) or 0) > (tonumber(selected.tier) or 0) then
                selected = opt
            end
        else
            if (tonumber(opt.tier) or 0) < (tonumber(selected.tier) or 0) then
                selected = opt
            end
        end
    end

    return selected
end

function Calc:CloneEntry(entry)
    return DeepCopy(entry)
end

function Calc:ApplyCheapestReagents(entry)
    if type(entry) ~= "table" then
        return nil
    end

    local copy = DeepCopy(entry)
    copy.autoCheap = true

    for _, reagent in ipairs(copy.reagents or {}) do
        if reagent.choices and #reagent.choices > 0 then
            local selected = SelectCheapestChoice(reagent.choices) or SelectByTier(reagent.choices, false)
            reagent.itemID = selected and selected.itemID or reagent.itemID
            reagent.tier = selected and selected.tier or reagent.tier
        end
    end

    return copy
end

function Calc:ApplyLowestTierReagents(entry)
    if type(entry) ~= "table" then
        return nil
    end

    local copy = DeepCopy(entry)
    copy.autoCheap = false

    for _, reagent in ipairs(copy.reagents or {}) do
        if reagent.choices and #reagent.choices > 0 then
            local selected = SelectByTier(reagent.choices, false)
            reagent.itemID = selected and selected.itemID or reagent.itemID
            reagent.tier = selected and selected.tier or reagent.tier
        end
    end

    return copy
end

function Calc:ApplyHighestTierReagents(entry)
    if type(entry) ~= "table" then
        return nil
    end

    local copy = DeepCopy(entry)
    copy.autoCheap = false

    for _, reagent in ipairs(copy.reagents or {}) do
        if reagent.choices and #reagent.choices > 0 then
            local selected = SelectByTier(reagent.choices, true)
            reagent.itemID = selected and selected.itemID or reagent.itemID
            reagent.tier = selected and selected.tier or reagent.tier
        end
    end

    return copy
end

function Calc:EvaluateEntry(entry, quantity)
    if type(entry) ~= "table" or not C then
        return nil
    end

    quantity = math.max(1, math.floor(tonumber(quantity) or tonumber(entry.quantity) or 1))

    local cost, complete, pricedCount, totalCount = nil, false, 0, 0
    local value, resultItemID, resultCount, resultUnitPrice = nil, nil, nil, nil
    local profit = nil

    if C.GetRecipeAuctionatorCost then
        cost, complete, pricedCount, totalCount = C.GetRecipeAuctionatorCost(entry)
    end

    if C.GetRecipeAuctionatorValue then
        value, resultItemID, resultCount, resultUnitPrice = C.GetRecipeAuctionatorValue(entry)
    end

    if cost and value then
        profit = value - cost
    end

    return {
        recipeID = entry.recipeID,
        recipeName = entry.recipeName,
        quantity = quantity,
        unitCost = cost,
        unitValue = value,
        unitProfit = profit,
        totalCost = cost and (cost * quantity) or nil,
        totalValue = value and (value * quantity) or nil,
        totalProfit = profit and (profit * quantity) or nil,
        costComplete = complete == true,
        pricedReagents = pricedCount or 0,
        totalReagents = totalCount or 0,
        resultItemID = resultItemID,
        resultCount = resultCount,
        resultUnitPrice = resultUnitPrice,
    }
end

function Calc:EvaluateCheapest(entry, quantity)
    local variant = self:ApplyCheapestReagents(entry)
    local result = self:EvaluateEntry(variant, quantity)
    if result then
        result.variant = variant
        result.mode = "cheapest"
    end
    return result
end

function Calc:EvaluateLowestTier(entry, quantity)
    local variant = self:ApplyLowestTierReagents(entry)
    local result = self:EvaluateEntry(variant, quantity)
    if result then
        result.variant = variant
        result.mode = "lowestTier"
    end
    return result
end

function Calc:EvaluateHighestTier(entry, quantity)
    local variant = self:ApplyHighestTierReagents(entry)
    local result = self:EvaluateEntry(variant, quantity)
    if result then
        result.variant = variant
        result.mode = "highestTier"
    end
    return result
end

function Calc:GetConcentrationMetrics(entry, quantity)
    if type(entry) ~= "table" or not C or not C.GetRecipeLiveConcentrationInfo then
        return nil
    end

    quantity = math.max(1, math.floor(tonumber(quantity) or tonumber(entry.quantity) or 1))

    local info = C.GetRecipeLiveConcentrationInfo(entry)
    if not info or not info.available then
        return nil
    end

    local cost = math.max(0, tonumber(info.cost) or 0)
    if cost <= 0 then
        return nil
    end

    local base = self:EvaluateEntry(entry, quantity)

    return {
        cost = cost,
        totalCost = cost * quantity,
        currencyID = info.currencyID,
        upgradesQuality = info.upgradesQuality == true,
        beforeQualityID = info.beforeQualityID,
        afterQualityID = info.afterQualityID,
        bonusSkill = math.max(0, tonumber(info.bonusSkillFromConcentration) or 0),
        profitPerConcentration = base and base.unitProfit and (base.unitProfit / cost) or nil,
        baseEvaluation = base,
    }
end