if isServer() then return end

if not TowBarMod then TowBarMod = {} end
TowBarMod.Sync = TowBarMod.Sync or {}
if TowBarMod.Sync._towSyncClientLoaded then return end
TowBarMod.Sync._towSyncClientLoaded = true

local function resolveVehicle(id)
    if not id then return nil end
    return getVehicleById(id)
end

local function ensureAttachment(vehicle, attachmentId)
    if not vehicle or not attachmentId then return false end

    local script = vehicle:getScript()
    if not script then return false end
    if script:getAttachmentById(attachmentId) ~= nil then return true end

    local wheelCount = script:getWheelCount()
    local yOffset = -0.5
    if wheelCount > 0 then
        local wheel = script:getWheel(0)
        if wheel and wheel:getOffset() then
            yOffset = wheel:getOffset():y() + 0.1
        end
    end

    local chassis = script:getPhysicsChassisShape()
    if not chassis then return false end

    local attach = ModelAttachment.new(attachmentId)
    if attachmentId == "trailer" then
        attach:getOffset():set(0, yOffset, -chassis:z() / 2 - 0.1)
        attach:setZOffset(-1)
    else
        attach:getOffset():set(0, yOffset, chassis:z() / 2 + 0.1)
        attach:setZOffset(1)
    end
    script:addAttachment(attach)
    return true
end

local function isLinked(vehicleA, vehicleB)
    if not vehicleA or not vehicleB then return false end
    return vehicleA:getVehicleTowing() == vehicleB and vehicleB:getVehicleTowedBy() == vehicleA
end

local function reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB)
    if TowBarMod.Utils and TowBarMod.Utils.updateAttachmentsForRigidTow then
        TowBarMod.Utils.updateAttachmentsForRigidTow(vehicleA, vehicleB, attachmentA, attachmentB)
    end

    local towingMd = vehicleA:getModData()
    local towedMd = vehicleB:getModData()
    local currentScript = vehicleB:getScriptName()

    if towingMd then
        towingMd["isTowingByTowBar"] = true
        vehicleA:transmitModData()
    end
    if towedMd then
        if towedMd.towBarOriginalScriptName == nil and currentScript ~= "notTowingA_Trailer" then
            towedMd.towBarOriginalScriptName = currentScript
        end
        if towedMd.towBarOriginalMass == nil then
            towedMd.towBarOriginalMass = vehicleB:getMass()
        end
        if towedMd.towBarOriginalBrakingForce == nil then
            towedMd.towBarOriginalBrakingForce = vehicleB:getBrakingForce()
        end
        towedMd["isTowingByTowBar"] = true
        towedMd["towed"] = true
        vehicleB:transmitModData()
    end

    vehicleB:setScriptName("notTowingA_Trailer")
    if TowBarMod.Hook and TowBarMod.Hook.setVehiclePostAttach then
        pcall(TowBarMod.Hook.setVehiclePostAttach, nil, vehicleB)
    end
end

local function applyAttachSync(args)
    if not args then return end

    local vehicleA = resolveVehicle(args.vehicleA)
    local vehicleB = resolveVehicle(args.vehicleB)
    if not vehicleA or not vehicleB then return end

    local attachmentA = args.attachmentA or "trailer"
    local attachmentB = args.attachmentB or "trailerfront"
    if not ensureAttachment(vehicleA, attachmentA) or not ensureAttachment(vehicleB, attachmentB) then
        return
    end

    if not isLinked(vehicleA, vehicleB) then
        vehicleA:addPointConstraint(nil, vehicleB, attachmentA, attachmentB)
    end

    reconcilePairState(vehicleA, vehicleB, attachmentA, attachmentB)
end

local function safeBreak(vehicle)
    if not vehicle then return end
    if vehicle:getVehicleTowing() == nil and vehicle:getVehicleTowedBy() == nil then return end
    vehicle:breakConstraint(true, true)
end

local function applyDetachSync(args)
    if not args then return end
    safeBreak(resolveVehicle(args.vehicleA))
    safeBreak(resolveVehicle(args.vehicleB))
end

local function onServerCommand(module, command, args)
    if module ~= "towbar" then return end

    if command == "forceAttachSync" then
        applyAttachSync(args)
    elseif command == "forceDetachSync" then
        applyDetachSync(args)
    end
end

Events.OnServerCommand.Add(onServerCommand)
