var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _SupervisorActor_instances, _SupervisorActor_hostname, _SupervisorActor_runtimeClient, _SupervisorActor_maximumActors, _SupervisorActor_actorCount, _SupervisorActor_lruRoots, _SupervisorActor_dependencyGraph, _SupervisorActor_filter, _SupervisorActor_enforceActorLimit;
import { RuntimeActorDefinition, WorkerQuery, } from '@devvit/protos/types/devvit/runtime/actor/runtime.js';
import { Empty } from '@devvit/protos/types/google/protobuf/empty.js';
import { Hostname, isDescendantHostname, RUNTIME_CNAME, siblingHostname, } from '@devvit/shared-types/HostnameUtil.js';
import { NonNull } from '@devvit/shared-types/NonNull.js';
import { getUniqueNodes } from '../../lib/BundleUtil.js';
import { DependencyGraph } from '../../lib/DependencyGraph.js';
import { Log } from '../../lib/loggers/Log.js';
import { SystemLogTag } from '../../lib/loggers/SystemLogger.js';
import { EnvelopeClientBuilder } from '../envelope/EnvelopeClientBuilder.js';
import { MetadataEnvelopeFilter } from '../envelope/filter/MetadataEnvelopeFilter.js';
import { RepeatEnvelopeFilter } from '../envelope/filter/RepeatEnvelopeFilter.js';
import { SequenceEnvelopeFilter } from '../envelope/filter/SequenceEnvelopeFilter.js';
export function DefaultFilters(namespace) {
    const filters = [
        new MetadataEnvelopeFilter(namespace),
        new RepeatEnvelopeFilter(2),
    ];
    return new SequenceEnvelopeFilter(filters);
}
function containsCode(bundle) {
    return bundle.code.length > 0;
}
/**
 * The Supervisor is the first worker created that manages creating additional Actors
 */
export class SupervisorActor {
    constructor(dispatcher, filter = DefaultFilters({
        hostname: Hostname.getRoot(NonNull(dispatcher.hostname)),
    })) {
        _SupervisorActor_instances.add(this);
        _SupervisorActor_hostname.set(this, void 0);
        _SupervisorActor_runtimeClient.set(this, void 0);
        _SupervisorActor_maximumActors.set(this, void 0);
        _SupervisorActor_actorCount.set(this, void 0);
        _SupervisorActor_lruRoots.set(this, void 0);
        _SupervisorActor_dependencyGraph.set(this, void 0);
        _SupervisorActor_filter.set(this, void 0);
        __classPrivateFieldSet(this, _SupervisorActor_maximumActors, 100, "f");
        __classPrivateFieldSet(this, _SupervisorActor_actorCount, 0, "f");
        __classPrivateFieldSet(this, _SupervisorActor_lruRoots, new Set(), "f");
        __classPrivateFieldSet(this, _SupervisorActor_hostname, NonNull(dispatcher.hostname), "f");
        __classPrivateFieldSet(this, _SupervisorActor_dependencyGraph, new DependencyGraph((linkedBundle) => linkedBundle.hostname, (linkedBundle) => linkedBundle.uses.filter(containsCode)), "f");
        __classPrivateFieldSet(this, _SupervisorActor_filter, filter, "f");
        __classPrivateFieldSet(this, _SupervisorActor_runtimeClient, EnvelopeClientBuilder.build(siblingHostname(RUNTIME_CNAME, dispatcher), [
            RuntimeActorDefinition,
        ]), "f");
        dispatcher.bind(__classPrivateFieldGet(this, _SupervisorActor_runtimeClient, "f"));
    }
    Filter(envelope, _metadata) {
        if (isDescendantHostname(envelope.src, __classPrivateFieldGet(this, _SupervisorActor_hostname, "f")))
            throw Error('The supervisor cannot filter envelopes from itself.');
        // If request is to a root, move it to the end of our LRU cache
        if (__classPrivateFieldGet(this, _SupervisorActor_lruRoots, "f").has(envelope.dst)) {
            __classPrivateFieldGet(this, _SupervisorActor_lruRoots, "f").delete(envelope.dst);
            __classPrivateFieldGet(this, _SupervisorActor_lruRoots, "f").add(envelope.dst);
        }
        // This filter call may throw (reject). It's correct to allow the
        // CentralDispatcher to catch and wrap the error in a new Envelope.
        return __classPrivateFieldGet(this, _SupervisorActor_filter, "f").filter(envelope);
    }
    /**
     * Communicates with the Runtime to unpack a LinkedBundle and spawn Workers
     */
    // RPC
    async Load(bundle, metadata) {
        await Log.verbose(`Loading ${bundle.hostname} v${bundle.actor?.version ?? '?.?.?'}.`, [SystemLogTag.ScriptLifecycle], metadata);
        // Unload oldest roots until there's enough space for this app
        const toLoad = await __classPrivateFieldGet(this, _SupervisorActor_instances, "m", _SupervisorActor_enforceActorLimit).call(this, bundle, metadata);
        // We should now have enough space to load the current bundle
        __classPrivateFieldSet(this, _SupervisorActor_actorCount, __classPrivateFieldGet(this, _SupervisorActor_actorCount, "f") + toLoad.size, "f");
        __classPrivateFieldGet(this, _SupervisorActor_lruRoots, "f").add(bundle.hostname);
        try {
            await Promise.all([...toLoad].map((linkedBundle) => {
                return __classPrivateFieldGet(this, _SupervisorActor_runtimeClient, "f").NewWorker(linkedBundle, metadata);
            }));
        }
        catch (err) {
            await this.Unload({ force: true, hostname: bundle.hostname }, metadata);
            throw err;
        }
        return Empty.fromPartial({});
    }
    /**
     * Unregisters an actor. This also unloads all actors that have it as a
     * downstream dependency and actors that it depends on if they would be
     * otherwise un-used.
     */
    // RPC
    async Unload(request, metadata) {
        const roots = __classPrivateFieldGet(this, _SupervisorActor_dependencyGraph, "f").findRoots(request.hostname);
        const toUnload = __classPrivateFieldGet(this, _SupervisorActor_dependencyGraph, "f").deleteRoots(roots);
        await Promise.all([...toUnload].map((name) => {
            __classPrivateFieldGet(this, _SupervisorActor_lruRoots, "f").delete(name);
            if (request.force) {
                return __classPrivateFieldGet(this, _SupervisorActor_runtimeClient, "f").TerminateWorker(WorkerQuery.fromPartial({ hostname: name }), metadata);
            }
            else {
                return Promise.resolve();
            }
        }));
        __classPrivateFieldSet(this, _SupervisorActor_actorCount, __classPrivateFieldGet(this, _SupervisorActor_actorCount, "f") - toUnload.size, "f");
        return Empty.fromPartial({});
    }
}
_SupervisorActor_hostname = new WeakMap(), _SupervisorActor_runtimeClient = new WeakMap(), _SupervisorActor_maximumActors = new WeakMap(), _SupervisorActor_actorCount = new WeakMap(), _SupervisorActor_lruRoots = new WeakMap(), _SupervisorActor_dependencyGraph = new WeakMap(), _SupervisorActor_filter = new WeakMap(), _SupervisorActor_instances = new WeakSet(), _SupervisorActor_enforceActorLimit = async function _SupervisorActor_enforceActorLimit(bundle, metadata) {
    const actorsInBundle = getUniqueNodes(bundle, (bundle) => bundle.hostname, (bundle) => bundle.uses.filter(containsCode));
    if (actorsInBundle.size > __classPrivateFieldGet(this, _SupervisorActor_maximumActors, "f")) {
        throw new Error(`${bundle.hostname} has more actors than the runtime supports`);
    }
    // toLoadCount is the number of actors in the bundle that aren't already in the dependency graph
    const toLoad = __classPrivateFieldGet(this, _SupervisorActor_dependencyGraph, "f").addNode(bundle, undefined);
    // While there isn't enough room for the number of actors we need to load
    while (__classPrivateFieldGet(this, _SupervisorActor_dependencyGraph, "f").size() > __classPrivateFieldGet(this, _SupervisorActor_maximumActors, "f")) {
        // Unload the oldest root
        const toUnload = __classPrivateFieldGet(this, _SupervisorActor_lruRoots, "f").keys().next().value;
        await this.Unload({ hostname: toUnload, force: true }, metadata);
    }
    return toLoad;
};
