import { DwnInterfaceName, DwnMethodName, PermissionScopeMatcher, PermissionsProtocol } from '@enbox/dwn-sdk-js';
/**
 * Classifies whether a live event belongs to the link's current sync scope.
 *
 * Full links accept every message. Protocol-set links accept records for a
 * covered protocol, ProtocolsConfigure messages that install a covered
 * protocol, and permission records tagged for a covered protocol. RecordsDelete
 * messages carry no protocol in their descriptor, so they must be classified
 * from the event's initial write metadata.
 */
export function classifySyncEventScope(event, scope) {
    return classifySyncMessageScope({
        message: event.message,
        initialWrite: event.initialWrite,
        scope,
    });
}
/**
 * Classifies whether a DWN message belongs to a sync scope before local apply.
 *
 * If a RecordsDelete cannot be tied to its initial write, the result is
 * `unknown` so callers fail closed instead of applying an unclassified delete.
 */
export function classifySyncMessageScope({ message, initialWrite, scope, }) {
    if (scope.kind === 'full') {
        return 'in-scope';
    }
    if (scope.kind === 'recordsProjection') {
        return classifyRecordsProjectionScope(message, initialWrite, scope);
    }
    return classifyProtocolSetScope(message, initialWrite, scope);
}
function classifyProtocolSetScope(message, initialWrite, scope) {
    const descriptor = message.descriptor;
    const scopedDescriptor = getScopedMessageDescriptor(message, initialWrite);
    if (scopedDescriptor === undefined) {
        return 'unknown';
    }
    const permissionRecordClassification = classifyTaggedPermissionRecord(scopedDescriptor, scope.protocols);
    if (permissionRecordClassification !== undefined) {
        return permissionRecordClassification;
    }
    const protocolClassification = classifyProtocolField(scopedDescriptor.protocol, scope.protocols);
    if (protocolClassification !== undefined) {
        return protocolClassification;
    }
    return classifyProtocolsConfigureDescriptor(descriptor, scope.protocols);
}
function classifyTaggedPermissionRecord(descriptor, protocols) {
    if (descriptor.protocol !== PermissionsProtocol.uri || !isRecordObject(descriptor.tags)) {
        return undefined;
    }
    const taggedProtocol = descriptor.tags.protocol;
    return typeof taggedProtocol === 'string' && protocols.includes(taggedProtocol)
        ? 'in-scope'
        : 'out-of-scope';
}
function classifyProtocolField(protocol, protocols) {
    if (typeof protocol !== 'string') {
        return undefined;
    }
    return protocols.includes(protocol) ? 'in-scope' : 'out-of-scope';
}
function classifyProtocolsConfigureDescriptor(descriptor, protocols) {
    if (descriptor.interface === DwnInterfaceName.Protocols &&
        descriptor.method === DwnMethodName.Configure &&
        isRecordObject(descriptor.definition)) {
        const definitionProtocol = descriptor.definition.protocol;
        return typeof definitionProtocol === 'string' && protocols.includes(definitionProtocol)
            ? 'in-scope'
            : 'out-of-scope';
    }
    return 'out-of-scope';
}
function getScopedMessageDescriptor(message, initialWrite) {
    const descriptor = message.descriptor;
    if (descriptor.interface === DwnInterfaceName.Records &&
        descriptor.method === DwnMethodName.Delete) {
        return initialWrite === null || initialWrite === void 0 ? void 0 : initialWrite.descriptor;
    }
    return descriptor;
}
function classifyRecordsProjectionScope(message, initialWrite, scope) {
    const target = getRecordsProjectionTarget(message, initialWrite);
    if (target === undefined) {
        return isRecordsDelete(message) ? 'unknown' : 'out-of-scope';
    }
    return scope.scopes.some(projectionScope => PermissionScopeMatcher.matches(projectionScope, target))
        ? 'in-scope'
        : 'out-of-scope';
}
function getRecordsProjectionTarget(message, initialWrite) {
    const descriptor = message.descriptor;
    if (descriptor.interface !== DwnInterfaceName.Records) {
        return undefined;
    }
    if (descriptor.method === DwnMethodName.Write) {
        return {
            protocol: stringField(descriptor.protocol),
            protocolPath: stringField(descriptor.protocolPath),
            contextId: message.contextId,
        };
    }
    if (descriptor.method === DwnMethodName.Delete) {
        if (initialWrite === undefined) {
            return undefined;
        }
        return {
            protocol: initialWrite.descriptor.protocol,
            protocolPath: initialWrite.descriptor.protocolPath,
            contextId: initialWrite.contextId,
        };
    }
}
function isRecordsDelete(message) {
    const descriptor = message.descriptor;
    return descriptor.interface === DwnInterfaceName.Records &&
        descriptor.method === DwnMethodName.Delete;
}
function isRecordObject(value) {
    return typeof value === 'object' && value !== null;
}
function stringField(value) {
    return typeof value === 'string' ? value : undefined;
}
//# sourceMappingURL=sync-scope-acceptance.js.map