import { debuglog } from 'node:util';
import { MultiplexedProcessor, createMultiplexServer } from 'thrift';
import { isObject, getServiceName, getProcessor } from './utils.js';
import { DEFAULT_TRANSPORT, DEFAULT_PROTOCOL } from './options.js';
/**
 * 生成用户 Handler 的包装器
 */
function wrapHandler(handler, processor, server, logger) {
    const wrap = {};
    let currentObj = processor.prototype;
    do {
        // 在原型链上查找 process_* 方法
        for (const processorKey of Object.getOwnPropertyNames(currentObj)) {
            if (!processorKey.startsWith('process_'))
                continue;
            const key = processorKey.slice('process_'.length);
            const wrapper = async function (...args) {
                if (typeof args.at(-1) === 'function') {
                    const callback = args.pop();
                    try {
                        logger('calling method %s', key);
                        server._beforeCall();
                        const ret = await this[key](...args);
                        callback(null, ret);
                        logger('method %s called', key);
                    }
                    catch (ex) {
                        logger('method %s error: %s', key, ex);
                        callback(ex);
                    }
                    finally {
                        server._afterCall();
                    }
                }
                else {
                    // one-way methods
                    try {
                        logger('calling one-way method %s', key);
                        await this[key](...args);
                        logger('one-way method %s called', key);
                    }
                    catch (ex) {
                        logger('one-way method %s error: %s', key, ex);
                        server.emit('error', ex);
                    }
                }
            }.bind(handler);
            // 设置长度以强制使用回调版本重载
            Object.defineProperty(wrapper, 'length', { value: -1 });
            wrap[key] = wrapper;
            logger('registered method %s', key);
        }
    } while ((currentObj = Object.getPrototypeOf(currentObj)));
    return wrap;
}
/** 创建服务端 */
export function createServer(options) {
    let logger = debuglog(`cloudpss/rpc-server`, (l) => (logger = l));
    const { ...opt } = options ?? {};
    opt.transport ??= DEFAULT_TRANSPORT;
    opt.protocol ??= DEFAULT_PROTOCOL;
    const multiplex = new MultiplexedProcessor();
    const server = createMultiplexServer(multiplex, opt);
    const connections = new Map();
    server.on('connection', (socket) => {
        const id = `${socket.remoteAddress}:${socket.remotePort}`;
        connections.set(id, socket);
        logger('[%s] connected', id);
        socket.on('error', (ex) => {
            logger('[%s] error: %s', id, ex);
        });
        socket.on('close', () => {
            connections.delete(id);
            logger('[%s] closed', id);
        });
    });
    server.on('error', (ex) => {
        logger('error: %s', ex);
    });
    server.on('listening', () => {
        logger('listening on %o', server.address());
    });
    server.on('close', () => {
        logger('closed');
    });
    const closeConnections = () => {
        logger('closing all connections');
        for (const socket of connections.values()) {
            socket.end();
        }
    };
    // eslint-disable-next-line @typescript-eslint/unbound-method
    const _close = server.close;
    server.close = function close(...args) {
        logger('close called, %d pending rpc calls', this._pendingCalls);
        if (this._closing) {
            Reflect.apply(_close, this, args);
            return this;
        }
        this._closing = true;
        Reflect.apply(_close, this, args);
        if (this._pendingCalls === 0) {
            closeConnections();
        }
        return this;
    };
    Object.defineProperty(server, '_processor', { value: multiplex });
    Object.defineProperty(server, '_pendingCalls', { value: 0, writable: true });
    Object.defineProperty(server, '_closing', { value: false, writable: true });
    Object.defineProperty(server, '_beforeCall', {
        value: function () {
            if (this._closing) {
                throw new Error('Server is closing');
            }
            this._pendingCalls++;
        },
    });
    Object.defineProperty(server, '_afterCall', {
        value: function () {
            this._pendingCalls--;
            if (this._closing && this._pendingCalls === 0) {
                closeConnections();
            }
        },
    });
    Object.defineProperty(server, 'route', {
        value: function route(name, service, handler) {
            if (!name)
                throw new TypeError(`Invalid empty name/service`);
            let _name, _service, _handler;
            if (typeof name == 'string') {
                // OK
                _name = name;
                _service = service;
                if (!handler)
                    throw new TypeError(`Invalid empty handler`);
                _handler = handler;
            }
            else if (isObject(name)) {
                // 第二种签名
                _name = getServiceName(name);
                _service = name;
                if (!service)
                    throw new TypeError(`Invalid empty handler`);
                _handler = service;
            }
            else {
                throw new TypeError(`Invalid name ${String(name)}, string expected`);
            }
            if (_name in multiplex.services) {
                throw new Error(`Service with name "${_name}" already exists`);
            }
            const processor = getProcessor(_service);
            this._processor.registerProcessor(_name, new processor(wrapHandler(_handler, processor, this, logger)));
            return this;
        },
    });
    return server;
}
//# sourceMappingURL=server.js.map