import EventEmitter from "node:events";
import { privateKeyToProtobuf } from "@libp2p/crypto/keys";
import { ENR, SignableENR } from "@chainsafe/enr";
import { Thread, Worker, spawn } from "@chainsafe/threads";
import { chainConfigFromJson, chainConfigToJson } from "@lodestar/config";
/**
 * Wrapper class abstracting the details of discv5 worker instantiation and message-passing
 */
export class Discv5Worker extends EventEmitter {
    opts;
    workerApi;
    subscription;
    closed = false;
    constructor(opts, workerApi) {
        super();
        this.opts = opts;
        this.workerApi = workerApi;
        this.subscription = workerApi.discovered().subscribe((enrObj) => this.onDiscovered(enrObj));
    }
    static async init(opts) {
        const workerData = {
            enr: opts.discv5.enr,
            privateKeyProto: privateKeyToProtobuf(opts.privateKey),
            bindAddrs: opts.discv5.bindAddrs,
            config: opts.discv5.config ?? {},
            bootEnrs: opts.discv5.bootEnrs,
            metrics: Boolean(opts.metrics),
            chainConfig: chainConfigFromJson(chainConfigToJson(opts.config)),
            genesisValidatorsRoot: opts.config.genesisValidatorsRoot,
            loggerOpts: opts.logger.toOpts(),
            genesisTime: opts.genesisTime,
        };
        const worker = new Worker("./worker.js", {
            suppressTranspileTS: Boolean(globalThis.Bun),
            workerData,
        });
        const workerApi = await spawn(worker, {
            // A Lodestar Node may do very expensive task at start blocking the event loop and causing
            // the initialization to timeout. The number below is big enough to almost disable the timeout
            timeout: 5 * 60 * 1000,
        });
        return new Discv5Worker(opts, workerApi);
    }
    async close() {
        if (this.closed)
            return;
        this.closed = true;
        this.subscription.unsubscribe();
        await this.workerApi.close();
        await Thread.terminate(this.workerApi);
    }
    onDiscovered(obj) {
        const enr = this.decodeEnr(obj);
        if (enr) {
            this.emit("discovered", enr);
        }
    }
    async enr() {
        const obj = await this.workerApi.enr();
        return new SignableENR(obj.kvs, obj.seq, this.opts.privateKey.raw);
    }
    setEnrValue(key, value) {
        return this.workerApi.setEnrValue(key, value);
    }
    async kadValues() {
        return this.decodeEnrs(await this.workerApi.kadValues());
    }
    discoverKadValues() {
        return this.workerApi.discoverKadValues();
    }
    async findRandomNode() {
        return this.decodeEnrs(await this.workerApi.findRandomNode());
    }
    scrapeMetrics() {
        return this.workerApi.scrapeMetrics();
    }
    async writeProfile(durationMs, dirpath) {
        return this.workerApi.writeProfile(durationMs, dirpath);
    }
    async writeHeapSnapshot(prefix, dirpath) {
        return this.workerApi.writeHeapSnapshot(prefix, dirpath);
    }
    decodeEnrs(objs) {
        const enrs = [];
        for (const obj of objs) {
            const enr = this.decodeEnr(obj);
            if (enr) {
                enrs.push(enr);
            }
        }
        return enrs;
    }
    decodeEnr(obj) {
        this.opts.metrics?.discv5.decodeEnrAttemptCount.inc(1);
        return new ENR(obj.kvs, obj.seq, obj.signature);
    }
}
//# sourceMappingURL=index.js.map