/**
 * Client-side RPC — replaces RageMP's mp.events.callRemoteProc / addProc.
 *
 * Call a server proc (registered with rpc.register on the server):
 *   const result = await rpc.call('getClosestTrunk', ...args);
 *
 * Register a client proc (called from server via rpc.callClient):
 *   rpc.registerClientProc('getVehicleClass', (vehicle) => { ... });
 */

let callCounter = 0;
const pending = new Map<string, { resolve: (v: any) => void; reject: (e: any) => void }>();

// Receive results from the server
RegisterNetEvent('_rpc:result');
AddEventHandler('_rpc:result', (callId: string, result: any, error: string | null) => {
    const cb = pending.get(callId);
    if (!cb) return;
    pending.delete(callId);
    if (error) cb.reject(new Error(error));
    else cb.resolve(result);
});

// Server → client proc invocations
RegisterNetEvent('_rpc:clientCall');
AddEventHandler('_rpc:clientCall', (callId: string, name: string, args: any[]) => {
    const handler = clientProcs.get(name);
    if (!handler) {
        TriggerServerEvent('_rpc:clientResult', callId, null, `No client proc: ${name}`);
        return;
    }
    Promise.resolve()
        .then(() => handler(...(args ?? [])))
        .then((result) => TriggerServerEvent('_rpc:clientResult', callId, result, null))
        .catch((err) => TriggerServerEvent('_rpc:clientResult', callId, null, err?.message ?? String(err)));
});

const clientProcs = new Map<string, (...args: any[]) => any>();

export const rpc = {
    call<T = any>(name: string, ...args: any[]): Promise<T> {
        const id = `${Date.now()}_${callCounter++}`;
        return new Promise<T>((resolve, reject) => {
            pending.set(id, { resolve, reject });
            TriggerServerEvent('_rpc:call', id, name, args);
            // Timeout after 10s
            setTimeout(() => {
                if (pending.has(id)) {
                    pending.delete(id);
                    reject(new Error(`RPC timeout: ${name}`));
                }
            }, 10000);
        });
    },

    registerClientProc(name: string, handler: (...args: any[]) => any): void {
        clientProcs.set(name, handler);
    },
};
