/**
 * Protocol definition fetching and caching utilities for {@link AgentDwnApi}.
 *
 * Extracted from `dwn-api.ts` to keep protocol-resolution logic in its own
 * module.
 *
 * @module
 */
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 { KeyDerivationScheme } from '@enbox/dwn-sdk-js';
import { DwnInterface as DwnInterfaceEnum, dwnMessageConstructors } from './types/dwn.js';
// ---------------------------------------------------------------------------
// Exported functions
// ---------------------------------------------------------------------------
/**
 * Fetches a protocol definition from the **local** DWN, backed by a TTL cache.
 *
 * @param tenantDid - The DID whose DWN to query
 * @param protocolUri - The protocol URI to look up
 * @param dwn - The local DWN instance
 * @param getSigner - Callback to obtain the signer for `tenantDid`
 * @param cache - The shared protocol definition cache
 * @returns The protocol definition, or `undefined` if not installed
 */
export function getProtocolDefinition(tenantDid, protocolUri, dwn, getSigner, cache, granteeDid, permissionGrantId) {
    return __awaiter(this, void 0, void 0, function* () {
        var _a;
        const cacheKey = `${tenantDid}~${protocolUri}`;
        const cached = cache.get(cacheKey);
        if (cached) {
            return cached;
        }
        // When operating as a delegate, the tenant's private signing key is not
        // available locally. Sign the ProtocolsQuery with the delegate's key
        // and include the permissionGrantId so the local DWN authorises the
        // query against the unpublished protocol.
        const signerDid = granteeDid !== null && granteeDid !== void 0 ? granteeDid : tenantDid;
        const signer = yield getSigner(signerDid);
        const protocolsQuery = yield dwnMessageConstructors[DwnInterfaceEnum.ProtocolsQuery].create(Object.assign({ filter: { protocol: protocolUri }, signer }, (permissionGrantId ? { permissionGrantId } : {})));
        const reply = yield dwn.processMessage(tenantDid, protocolsQuery.message);
        if (reply.status.code !== 200 || !((_a = reply.entries) === null || _a === void 0 ? void 0 : _a.length)) {
            return undefined;
        }
        const definition = reply.entries[0].descriptor.definition;
        cache.set(cacheKey, definition);
        return definition;
    });
}
/**
 * Fetches a protocol definition from a **remote** DWN.
 *
 * Uses an unsigned `ProtocolsQuery` since public protocols can be queried
 * anonymously.
 *
 * @param targetDid - The remote DWN owner
 * @param protocolUri - The protocol URI to look up
 * @param getDwnEndpointUrls - Callback to resolve DWN endpoint URLs (with local discovery)
 * @param sendDwnRpcRequest - Callback to send the RPC query
 * @param cache - The shared protocol definition cache
 * @returns The protocol definition
 * @throws If the protocol cannot be fetched
 */
export function fetchRemoteProtocolDefinition(targetDid, protocolUri, getDwnEndpointUrls, sendDwnRpcRequest, cache) {
    return __awaiter(this, void 0, void 0, function* () {
        var _a;
        const cacheKey = `remote~${targetDid}~${protocolUri}`;
        const cached = cache.get(cacheKey);
        if (cached) {
            return cached;
        }
        const protocolsQuery = yield dwnMessageConstructors[DwnInterfaceEnum.ProtocolsQuery].create({
            filter: { protocol: protocolUri },
        });
        const reply = yield sendDwnRpcRequest({
            targetDid,
            dwnEndpointUrls: yield getDwnEndpointUrls(targetDid),
            message: protocolsQuery.message,
        });
        if (reply.status.code !== 200 || !((_a = reply.entries) === null || _a === void 0 ? void 0 : _a.length)) {
            throw new Error(`AgentDwnApi: Failed to fetch protocol '${protocolUri}' from ` +
                `'${targetDid}'. The recipient may not have the protocol installed.`);
        }
        const definition = reply.entries[0].descriptor.definition;
        cache.set(cacheKey, definition);
        return definition;
    });
}
/**
 * Extracts the `derivedPublicKey` from an existing `ProtocolContext`-encrypted
 * record in a context on a remote DWN.
 *
 * This key allows an external author to encrypt new records in the same
 * context without knowing the context private key.
 *
 * @param targetDid      - The DWN owner's DID
 * @param protocolUri    - The protocol URI to search
 * @param rootContextId  - The root context ID
 * @param requesterDid   - The DID of the requester (used for signing the query)
 * @param getDwnEndpointUrls - Callback to resolve DWN endpoint URLs (with local discovery)
 * @param getSigner      - Callback to obtain the signer for `requesterDid`
 * @param sendDwnRpcRequest - Callback to send the RPC query
 * @returns The rootKeyId and derivedPublicKey, or `undefined` if no
 *          `ProtocolContext` record exists yet
 */
export function extractDerivedPublicKey(targetDid, protocolUri, rootContextId, requesterDid, getDwnEndpointUrls, getSigner, sendDwnRpcRequest) {
    return __awaiter(this, void 0, void 0, function* () {
        var _a, _b;
        const signer = yield getSigner(requesterDid);
        // Query the target's DWN for any record in this context
        const recordsQuery = yield dwnMessageConstructors[DwnInterfaceEnum.RecordsQuery].create({
            signer,
            filter: {
                protocol: protocolUri,
                contextId: rootContextId,
            },
        });
        const dwnEndpointUrls = yield getDwnEndpointUrls(targetDid);
        const queryReply = yield sendDwnRpcRequest({
            targetDid,
            dwnEndpointUrls,
            message: recordsQuery.message,
        });
        if (queryReply.status.code !== 200 || !((_a = queryReply.entries) === null || _a === void 0 ? void 0 : _a.length)) {
            return undefined;
        }
        // Search entries for one with a ProtocolContext recipient entry
        // that includes derivedPublicKey
        for (const entry of queryReply.entries) {
            if ((_b = entry.encryption) === null || _b === void 0 ? void 0 : _b.recipients) {
                const contextEntry = entry.encryption.recipients.find((r) => r.header.derivationScheme === KeyDerivationScheme.ProtocolContext && r.header.derivedPublicKey);
                if (contextEntry === null || contextEntry === void 0 ? void 0 : contextEntry.header.derivedPublicKey) {
                    return {
                        rootKeyId: contextEntry.header.kid,
                        derivedPublicKey: contextEntry.header.derivedPublicKey,
                    };
                }
            }
        }
        return undefined;
    });
}
//# sourceMappingURL=dwn-protocol-cache.js.map