"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MeshClientService = exports.__ΩMeshClientServiceOptions = void 0;
function __assignType(fn, args) {
    fn.__type = args;
    return fn;
}
const logger_1 = require("../logger");
const mesh_1 = require("../mesh");
const mesh_client_redis_registry_1 = require("./mesh-client-redis-registry");
const mesh_client_registry_1 = require("./mesh-client-registry");
const types_1 = require("./types");
const __ΩForwardRequest = ['clientId', 'type', 'data', 'timeoutMs', 'ForwardRequest', 'P&4!&4"#4#\'4$8Mw%y'];
const __ΩForwardResponse = ['data', 'error', 'errorName', 'ForwardResponse', 'P#4!8&4"8&4#8Mw$y'];
const __ΩKickClientRequest = ['clientId', 'KickClientRequest', 'P&4!Mw"y'];
const __ΩKickClientResponse = ['kicked', 'KickClientResponse', 'P)4!Mw"y'];
const __ΩUpdateMetaRequest = ['clientId', 'metadata', 'UpdateMetaRequest', 'P&4!#4"Mw#y'];
const __ΩUpdateMetaResponse = ['updated', 'UpdateMetaResponse', 'P)4!Mw"y'];
const __ΩForwardMessages = [() => __ΩForwardRequest, 'request', () => __ΩForwardResponse, 'response', 'forward', () => __ΩKickClientRequest, () => __ΩKickClientResponse, 'kickClient', () => __ΩUpdateMetaRequest, () => __ΩUpdateMetaResponse, 'updateMeta', 'ForwardMessages', 'PPn!4"n#4$M4%Pn&4"n\'4$M4(Pn)4"n*4$M4+Mw,y'];
const __ΩMeshClientServiceOptions = ['TMeta', 'key', 'MeshServiceOptions', 'meshOptions', 'MeshClientRegistryBackend', 'registryBackend', 'clientId', 'type', 'data', 'timeoutMs', '', 'clientInvokeFn', 'metadata', 'clientUpdateMetaFn', 'MeshClientServiceOptions', 'b!P&4""w#4$8"w%4&8P&2\'&2(#2)\'2*8#`/+4,P&2\'e#!2-)/+4.8Mw/y'];
exports.__ΩMeshClientServiceOptions = __ΩMeshClientServiceOptions;
// --- MeshClientService ---
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
class MeshClientService {
    constructor(options) {
        this.logger = (0, logger_1.createLogger)(this);
        this.nodeCleanedUpCallbacks = [];
        this.clientSupersededCallbacks = [];
        this.running = false;
        this.backend = options.registryBackend ?? (mesh_client_redis_registry_1.MeshClientRedisRegistry.Ω = [['!']], new mesh_client_redis_registry_1.MeshClientRedisRegistry(`_mc:${options.key}`));
        this.clientInvokeFn = options.clientInvokeFn;
        this.clientUpdateMetaFn = options.clientUpdateMetaFn;
        this.mesh = (mesh_1.MeshService.Ω = [[() => __ΩForwardMessages, 'n!'], ['!']], new mesh_1.MeshService(`_mc:${options.key}`, options.meshOptions));
        this.mesh.registerHandler('forward', __assignType(async (req) => {
            try {
                const result = await this.clientInvokeFn(req.clientId, req.type, req.data, req.timeoutMs);
                return { data: result };
            }
            catch (err) {
                return {
                    error: err instanceof Error ? err.message : String(err),
                    errorName: err instanceof Error ? err.name : undefined
                };
            }
        }, [() => __ΩForwardRequest, 'req', () => __ΩForwardResponse, '', 'Pn!2"n#`/$']));
        this.mesh.registerHandler('kickClient', __assignType(async (req) => {
            let kicked = false;
            for (const cb of this.clientSupersededCallbacks) {
                try {
                    await cb(req.clientId);
                    kicked = true;
                }
                catch (err) {
                    this.logger.warn('client superseded callback error', { err, clientId: req.clientId });
                }
            }
            return { kicked };
        }, [() => __ΩKickClientRequest, 'req', () => __ΩKickClientResponse, '', 'Pn!2"n#`/$']));
        this.mesh.registerHandler('updateMeta', __assignType(async (req) => {
            const updated = this.clientUpdateMetaFn?.(req.clientId, req.metadata) ?? false;
            return { updated };
        }, [() => __ΩUpdateMetaRequest, 'req', () => __ΩUpdateMetaResponse, '', 'Pn!2"n#`/$']));
        this.mesh.setNodeCleanedUpCallback(__assignType(async (nodeId) => {
            const orphaned = await this.backend.cleanupNode(nodeId);
            if (orphaned.length > 0) {
                for (const cb of this.nodeCleanedUpCallbacks) {
                    try {
                        await cb(nodeId, orphaned);
                    }
                    catch (err) {
                        this.logger.warn('node cleanup callback error', { err, nodeId });
                    }
                }
            }
        }, ['nodeId', '', 'P\'2!"/"']));
        // Placeholder registry — will be re-created in start() with the real instanceId
        this.registry = (mesh_client_registry_1.MeshClientRegistry.Ω = [['!']], new mesh_client_registry_1.MeshClientRegistry(0, this.backend));
    }
    onNodeClientsOrphaned(cb) {
        this.nodeCleanedUpCallbacks.push(cb);
    }
    onClientSuperseded(cb) {
        this.clientSupersededCallbacks.push(cb);
    }
    get instanceId() {
        return this.mesh.instanceId;
    }
    get clientRegistry() {
        return this.registry;
    }
    async start() {
        await this.mesh.start();
        this.registry = (mesh_client_registry_1.MeshClientRegistry.Ω = [['!']], new mesh_client_registry_1.MeshClientRegistry(this.mesh.instanceId, this.backend));
        this.running = true;
    }
    async stop() {
        this.running = false;
        try {
            // Clean up our own clients
            if (this.mesh.instanceId !== 0) {
                await this.registry.cleanupNode();
            }
        }
        finally {
            await this.mesh.stop();
        }
    }
    /**
     * Register a client on this node. Returns true if registered, false if
     * another node owns the client and `allowSupersede` is false (conflict).
     */
    async registerClient(clientId, metadata, allowSupersede = true) {
        if (!this.running)
            return true;
        const result = await this.registry.register(clientId, metadata, allowSupersede);
        if (result.status === 'conflict') {
            return false;
        }
        if (result.supersededNodeId !== null) {
            this.mesh.invoke(result.supersededNodeId, 'kickClient', { clientId }).catch(__assignType(err => {
                this.logger.warn('failed to kick superseded client', { err, clientId, supersededNodeId: result.supersededNodeId });
            }, ['err', '', 'P"2!"/"']));
        }
        return true;
    }
    /**
     * Reserve ownership of a clientId without exposing it for lookup/invoke
     * until activation completes.
     */
    async reserveClient(clientId, metadata, allowSupersede = true) {
        if (!this.running)
            return true;
        const result = await this.registry.reserve(clientId, metadata, allowSupersede);
        if (result.status === 'conflict') {
            return false;
        }
        if (result.supersededNodeId !== null) {
            this.mesh.invoke(result.supersededNodeId, 'kickClient', { clientId }).catch(__assignType(err => {
                this.logger.warn('failed to kick superseded client', { err, clientId, supersededNodeId: result.supersededNodeId });
            }, ['err', '', 'P"2!"/"']));
        }
        return true;
    }
    /**
     * Promote a same-node reservation to an active, discoverable client.
     */
    async activateClient(clientId, metadata) {
        if (!this.running)
            return false;
        return this.registry.activate(clientId, metadata);
    }
    async unregisterClient(clientId) {
        if (!this.running)
            return false;
        return this.registry.unregister(clientId);
    }
    async updateClientMetadata(clientId, metadata) {
        if (!this.running)
            return false;
        const client = await this.registry.getClient(clientId);
        if (!client)
            return false;
        // Local — apply to stream.meta and update registry directly.
        if (client.nodeId === this.mesh.instanceId) {
            this.clientUpdateMetaFn?.(clientId, metadata);
            return this.registry.updateMetadata(clientId, metadata);
        }
        // Remote — route through mesh to the owning node
        try {
            const response = await this.mesh.invoke(client.nodeId, 'updateMeta', { clientId, metadata });
            return response.updated;
        }
        catch (err) {
            this.logger.warn('cross-pod metadata update failed', { err, clientId, targetNodeId: client.nodeId });
            return false;
        }
    }
    registerBroadcastHandler(type, handler) {
        this.mesh.registerBroadcastHandler(type, handler);
    }
    async broadcast(type, data, options) {
        return this.mesh.broadcast(type, data, options);
    }
    async invoke(clientId, type, data, timeoutMs) {
        if (!this.running) {
            throw new types_1.ClientNotFoundError(clientId);
        }
        const client = await this.registry.getClient(clientId);
        if (!client) {
            throw new types_1.ClientNotFoundError(clientId);
        }
        // Local delivery
        if (client.nodeId === this.mesh.instanceId) {
            return this.clientInvokeFn(clientId, type, data, timeoutMs);
        }
        // Remote delivery via mesh
        const response = await this.mesh.invoke(client.nodeId, 'forward', { clientId, type, data, timeoutMs }, timeoutMs);
        if (response.error !== undefined) {
            if (response.errorName === 'ClientDisconnectedError') {
                throw new types_1.ClientDisconnectedError(clientId);
            }
            throw new types_1.ClientInvocationError(response.error);
        }
        return response.data;
    }
}
exports.MeshClientService = MeshClientService;
MeshClientService.__type = ['TMeta', 'TBroadcasts', () => __ΩForwardMessages, () => mesh_1.MeshService, 'mesh', 'logger', function () { return (0, logger_1.createLogger)(this); }, () => mesh_client_registry_1.MeshClientRegistry, 'registry', 'MeshClientRegistryBackend', 'backend', () => __ΩMeshClientServiceOptions, "clientInvokeFn", 'clientInvokeFn', () => __ΩMeshClientServiceOptions, "clientUpdateMetaFn", 'clientUpdateMetaFn', 'nodeId', 'RegisteredClient', 'orphaned', '', 'nodeCleanedUpCallbacks', function () { return []; }, 'clientId', 'clientSupersededCallbacks', function () { return []; }, () => __ΩMeshClientServiceOptions, 'options', 'constructor', 'cb', 'onNodeClientsOrphaned', 'onClientSuperseded', 'running', function () { return false; }, 'start', 'stop', 'metadata', 'allowSupersede', 'registerClient', 'reserveClient', 'activateClient', 'unregisterClient', 'updateClientMetadata', 'type', 'data', 'senderInstanceId', 'handler', 'registerBroadcastHandler', 'MeshBroadcastOptions', 'broadcast', 'timeoutMs', 'invoke', 'MeshClientService', 'b!PMc"Pn#e""7$3%9!3&;>\'Pe"!7(3);"w*3+;e!!o,".-f3.;e!!o/".0f31;P\'22"w3F24P$$`J/5F36;>7P&28P$$`J/5F39;>:Pe"!o;"2<"0=PP\'22"w3F24P$$`J/52>$0?PP&28P$$`J/52>$0@!!)3A;>BP$`0CP$`0DP&28e"!2E"2F)`0GP&28e"!2E"2F)`0HP&28e"!2E)`0IP&28)`0JP&28e"!2E)`0KP"2LPe#""f2M\'2NP$$`J/52O$0PP"2Le"""f2M"wQ2<8$`0RP&28&2L#2M\'2S8#`0T5wU'];
//# sourceMappingURL=mesh-client-service.js.map