local Callback = {}
local CallbackRegistry = {}

-- Constants
local RESOURCE = GetCurrentResourceName() or 'unknown'
local EVENT_NAMES = {
    CLIENT_TO_SERVER = RESOURCE .. ':CS:Callback',
    SERVER_TO_CLIENT = RESOURCE .. ':SC:Callback',
    CLIENT_RESPONSE = RESOURCE .. ':CSR:Callback',
    SERVER_RESPONSE = RESOURCE .. ':SCR:Callback'
}

-- Utility functions
local function generateCallbackId(name)
    return string.format('%s_%d', name, math.random(1000000, 9999999))
end

local function handleResponse(registry, name, callbackId, ...)
    local data = registry[callbackId]
    if not data then return end

    if data.callback then
        data.callback(...)
    end

    if data.promise then
        data.promise:resolve({ ... })
    end

    registry[callbackId] = nil
end

local function triggerCallback(eventName, target, name, args, callback)
    local callbackId = generateCallbackId(name)
    local promise = promise.new()

    CallbackRegistry[callbackId] = {
        callback = callback,
        promise = promise
    }

    if type(target) == 'table' then
        for _, id in ipairs(target) do
            TriggerClientEvent(eventName, tonumber(id), name, callbackId, table.unpack(args))
        end
    else
        TriggerClientEvent(eventName, tonumber(target), name, callbackId, table.unpack(args))
    end

    if not callback then
        local result = Citizen.Await(promise)
        local returnResults = (result and type(result) == 'table' ) and result or {result}
        return table.unpack(returnResults)
    end
end

-- Server-side implementation
if IsDuplicityVersion() then
    function Callback.Register(name, handler)
        Callback[name] = handler
    end

    function Callback.Trigger(name, target, ...)
        local args = { ... }
        local callback = type(args[1]) == 'function' and table.remove(args, 1) or nil
        return triggerCallback(EVENT_NAMES.SERVER_TO_CLIENT, target or -1, name, args, callback)
    end

    RegisterNetEvent(EVENT_NAMES.CLIENT_TO_SERVER, function(name, callbackId, ...)
        if not name or not callbackId then return print(string.format("[%s] Warning: Invalid callback parameters - name: %s, callbackId: %s", RESOURCE, tostring(name), tostring(callbackId))) end

        local handler = Callback[name]
        if not handler then return end

        local playerId = source
        if not playerId or playerId == 0 then return print(string.format("[%s] Warning: Invalid source for callback '%s'", RESOURCE, name)) end

        local result = table.pack(handler(playerId, ...))
        TriggerClientEvent(EVENT_NAMES.CLIENT_RESPONSE, playerId, name, callbackId, table.unpack(result))
    end)

    RegisterNetEvent(EVENT_NAMES.SERVER_RESPONSE, function(name, callbackId, ...)
        handleResponse(CallbackRegistry, name, callbackId, ...)
    end)

    -- Client-side implementation
else
    local ClientCallbacks = {}
    local ReboundCallbacks = {}

    function Callback.Register(name, handler)
        ClientCallbacks[name] = handler
    end

    function Callback.RegisterRebound(name, handler)
        ReboundCallbacks[name] = handler
    end

    function Callback.Trigger(name, ...)
        local args = { ... }
        local callback = type(args[1]) == 'function' and table.remove(args, 1) or nil

        local callbackId = generateCallbackId(name)
        local promise = promise.new()

        CallbackRegistry[callbackId] = {
            callback = callback,
            promise = promise
        }

        TriggerServerEvent(EVENT_NAMES.CLIENT_TO_SERVER, name, callbackId, table.unpack(args))

        if not callback then
            local result = Citizen.Await(promise)
            return table.unpack(result)
        end
    end

    RegisterNetEvent(EVENT_NAMES.CLIENT_RESPONSE, function(name, callbackId, ...)
        if ReboundCallbacks[name] then
            ReboundCallbacks[name](...)
        end
        handleResponse(CallbackRegistry, name, callbackId, ...)
    end)

    RegisterNetEvent(EVENT_NAMES.SERVER_TO_CLIENT, function(name, callbackId, ...)
        local handler = ClientCallbacks[name]
        if not handler then return end

        local result = table.pack(handler(...))
        TriggerServerEvent(EVENT_NAMES.SERVER_RESPONSE, name, callbackId, table.unpack(result))
    end)
end

-- Exports
exports('Callback', Callback)
exports('RegisterCallback', Callback.Register)
exports('TriggerCallback', Callback.Trigger)
if not IsDuplicityVersion() then
    exports('RegisterRebound', Callback.RegisterRebound)
end

return Callback
