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 { Jws, Message } from '@enbox/dwn-sdk-js';
import { lexicographicalCompare } from './types/sync.js';
export class SyncProtocolRootPermissionGrantMissingError extends Error {
    constructor(messageType, protocol) {
        super(`SyncPermissions: No active protocol-root Messages.Read permission found for ${messageType}: ${protocol}`);
        this.name = 'SyncProtocolRootPermissionGrantMissingError';
        this.protocol = protocol;
    }
}
/** Returns a sorted, duplicate-free grant ID set, or `undefined` for owner requests. */
export function toMessagesPermissionGrantIds(permissionGrantIds) {
    if (permissionGrantIds === undefined || permissionGrantIds.length === 0) {
        return undefined;
    }
    return [...new Set(permissionGrantIds)].sort(lexicographicalCompare);
}
/**
 * Gets the active permission grant IDs that authorize a Messages operation.
 *
 * Owner-authored Messages feed access does not invoke grants. Delegate full
 * scope requires at least one active unscoped Messages.Read grant. Delegate
 * protocol-set scope requires each requested protocol root to be covered by an
 * active Messages.Read grant, then invokes every active grant that participates
 * in that root set. This keeps the authorization epoch tied to grant churn
 * without widening the CID set being compared.
 */
export function getMessagesPermissionGrantsForScope(_a) {
    return __awaiter(this, arguments, void 0, function* ({ did, delegateDid, protocols, messageType, permissionsApi, }) {
        const requestedScope = protocols === undefined
            ? { kind: 'full' }
            : { kind: 'protocolSet', protocols };
        const resolutions = yield resolveMessagesScopes({
            did,
            delegateDid,
            requestedScope,
            messageType,
            permissionsApi,
        });
        return resolutions
            .flatMap(resolution => resolution.permissionGrants)
            .sort((a, b) => lexicographicalCompare(a.grant.id, b.grant.id));
    });
}
/**
 * Resolves active Messages.Read grants into one or more Messages feed scopes.
 *
 * Exact protocolPath and contextId grants do not authorize root log scopes
 * because they cover a strict subset of the messages in those scopes.
 */
export function resolveMessagesScopes(_a) {
    return __awaiter(this, arguments, void 0, function* ({ did, delegateDid, requestedScope, messageType, permissionsApi, }) {
        if (!delegateDid) {
            return [{ scope: requestedScope, permissionGrants: [] }];
        }
        const now = new Date().toISOString();
        const permissionGrants = (yield permissionsApi.fetchGrants({
            author: delegateDid,
            target: delegateDid,
            grantor: did,
            grantee: delegateDid,
            checkRevoked: true,
        })).filter(entry => isActiveMessagesGrant(entry, did, delegateDid, now));
        if (requestedScope.kind === 'full') {
            return [resolveFullScope(permissionGrants, requestedScope, messageType)];
        }
        return [resolveProtocolSetScope(permissionGrants, requestedScope, messageType)];
    });
}
function resolveFullScope(permissionGrants, requestedScope, messageType) {
    const grants = permissionGrants
        .filter(grantMatchesFullRoot)
        .sort((a, b) => lexicographicalCompare(a.grant.id, b.grant.id));
    if (grants.length === 0) {
        throw new Error(`SyncPermissions: No active Messages.Read permission found for ${messageType}: all protocols`);
    }
    return { scope: requestedScope, permissionGrants: grants };
}
function resolveProtocolSetScope(permissionGrants, requestedScope, messageType) {
    for (const protocol of requestedScope.protocols) {
        if (permissionGrants.some(entry => grantMatchesProtocolRoot(entry, protocol))) {
            continue;
        }
        throw new SyncProtocolRootPermissionGrantMissingError(messageType, protocol);
    }
    const grants = permissionGrants
        .filter(entry => grantParticipatesInProtocolSet(entry, requestedScope.protocols))
        .sort((a, b) => lexicographicalCompare(a.grant.id, b.grant.id));
    return { scope: requestedScope, permissionGrants: grants };
}
function isMessagesReadScope(scope) {
    return scope.interface === 'Messages' &&
        scope.method === 'Read';
}
function grantParticipatesInProtocolSet(entry, protocols) {
    return protocols.some(protocol => grantMatchesProtocolRoot(entry, protocol));
}
/** Converts permission grant entries into authorization epoch inputs. */
export function toSyncAuthorizationGrants(permissionGrants) {
    if (permissionGrants.length === 0) {
        throw new Error('SyncPermissions: delegate authorization requires at least one grant.');
    }
    return permissionGrants
        .map(({ grant }) => ({
        dateExpires: grant.dateExpires,
        dateGranted: grant.dateGranted,
        id: grant.id,
    }))
        .sort((a, b) => lexicographicalCompare(a.id, b.id));
}
function isActiveMessagesGrant(entry, grantor, grantee, now) {
    const { grant } = entry;
    if (grant.grantor !== grantor || grant.grantee !== grantee) {
        return false;
    }
    if (grant.dateGranted > now || grant.dateExpires <= now) {
        return false;
    }
    const scope = grant.scope;
    return scope.interface === 'Messages' &&
        scope.method === 'Read';
}
function grantMatchesFullRoot(entry) {
    const scope = entry.grant.scope;
    return isMessagesReadScope(scope) &&
        scope.protocol === undefined &&
        scope.protocolPath === undefined &&
        scope.contextId === undefined;
}
function grantMatchesProtocolRoot(entry, protocol) {
    if (grantMatchesFullRoot(entry)) {
        return true;
    }
    const scope = entry.grant.scope;
    return isMessagesReadScope(scope) &&
        scope.protocol === protocol &&
        scope.protocolPath === undefined &&
        scope.contextId === undefined;
}
/** Returns sorted grant IDs from permission grant entries. */
export function permissionGrantIdsFromEntries(permissionGrants) {
    return toMessagesPermissionGrantIds(permissionGrants.map(entry => entry.grant.id));
}
/**
 * Returns the permission grant IDs invoked by a message.
 *
 * Real DWN messages use the author signature payload as the source of truth.
 */
export function getInvokedPermissionGrantIds(message) {
    if (message.authorization === undefined) {
        return [];
    }
    try {
        const signaturePayload = Jws.decodePlainObjectPayload(message.authorization.signature);
        return Message.getPermissionGrantIds(signaturePayload);
    }
    catch (_a) {
        return [];
    }
}
//# sourceMappingURL=sync-permission-grants.js.map