var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { DidResolverCacheLevel } from '@enbox/dids';
import { logger } from '@enbox/common';
/**
 * AgentDidResolverCache keeps a stale copy of the Agent's managed Identity DIDs and only evicts and refreshes upon a successful resolution.
 * This allows for quick and offline access to the internal DIDs used by the agent.
 */
export class AgentDidResolverCache extends DidResolverCacheLevel {
    constructor({ agent, db, location, ttl }) {
        super({ db, location, ttl });
        /** A map of DIDs that are currently in-flight. This helps avoid going into an infinite loop */
        this._resolving = new Map();
        this._agent = agent;
    }
    get agent() {
        if (!this._agent) {
            throw new Error('Agent not initialized');
        }
        return this._agent;
    }
    set agent(agent) {
        this._agent = agent;
    }
    /**
     * Get the DID resolution result from the cache for the given DID.
     *
     * If the DID is managed by the agent, or is the agent's own DID, it will not evict it from the cache until a new resolution is successful.
     * This is done to achieve quick and offline access to the agent's own managed DIDs.
     */
    get(did) {
        return __awaiter(this, void 0, void 0, function* () {
            try {
                const str = yield this.cache.get(did);
                const cachedResult = JSON.parse(str);
                if (!this._resolving.has(did) && Date.now() >= cachedResult.ttlMillis) {
                    this._resolving.set(did, true);
                    // if a DID is stored in the DID Store, then we don't want to evict it from the cache until we have a successful resolution
                    // upon a successful resolution, we will update both the storage and the cache with the newly resolved Document.
                    const storedDid = yield this.agent.did.get({ didUri: did, tenant: this.agent.agentDid.uri });
                    if ('undefined' !== typeof storedDid) {
                        try {
                            const result = yield this.agent.did.resolve(did);
                            // if the resolution was successful, update the stored DID with the new Document
                            if (!result.didResolutionMetadata.error && result.didDocument) {
                                const portableDid = Object.assign(Object.assign({}, storedDid), { document: result.didDocument, metadata: result.didDocumentMetadata });
                                try {
                                    // this will throw an error if the DID is not managed by the agent, or there is no difference between the stored and resolved DID
                                    // We don't publish the DID in this case, as it was received by the resolver.
                                    yield this.agent.did.update({ portableDid, tenant: this.agent.agentDid.uri, publish: false });
                                }
                                catch (error) {
                                    // if the error is not due to no changes detected, log the error
                                    if (error.message && !error.message.includes('No changes detected, update aborted')) {
                                        logger.error(`Error updating DID: ${error.message}`);
                                    }
                                }
                            }
                        }
                        finally {
                            this._resolving.delete(did);
                        }
                    }
                    else {
                        this._resolving.delete(did);
                        this.cache.nextTick(() => this.cache.del(did));
                    }
                }
                return cachedResult.value;
            }
            catch (error) {
                if (error.notFound) {
                    return;
                }
                throw error;
            }
        });
    }
}
//# sourceMappingURL=agent-did-resolver-cache.js.map