local OFHotBrassPatch = {
    patched = false,
}

local AMMO_TYPE_ALIAS_BY_ITEM = {
    ["Base.9x39Bullets"] = "9x39",
    ["Base.Bullets22LR"] = "22LR",
    ["Base.Bullets50Magnum"] = "50AE",
    ["Base.762x54rBullets"] = "762x54R",
    ["Base.792x57Bullets"] = "792x57Maus",
    -- Common spelling variants seen in third-party weapon scripts.
    ["Base.308Bulets"] = "308Win",
    ["Base.762x54rBulets"] = "762x54R",
}

local function isPatchToggleEnabled()
    local vars = SandboxVars and SandboxVars.OpinionatedFirearms
    if vars and vars.HandleHotBrassCasingSpawnUseAmmoMaker ~= nil then
        return vars.HandleHotBrassCasingSpawnUseAmmoMaker == true
    end

    -- Backward compatibility for existing worlds.
    if vars and vars.HBVCEFAmmoMakerPatch ~= nil then
        return vars.HBVCEFAmmoMakerPatch == true
    end

    return true
end

local function isSessionEligible()
    return isPatchToggleEnabled()
end

local function getAmmoMakerFiredFromAmmoDataKey(ammoDataKey)
    if type(ammoDataKey) ~= "string" or ammoDataKey == "" then
        return nil
    end

    if type(ammoMakerAmmoData) ~= "table" or type(ammoMakerAmmoParts) ~= "table" then
        return nil
    end

    local ammoData = ammoMakerAmmoData[ammoDataKey]
    if type(ammoData) ~= "table" then
        return nil
    end

    local casingType = ammoData.casingType
    if type(casingType) ~= "string" or casingType == "" then
        return nil
    end

    local partData = ammoMakerAmmoParts[casingType]
    if type(partData) ~= "table" then
        return nil
    end

    local firedType = partData.partFired or partData.partOld
    if type(firedType) ~= "string" or firedType == "" then
        return nil
    end

    return firedType
end

local function getAmmoMakerFiredFromItemKey(ammoType)
    if type(ammoType) ~= "string" or ammoType == "" then
        return nil
    end

    if type(ammoMakerAmmoTypes) ~= "table" then
        return nil
    end

    local typeData = ammoMakerAmmoTypes[ammoType]
    if type(typeData) ~= "table" or type(typeData.ammoTypes) ~= "table" or #typeData.ammoTypes == 0 then
        return nil
    end

    local activeIndex = 1
    if type(typeData.modIds) == "table" and type(ammoMakerCompatibleMods) == "table" then
        for i = 1, #typeData.modIds do
            local modId = typeData.modIds[i]
            if ammoMakerCompatibleMods[modId] == true then
                activeIndex = i
            end
        end
    end

    if activeIndex < 1 or activeIndex > #typeData.ammoTypes then
        activeIndex = 1
    end

    local activeAmmoKey = typeData.ammoTypes[activeIndex]
    local activeFiredType = getAmmoMakerFiredFromAmmoDataKey(activeAmmoKey)
    if activeFiredType then
        return activeFiredType
    end

    for i = 1, #typeData.ammoTypes do
        local fallbackFiredType = getAmmoMakerFiredFromAmmoDataKey(typeData.ammoTypes[i])
        if fallbackFiredType then
            return fallbackFiredType
        end
    end

    return nil
end

local function getAmmoMakerFiredCasing(ammoType)
    if type(ammoType) ~= "string" or ammoType == "" then
        return nil
    end

    local directFiredType = getAmmoMakerFiredFromItemKey(ammoType)
    if directFiredType then
        return directFiredType
    end

    local aliasAmmoDataKey = AMMO_TYPE_ALIAS_BY_ITEM[ammoType]
    if aliasAmmoDataKey then
        local aliasFiredType = getAmmoMakerFiredFromAmmoDataKey(aliasAmmoDataKey)
        if aliasFiredType then
            return aliasFiredType
        end
    end

    local lowerAmmoType = string.lower(ammoType)
    for aliasItemType, ammoDataKey in pairs(AMMO_TYPE_ALIAS_BY_ITEM) do
        if string.lower(aliasItemType) == lowerAmmoType then
            local aliasFiredType = getAmmoMakerFiredFromAmmoDataKey(ammoDataKey)
            if aliasFiredType then
                return aliasFiredType
            end
        end
    end

    if type(ammoMakerAmmoTypes) == "table" then
        for itemType, typeData in pairs(ammoMakerAmmoTypes) do
            if type(itemType) == "string" and string.lower(itemType) == lowerAmmoType then
                if type(typeData) == "table" and type(typeData.ammoTypes) == "table" then
                    for i = 1, #typeData.ammoTypes do
                        local fallbackFiredType = getAmmoMakerFiredFromAmmoDataKey(typeData.ammoTypes[i])
                        if fallbackFiredType then
                            return fallbackFiredType
                        end
                    end
                end
            end
        end
    end

    return nil
end

local function applyPatch()
    if OFHotBrassPatch.patched then
        return true
    end

    if not isSessionEligible() then
        return false
    end

    if type(SpentCasingPhysics) ~= "table" then
        return false
    end

    if type(SpentCasingPhysics.getItemToEject) ~= "function" then
        return false
    end

    if type(SpentCasingPhysics.doSpawnCasing) ~= "function" then
        return false
    end

    local originalGetItemToEject = SpentCasingPhysics.getItemToEject
    local originalDoSpawnCasing = SpentCasingPhysics.doSpawnCasing

    SpentCasingPhysics.getItemToEject = function(ammoType)
        local mappedType = getAmmoMakerFiredCasing(ammoType)
        if mappedType then
            return mappedType
        end

        return originalGetItemToEject(ammoType)
    end

    SpentCasingPhysics.doSpawnCasing = function(player, weapon, params, racking, optionalItem)
        if not optionalItem and weapon and weapon.getAmmoType then
            local ammoTypeObj = weapon:getAmmoType()
            local ammoType = ammoTypeObj and ammoTypeObj.getItemKey and ammoTypeObj:getItemKey() or ammoTypeObj
            if ammoType then
                local mappedType = getAmmoMakerFiredCasing(tostring(ammoType))
                if mappedType then
                    optionalItem = mappedType
                    if racking then
                        -- Force rack ejects to stay as empties for this patch.
                        racking = false
                    end
                end
            end
        end

        return originalDoSpawnCasing(player, weapon, params, racking, optionalItem)
    end

    OFHotBrassPatch.patched = true
    return true
end

local function tryPatchOnTick()
    if OFHotBrassPatch.patched then
        Events.OnTick.Remove(tryPatchOnTick)
        return
    end

    applyPatch()
end

Events.OnTick.Add(tryPatchOnTick)
