import { GrantAuthorization } from './grant-authorization.js';
import { PermissionConditionPublication } from '../types/permission-types.js';
import { PermissionScopeMatcher } from '../utils/permission-scope.js';
import { DwnError, DwnErrorCode } from './dwn-error.js';
export class RecordsGrantAuthorization {
    /**
     * Authorizes the given RecordsWrite in the scope of the DID given.
     */
    static async authorizeWrite(input) {
        const { recordsWriteMessage, expectedGrantor, expectedGrantee, permissionGrant, validationStateReader } = input;
        await GrantAuthorization.performBaseValidation({
            incomingMessage: recordsWriteMessage,
            expectedGrantor,
            expectedGrantee,
            permissionGrant,
            validationStateReader
        });
        // NOTE: validated the invoked permission is for Records in GrantAuthorization.performBaseValidation()
        RecordsGrantAuthorization.verifyScope(recordsWriteMessage, permissionGrant.scope);
        RecordsGrantAuthorization.verifyConditions(recordsWriteMessage, permissionGrant.conditions);
    }
    /**
     * Authorizes a RecordsReadMessage using the given permission grant.
     * @param validationStateReader Used to check if the given grant has been revoked.
     */
    static async authorizeRead(input) {
        const { recordsReadMessage, recordsWriteMessageToBeRead, expectedGrantor, expectedGrantee, permissionGrant, validationStateReader } = input;
        await GrantAuthorization.performBaseValidation({
            incomingMessage: recordsReadMessage,
            expectedGrantor,
            expectedGrantee,
            permissionGrant,
            validationStateReader
        });
        // NOTE: validated the invoked permission is for Records in GrantAuthorization.performBaseValidation()
        RecordsGrantAuthorization.verifyScope(recordsWriteMessageToBeRead, permissionGrant.scope);
    }
    /**
     * Authorizes the scope of a permission grant for RecordsQuery or RecordsSubscribe.
     * @param validationStateReader Used to check if the grant has been revoked.
     */
    static async authorizeQueryOrSubscribe(input) {
        const { incomingMessage, expectedGrantor, expectedGrantee, permissionGrant, validationStateReader } = input;
        await GrantAuthorization.performBaseValidation({
            incomingMessage,
            expectedGrantor,
            expectedGrantee,
            permissionGrant,
            validationStateReader
        });
        // The grant's protocol must match the query/subscribe filter's protocol.
        // NOTE: validated the invoked permission is for Records in GrantAuthorization.performBaseValidation()
        const permissionScope = permissionGrant.scope;
        const messageFilter = incomingMessage.descriptor.filter;
        if (!PermissionScopeMatcher.matches(permissionScope, {
            protocol: messageFilter.protocol,
            protocolPath: messageFilter.protocolPath,
            contextId: messageFilter.contextId,
        })) {
            throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationQueryOrSubscribeProtocolScopeMismatch, `Grant scope does not match Records ${incomingMessage.descriptor.method} filter`);
        }
    }
    /**
     * Authorizes the scope of a permission grant for RecordsDelete.
     * @param validationStateReader Used to check if the grant has been revoked.
     */
    static async authorizeDelete(input) {
        const { recordsDeleteMessage, recordsWriteToDelete, expectedGrantor, expectedGrantee, permissionGrant, validationStateReader } = input;
        await GrantAuthorization.performBaseValidation({
            incomingMessage: recordsDeleteMessage,
            expectedGrantor,
            expectedGrantee,
            permissionGrant,
            validationStateReader
        });
        // NOTE: validated the invoked permission is for Records in GrantAuthorization.performBaseValidation()
        RecordsGrantAuthorization.verifyDeleteScope(recordsWriteToDelete, permissionGrant.scope);
    }
    /**
     * Verifies a record against the scope of the given grant.
     */
    static verifyScope(recordsWriteMessage, grantScope) {
        const target = RecordsGrantAuthorization.getProtocolScopeTarget(recordsWriteMessage);
        if (PermissionScopeMatcher.matches(grantScope, target)) {
            return;
        }
        if (grantScope.protocol !== target.protocol) {
            throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationScopeProtocolMismatch, `Grant scope specifies different protocol than what appears in the record`);
        }
        RecordsGrantAuthorization.throwScopeMismatchAfterProtocolMatch(grantScope, target);
    }
    /**
     * Verifies RecordsDelete scope while preserving the delete-specific protocol mismatch code.
     */
    static verifyDeleteScope(recordsWriteMessage, grantScope) {
        const target = RecordsGrantAuthorization.getProtocolScopeTarget(recordsWriteMessage);
        if (PermissionScopeMatcher.matches(grantScope, target)) {
            return;
        }
        if (grantScope.protocol !== target.protocol) {
            throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationDeleteProtocolScopeMismatch, `Grant protocol scope ${grantScope.protocol} does not match protocol in record to delete ${target.protocol}`);
        }
        RecordsGrantAuthorization.throwScopeMismatchAfterProtocolMatch(grantScope, target);
    }
    static getProtocolScopeTarget(recordsWriteMessage) {
        return {
            protocol: recordsWriteMessage.descriptor.protocol,
            protocolPath: recordsWriteMessage.descriptor.protocolPath,
            contextId: recordsWriteMessage.contextId,
        };
    }
    static throwScopeMismatchAfterProtocolMatch(grantScope, target) {
        // If grant specifies a contextId, check that record falls under that contextId
        if (grantScope.contextId !== undefined) {
            throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationScopeContextIdMismatch, `Grant scope specifies different contextId than what appears in the record`);
        }
        // If grant specifies protocolPath, check that record is at that protocolPath
        if (grantScope.protocolPath !== undefined && grantScope.protocolPath !== target.protocolPath) {
            throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationScopeProtocolPathMismatch, `Grant scope specifies different protocolPath than what appears in the record`);
        }
        throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationScopeMismatch, `Grant scope does not match the record`);
    }
    /**
     * Verifies grant `conditions`.
     * Currently the only condition is `published` which only applies to RecordsWrites
     */
    static verifyConditions(recordsWriteMessage, conditions) {
        // If conditions require publication, RecordsWrite must have `published` === true
        if (conditions?.publication === PermissionConditionPublication.Required && !recordsWriteMessage.descriptor.published) {
            throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationConditionPublicationRequired, 'Permission grant requires message to be published');
        }
        // if conditions prohibit publication, RecordsWrite must have published === false or undefined
        if (conditions?.publication === PermissionConditionPublication.Prohibited && recordsWriteMessage.descriptor.published) {
            throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationConditionPublicationProhibited, 'Permission grant prohibits message from being published');
        }
    }
}
//# sourceMappingURL=records-grant-authorization.js.map