MudSystemVehicleLifecycle = MudSystemVehicleLifecycle or {}

local function lifecycleCall(name, callback)
    local ok, err = pcall(callback)
    if not ok then
        Logging.warning(
            "[MudSystemPhysics] Vehicle delete cleanup '%s' failed: %s",
            tostring(name),
            tostring(err)
        )
    end
end

function MudSystemVehicleLifecycle:prepareVehicleDelete(vehicle)
    if vehicle == nil or vehicle.__mudSystemDeleting == true then
        return
    end

    -- This must be the first Mud System flag set during Vehicle.delete. Several
    -- engine lists retain the Lua vehicle for the remainder of the frame even
    -- though its native wheel/component entities are already being destroyed.
    vehicle.__mudSystemDeleting = true
    print(string.format(
        "[MudSystemPhysics] vehicle delete begin: %s",
        tostring(vehicle.configFileName or vehicle.typeName or vehicle)
    ))

    lifecycleCall("wheel mud visuals", function()
        if MudWheelVisualSystem ~= nil
            and MudWheelVisualSystem.clearVehicle ~= nil then
            MudWheelVisualSystem:clearVehicle(vehicle)
        end
    end)

    lifecycleCall("mud particles", function()
        if MudPhysics ~= nil and MudPhysics.cleanupVehicleState ~= nil then
            MudPhysics:cleanupVehicleState(vehicle, "Vehicle.delete")
        end
    end)

    lifecycleCall("field mud particles", function()
        if FieldGroundMudPhysics ~= nil
            and FieldGroundMudPhysics.cleanupVehicleState ~= nil then
            FieldGroundMudPhysics:cleanupVehicleState(vehicle, "Vehicle.delete")
        end
    end)

    lifecycleCall("TTD bridge", function()
        if TractorTerrainDynamicsCompatibility ~= nil
            and TractorTerrainDynamicsCompatibility.releaseVehicle ~= nil then
            TractorTerrainDynamicsCompatibility:releaseVehicle(vehicle)
        end

        -- TTD's own onDelete calls applySlipPhysics(), which writes wheel
        -- physics while the vehicle is being dismantled. Suppress that final
        -- update; the object is being deleted and does not need restoration.
        if vehicle.spec_tractorTerrainDynamics ~= nil
            and type(vehicle.applySlipPhysics) == "function" then
            vehicle.__mudSystemOriginalApplySlipPhysics = vehicle.applySlipPhysics
            vehicle.applySlipPhysics = function() end
        end
    end)

    lifecycleCall("punctures", function()
        if PunctureSystem ~= nil and PunctureSystem.clearVehicleLocal ~= nil then
            PunctureSystem:clearVehicleLocal(vehicle)
        end
    end)

    lifecycleCall("tire pressure", function()
        if TirePressureSystem ~= nil and TirePressureSystem.clearVehicle ~= nil then
            TirePressureSystem:clearVehicle(vehicle, true)
        end
    end)

    lifecycleCall("tire wear bridge", function()
        if UseYourTyresCompatibility ~= nil
            and UseYourTyresCompatibility.releaseVehicle ~= nil then
            UseYourTyresCompatibility:releaseVehicle(vehicle)
        end
    end)

    lifecycleCall("wheel load", function()
        if WheelLoadSystem ~= nil and WheelLoadSystem.clearVehicle ~= nil then
            WheelLoadSystem:clearVehicle(vehicle)
        end
    end)

    lifecycleCall("drive coordinator", function()
        local coordinator = _G.__MudDriveCoordinator
        if coordinator ~= nil and coordinator.forgetVehicle ~= nil then
            coordinator:forgetVehicle(vehicle)
        end
    end)

    print("[MudSystemPhysics] vehicle delete cleanup complete")
end

function MudSystemVehicleLifecycle:installDeleteHook()
    if Vehicle == nil or Vehicle.delete == nil
        or Utils == nil or Utils.prependedFunction == nil then
        return
    end

    if Vehicle.delete == self._deleteWrapper then
        return
    end

    local lifecycle = self
    self._deleteWrapper = Utils.prependedFunction(
        Vehicle.delete,
        function(vehicle)
            lifecycle:prepareVehicleDelete(vehicle)
        end
    )
    Vehicle.delete = self._deleteWrapper
    print("[MudSystemPhysics] vehicle deletion lifecycle guard installed")
end

function MudSystemVehicleLifecycle:loadMap()
    -- Loaded last so this wrapper stays outside the MudPhysics,
    -- FieldGroundMudPhysics and puncture delete wrappers.
    self:installDeleteHook()
end

addModEventListener(MudSystemVehicleLifecycle)

-- Vehicle types copy Vehicle.delete while types are validated. Install once
-- during script loading as well as at loadMap, otherwise a late wrapper only
-- changes the class function and never runs for already registered types.
MudSystemVehicleLifecycle:installDeleteHook()
