ContainerIngest = {}

-- =============== DEBUG SWITCH ===============
local DEBUG = false
local function dbg(fmt, ...)
    if DEBUG then
        Logging.info(fmt, ...)
    end
end

-- =============== CONFIG RÁPIDA ===============
local LOCAL_FILL_UNIT_INDEX = 1   -- altere se seu container usa outro fillUnit
local FILLBAR_ANIM_NAME     = "fillBarVisibility10"

-- =============== REGISTRO / SCHEMA ===============
function ContainerIngest.prerequisitesPresent(_)
    return true
end

function ContainerIngest.initSpecialization()
    local schema = Vehicle.xmlSchema
    schema:register(XMLValueType.NODE_INDEX,
        "vehicle.containerIngest#triggerNode",
        "Trigger de detecção de pallets", nil)
end

function ContainerIngest.registerFunctions(vehicleType)
    SpecializationUtil.registerFunction(vehicleType, "ci_onTriggerCallback",     ContainerIngest.ci_onTriggerCallback)
    SpecializationUtil.registerFunction(vehicleType, "ci_getSupportedFillTypes", ContainerIngest.ci_getSupportedFillTypes)
    SpecializationUtil.registerFunction(vehicleType, "ci_tryIngestIfFits",       ContainerIngest.ci_tryIngestIfFits)
    SpecializationUtil.registerFunction(vehicleType, "ci_forceFillBarUpdate",    ContainerIngest.ci_forceFillBarUpdate)
end

function ContainerIngest.registerEventListeners(vehicleType)
    SpecializationUtil.registerEventListener(vehicleType, "onLoad",    ContainerIngest)
    SpecializationUtil.registerEventListener(vehicleType, "onDelete",  ContainerIngest)
    SpecializationUtil.registerEventListener(vehicleType, "onUpdate",  ContainerIngest)
end

-- =============== LIFECYCLE ===============
function ContainerIngest:onLoad(_)
    local spec = {}
    self.spec_containerIngest = spec

    spec.triggerNode = self.xmlFile:getValue("vehicle.containerIngest#triggerNode", nil, self.components, self.i3dMappings)
    if spec.triggerNode == nil then
        Logging.error("[%s] containerIngest triggerNode not found (checar i3dMappings/vehicle.containerIngest).", self.configFileName)
        return
    end

    spec.inside         = {}   -- [vehicle] = true
    spec.fillUnitIndex  = LOCAL_FILL_UNIT_INDEX
    spec.animName       = FILLBAR_ANIM_NAME

    addTrigger(spec.triggerNode, "ci_onTriggerCallback", self)
    if self.isServer then
        dbg("[CI] onLoad: trigger OK (%s) fillUnitIndex=%d", tostring(spec.triggerNode), spec.fillUnitIndex)
    end
end

function ContainerIngest:onDelete()
    local spec = self.spec_containerIngest
    if not spec then return end

    if spec.triggerNode then
        removeTrigger(spec.triggerNode)
        spec.triggerNode = nil
    end
    spec.inside = {}
end

-- =============== UPDATE LOOP (robustez MP) ===============
function ContainerIngest:onUpdate(_dt)
    if not self.isServer then return end
    local spec = self.spec_containerIngest
    if not spec or not spec.inside then return end

    local toCheck = {}
    for veh, _ in pairs(spec.inside) do
        table.insert(toCheck, veh)
    end

    for _, pallet in ipairs(toCheck) do
        if pallet == nil or pallet.isDeleted then
            spec.inside[pallet] = nil
        else
            local ftIdx = pallet.getFillUnitFillType and pallet:getFillUnitFillType(1) or nil
            if ftIdx ~= nil then
                self:ci_tryIngestIfFits(pallet, ftIdx)
            end
        end
    end
end

-- =============== UTILS ===============
function ContainerIngest:ci_getSupportedFillTypes()
    local fuIndex = (self.spec_containerIngest and self.spec_containerIngest.fillUnitIndex) or 1
    local fu = self.spec_fillUnit and self.spec_fillUnit.fillUnits and self.spec_fillUnit.fillUnits[fuIndex]
    local list = {}
    if fu and fu.supportedFillTypes then
        for ftIdx, enabled in pairs(fu.supportedFillTypes) do
            if enabled then
                table.insert(list, g_fillTypeManager:getFillTypeNameByIndex(ftIdx) or tostring(ftIdx))
            end
        end
    end
    table.sort(list)
    return list
end

-- força a animação de barra a refletir o nível atual imediatamente (lado local)
function ContainerIngest:ci_forceFillBarUpdate()
    local spec = self.spec_containerIngest
    local fuIndex = spec.fillUnitIndex or 1
    local anim    = spec.animName or "fillBarVisibility10"

    local level    = self:getFillUnitFillLevel(fuIndex) or 0
    local capacity = self:getFillUnitCapacity(fuIndex) or 1
    local ratio    = math.max(0, math.min(1, level / math.max(1, capacity)))

    if self.setAnimationTime ~= nil then
        self:setAnimationTime(anim, ratio, true)
    end
    if self.playAnimation ~= nil and self.stopAnimation ~= nil then
        self:playAnimation(anim, 0.0001, ratio, true)
        self:stopAnimation(anim, true)
    end
end

-- =============== INGEST (server) ===============
function ContainerIngest:ci_tryIngestIfFits(pallet, palletFtIdx)
    if not self.isServer then return false end

    local spec = self.spec_containerIngest
    local dstIndex = spec.fillUnitIndex or 1
    local srcIndex = 1

    if pallet == nil or pallet.getFillUnitFillLevel == nil then
        return false
    end

    local palletLevel = pallet:getFillUnitFillLevel(srcIndex) or 0
    if palletLevel <= 0 then
        return false
    end

    local fuSpec = self.spec_fillUnit
    local fu = fuSpec and fuSpec.fillUnits and fuSpec.fillUnits[dstIndex]
    if not fu then
        dbg("[CI] abort: fillUnit %d inexistente", dstIndex)
        return false
    end

    -- compatibilidade
    local supportsNow = true
    if FillUnit and FillUnit.getFillUnitSupportsFillType then
        supportsNow = FillUnit.getFillUnitSupportsFillType(self, dstIndex, palletFtIdx)
    end
    if not supportsNow then
        local ftName = g_fillTypeManager and g_fillTypeManager:getFillTypeNameByIndex(palletFtIdx) or tostring(palletFtIdx)
        dbg("[CI] abort: fillType %s não suportado pelo FU %d", ftName, dstIndex)
        return false
    end

    -- assinatura correta: incluir farmId
    local farmId = self.getOwnerFarmId and self:getOwnerFarmId() or nil

    -- garante o tipo
    self:setFillUnitFillType(dstIndex, palletFtIdx)

    local before = self:getFillUnitFillLevel(dstIndex) or 0
    local free   = self:getFillUnitFreeCapacity(dstIndex, palletFtIdx) or 0
    if free <= 0 or palletLevel > free then
        return false
    end

    -- aplica com evento (replica p/ clientes)
    self:addFillUnitFillLevel(farmId, dstIndex, palletLevel, palletFtIdx, ToolType.UNDEFINED, nil)

    local after = self:getFillUnitFillLevel(dstIndex) or 0
    if after <= before + 1e-6 then
        -- fallback local (raro)
        fu.fillType  = palletFtIdx
        fu.fillLevel = math.min((self:getFillUnitCapacity(dstIndex) or 0), (fu.fillLevel or 0) + palletLevel)
        if self.onFillUnitFillLevelChanged ~= nil then
            pcall(function()
                self:onFillUnitFillLevelChanged(dstIndex, palletLevel, palletFtIdx, ToolType.UNDEFINED, nil, palletLevel)
            end)
        end
        if fuSpec and fuSpec.dirtyFlag then
            self:raiseDirtyFlags(fuSpec.dirtyFlag)
        end
        after = fu.fillLevel or before

        -- nudge de rede pra garantir sync
        local eps = math.min(0.001, math.max(1e-6, self:getFillUnitFreeCapacity(dstIndex, palletFtIdx) or 0))
        if eps > 0 then
            self:addFillUnitFillLevel(farmId, dstIndex,  eps, palletFtIdx, ToolType.UNDEFINED, nil)
            self:addFillUnitFillLevel(farmId, dstIndex, -eps, palletFtIdx, ToolType.UNDEFINED, nil)
        end
    end

    if after > before + 1e-6 then
        -- zera/remover pallet
        local pfSpec = pallet.spec_fillUnit
        local pf = pfSpec and pfSpec.fillUnits and pfSpec.fillUnits[srcIndex]
        if pf ~= nil then
            pf.fillType  = palletFtIdx
            pf.fillLevel = math.max(0, (pf.fillLevel or palletLevel) - palletLevel)
            if pallet.onFillUnitFillLevelChanged ~= nil then
                pcall(function()
                    pallet:onFillUnitFillLevelChanged(srcIndex, -palletLevel, palletFtIdx, ToolType.UNDEFINED, nil, -palletLevel)
                end)
            end
            if pfSpec and pfSpec.dirtyFlag then
                pallet:raiseDirtyFlags(pfSpec.dirtyFlag)
            end
        end

        if pallet.delete ~= nil then
            pallet:delete()
        elseif g_currentMission and g_currentMission.removeVehicle then
            g_currentMission:removeVehicle(pallet)
        end

        self:ci_forceFillBarUpdate()
        return true
    end

    return false
end

-- =============== TRIGGER (server) ===============
function ContainerIngest:ci_onTriggerCallback(_, otherId, onEnter, onLeave, _, _)
    if not self.isServer then return end

    local other = g_currentMission.nodeToObject[otherId]
    if other == nil or other == self then return end

    local spec = self.spec_containerIngest
    local wasInside = spec.inside[other] == true

    if onEnter and not wasInside then
        spec.inside[other] = true
        local ftIdx = other.getFillUnitFillType and other:getFillUnitFillType(1) or -1
        local ftName = g_fillTypeManager and g_fillTypeManager:getFillTypeNameByIndex(ftIdx) or tostring(ftIdx)
        dbg("[CI] onEnter: pallet=%s (ft=%s)", tostring(other), ftName)
    elseif onLeave and wasInside then
        spec.inside[other] = nil
        dbg("[CI] onLeave: pallet=%s", tostring(other))
    end
end
