Helpers = {}

-- Replaces table contents in-place to preserve the reference for reactive UI bindings.
-- Recursively preserves sub-table references (e.g. transfer.currentList.donors)
-- when both target and source have a table at the same key.
function Helpers.replaceTableContents(target, source)
    local sourceKeys = {}
    for k in pairs(source) do sourceKeys[k] = true end

    for k in pairs(target) do
        if not sourceKeys[k] then target[k] = nil end
    end

    for k, v in pairs(source) do
        if type(v) == 'table' and type(target[k]) == 'table' then
            Helpers.replaceTableContents(target[k], v)
        else
            target[k] = Helpers.cloneValue(v)
        end
    end
end

function Helpers.cloneValue(value)
    if type(value) == 'table' then
        if table and type(table.recursivecopy) == 'function' then
            return table.recursivecopy(value)
        end

        local copy = {}
        for key, child in pairs(value) do
            copy[key] = Helpers.cloneValue(child)
        end
        return copy
    end

    return value
end

function Helpers.normalizeTierPriceEntries(entries)
    local prices = {}
    if type(entries) ~= 'table' then
        return prices
    end

    for _, entry in ipairs(entries) do
        if type(entry) == 'table' then
            local tierId = tonumber(entry.tier) or tonumber(entry.tierId) or tonumber(entry.id)
            local price = tonumber(entry.price) or tonumber(entry.cost) or tonumber(entry.value)
            if tierId then
                prices[tierId + 1] = price or 0
            end
        end
    end

    return prices
end

function Helpers.normalizeClassPriceEntries(entries)
    local pricesByClass = {}
    if type(entries) ~= 'table' then
        return pricesByClass
    end

    for _, classInfo in ipairs(entries) do
        if type(classInfo) == 'table' then
            local classId = tonumber(classInfo.classId)
                or tonumber(classInfo.id)
                or tonumber(classInfo.classification)
            if classId then
                pricesByClass[classId] = Helpers.normalizeTierPriceEntries(classInfo.tiers)
            end
        end
    end

    return pricesByClass
end

function Helpers.formatHistoryDate(timestamp)
    if not timestamp then
        return 'Unknown'
    end
    
    -- If already a formatted string (contains "-" or ":"), return it as-is
    if type(timestamp) == 'string' then
        if string.find(timestamp, '-') or string.find(timestamp, ':') then
            return timestamp
        end
        -- Try to convert string to number (in case it's a numeric string)
        timestamp = tonumber(timestamp)
        if not timestamp then
            return 'Unknown'
        end
    end
    
    -- If it's a number (unix timestamp), format it
    if type(timestamp) == 'number' then
        if timestamp == 0 then
            return 'Unknown'
        end
        -- Protect os.date call with pcall in case of invalid timestamp
        local success, result = pcall(os.date, '%Y-%m-%d, %H:%M:%S', timestamp)
        if success then
            return result
        else
            return 'Unknown'
        end
    end
    
    return 'Unknown'
end

Helpers.rightArrow = "/modules/game_forge/images/arrows/icon-arrow-rightlarge.png"
Helpers.filledRightArrow = "/modules/game_forge/images/arrows/icon-arrow-rightlarge-filled.png"

Helpers.baseResult = {
    leftItemId = -1,
    leftTier = 0,
    leftClip = nil,
    rightItemId = -1,
    rightTier = 0,
    rightClip = nil,
    label = "",
    labelResult = "",
    bonusItem = -1,
    bonus = 0,
    bonusTier = 0,
    bonusItemClip = nil,
    bonusLabel = "",
    bonusAction = nil,
    arrows = {
        { arrow = Helpers.rightArrow, },
        { arrow = Helpers.filledRightArrow, },
        { arrow = Helpers.rightArrow, }
    },
    buttonLabel = "Close"
}

Helpers.green = "#44ad25"
Helpers.red = "#d33c3c"
Helpers.grey = "#c0c0c0"

function Helpers.handleDescription(data, currentType)
    if currentType == "convert-dust" then
        data.description =
        "Convert your dust into slivers. Slivers are used to create exalted cores which are useful for fusions and transfers."
        return
    end

    if currentType == "convert-dust-cost" then
        local dusts = data.currentDust or 0
        local necessaryDusts = data.conversion.necessaryDustToSliver or 0
        if dusts < necessaryDusts then
            data.description = "You do not have enough dust to generate a sliver."
        else
            local dustToSliver = data.conversion.dustToSliver or 0
            data.description = string.format("Click here to convert %d dust into %d slivers.",
                necessaryDusts, dustToSliver)
        end
        return
    end

    if currentType == "convert-sliver" then
        data.description = "Convert slivers into exalted cores. Exalted cores are useful for fusions and transfers."
        return
    end

    if currentType == "convert-sliver-cost" then
        local dusts = data.currentDust or 0
        local necessarySliver = data.conversion.sliverToCore or 0
        if dusts < necessarySliver then
            data.description = "You do not have enough dust to generate a sliver."
        else
            data.description = string.format("Click here to convert %d slivers into %d exalted cores.",
                necessarySliver, 1)
        end
        return
    end

    if currentType == "increase-dust-limit" then
        local dustMax = data.conversion.dustMax or 0
        data.description = string.format(
            "Use dust to increase permanently the amount of the dust you can gather (currently %d).", dustMax)
        return
    end

    if currentType == "increase-dust-limit-cost" then
        local dusts = data.currentDust or 0

        if dusts < data.conversion.dustMaxIncreaseCost then
            data.description = "You do not have enough dust to raise the limit."
        else
            local dustMax = data.conversion.dustMax or 0
            data.description = "You do not have enough dust to raise the limit."

            data.description = string.format("Click here to spend %d dust to increase your limit from %d to %d.",
                data.conversion.dustMaxIncreaseCost, dustMax, dustMax + 1)
        end
        return
    end

    if currentType == "transfer-convergence" then
        data.description = "A convergence transfer does not reduce the tier of the consumed item."

        return
    end

    if currentType == "transfer-donors-items" then
        local isConvergence = data.transfer.isConvergence
        if isConvergence then
            data.description =
            "You can transfer the tier of a classification 4 item to another item of the same classification. To do this, you must have at least one tier 1 item that will be consumed during the transfer and additional resources. The other item will receive the tier of the consumed item."
        else
            data.description =
            "You can transfer the tier of an item to another item of the same classification. To do so, you need at least a tier 2 item that will be consumed during the transfer and additional resources. The other item will received the consumed item's tier reduced by one."
        end
        return
    end

    if currentType == "transfer-cost-donor-item" then
        data.description =
        "Select an item whose tier you want to transfer. Warning! This item will be consumed during the transfer."
        return
    end

    if currentType == "transfer-cost-receiver-item" then
        data.description = "Select the item to which you want to transfer the tier."
        return
    end

    if currentType == "transfer-cost-donor-preview" then
        if data.transfer.selected.id == -1 then
            data.description =
            "You do not carry an item whose tier can be transferred. Note that imbued items cannot be used for a transfer."
            return
        end

        data.description = string.format("%s with tier %d will be consumed during the transfer.",
            "Unknown Item", data.transfer.selected.tier or 0)
        return
    end

    if currentType == "transfer-cost-dust" then
        local dusts = data.currentDust or 0
        local necessaryDusts = data.transfer.isConvergence and data.transfer.convergenceDust or data.transfer.dust or 0
        if dusts < necessaryDusts then
            data.description = string.format(
                "A transfer requires %d dust. Unfortunately you do not have the needed amount.", necessaryDusts)
        else
            data.description = string.format("A transfer requires %d dust.",
                necessaryDusts)
        end
        return
    end

    if currentType == "transfer-cost-exalted-core" then
        if data.transfer.selected.id == -1 then
            data.description =
            "Please select an item above to continue."
            return
        end

        local dusts = data.currentExaltedCores or 0
        local necessaryDusts = data.transfer.necessaryExaltedCores or 0
        if dusts < necessaryDusts then
            data.description = string.format(
                "A transfer requires %d exalted cores. Unfortunately you do not have the needed amount.", necessaryDusts)
        else
            data.description = string.format("Click here to spend %d dust to perform the transfer.",
                necessaryDusts)
        end
        return
    end

    if currentType == "transfer-forge-action" then
        if data.transfer.selected.id == -1 or data.transfer.selectedTarget.id == -1 then
            data.description =
            "Please select both the item whose tier you wish to transfer and the item you want to transfer the tier to."
            return
        end

        if not data.transfer.canTransfer then
            data.description = "You do not have all the ingredients you need."
            return
        end

        data.description = "Click here to carry out the transfer. This will consume all required ingredients."
        return
    end

    if currentType == "fusion-select-item" then
        data.description = "Select an item you want to fuse."
        return
    end

    if currentType == "fusion-preview-item" then
        if data.fusion.selected.id == -1 then
            data.description = "Please select an item above to continue."
        else
            data.description =
            "For the fusion a second item of the same type and tier is needed, which will be consumed. Be warned! The item will be consumed even if the fusion fails."
        end
        return
    end

    if currentType == "fusion-cost-dust" then
        local dust = data.currentDust or 0
        local necessaryDust = data.fusion.isConvergence and data.fusion.convergenceDust or data.fusion.dust or 0

        if dust < necessaryDust then
            data.description = string.format(
                "A fusion requires %d dust. Unfortunately you do not have the needed amount.", necessaryDust)
        else
            data.description = string.format(
                "A fusion requires %d dust. Be warned! The dust will be consumed even if the fusion fails.",
                necessaryDust)
            return
        end
    end

    if currentType == "fusion-improve-chance" then
        local baseImproveChance = data.fusion.chanceBase
        local improvedChance = data.fusion.chanceImproved
        local totalImprovedChance = baseImproveChance + improvedChance
        if data.fusion.chanceImprovedChecked then
            data.description =
                string.format(
                    "Click here if you do not want to use the exalted core. This will reduce your chance from %d%% to %d%%.",
                    totalImprovedChance, baseImproveChance)
        else
            data.description =
                string.format(
                    "Improve your chances! Click here to raise your chance for a successful fusion from %d%% to %d%% at the cost of one exalted core.",
                    baseImproveChance, totalImprovedChance)
        end
        return
    end

    if currentType == "fusion-reduce-loss" then
        if data.fusion.reduceTierLossChecked then
            data.description =
            "Click here if you do not want to use the exalted core. Note that you will lose the chance to keep the tier on the second item in case the fusion fails."
        else
            local reduceLoss = data.fusion.reduceTierLoss
            data.description =
                string.format(
                    "Reduce your losses! Click here to pay one exalted core and with that get a %d%% chance of keeping the tier of the second item in case the fusion fails.",
                    reduceLoss)
        end
        return
    end

    if currentType == "fusion-forge-action" then
        local isConvergence = data.fusion.isConvergence

        if not isConvergence then
            if data.fusion.selected.id == -1 then
                data.description = "Please select all required ingredients."
                return
            end
        else
            if data.fusion.selectedTarget.id == -1 then
                data.description = "Please select all required ingredients."
                return
            end
        end

        if not data.fusion.canFusion then
            data.description = "You do not have all the ingredients you need."
            return
        end

        data.description = "Click here to start a fusion attempt. This will consume or alter the required ingredients."
        return
    end
end
