import { EncryptionProtocol } from '../protocols/encryption.js';
import { PermissionsProtocol } from '../protocols/permissions.js';
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
const POSITION_PAD_WIDTH = 20;
/**
 * Shared helpers for the replication log substrate.
 */
export class Replication {
    static globalDomain = '';
    static protocolDomain(protocolUri) {
        return `protocol:${protocolUri}`;
    }
    static permissionDomain(protocolUri) {
        return `perm:${protocolUri}`;
    }
    static encryptionDomain(protocolUri) {
        return `enc:${protocolUri}`;
    }
    static taggedCoreProtocolDomains(protocolUri, protocolsInScope = new Set()) {
        if (Replication.isCoreProtocolUri(protocolUri)) {
            return [];
        }
        const domains = [];
        if (!protocolsInScope.has(PermissionsProtocol.uri)) {
            domains.push(Replication.permissionDomain(protocolUri));
        }
        if (!protocolsInScope.has(EncryptionProtocol.uri)) {
            domains.push(Replication.encryptionDomain(protocolUri));
        }
        return domains;
    }
    static async deriveStreamId(tenant) {
        const bytes = new TextEncoder().encode(tenant);
        const hashBuffer = await crypto.subtle.digest('SHA-256', bytes);
        const hashArray = new Uint8Array(hashBuffer);
        return Array.from(hashArray.slice(0, 8), (b) => b.toString(16).padStart(2, '0')).join('');
    }
    static computeFingerprintScopes(message, indexes) {
        const scopes = [Replication.globalDomain];
        const descriptor = message.descriptor;
        const protocol = indexes.protocol;
        if (typeof protocol === 'string') {
            scopes.push(Replication.protocolDomain(protocol));
            const indexedTaggedProtocol = indexes['tag.protocol'];
            const taggedProtocol = indexedTaggedProtocol ?? descriptor.tags?.protocol;
            if (typeof taggedProtocol === 'string') {
                if (protocol === PermissionsProtocol.uri) {
                    scopes.push(Replication.permissionDomain(taggedProtocol));
                }
                else if (protocol === EncryptionProtocol.uri) {
                    scopes.push(Replication.encryptionDomain(taggedProtocol));
                }
            }
        }
        return scopes;
    }
    static assertFingerprintScopesUntouched(persistedScopes, message, messageCid, newIndexes) {
        const expectedScopes = Replication.computeFingerprintScopes(message, newIndexes);
        if (!Replication.scopeSetsMatch(persistedScopes, expectedScopes)) {
            Replication.throwFingerprintScopeMutation(messageCid);
        }
    }
    static scopeSetsMatch(left, right) {
        if (left.length !== right.length) {
            return false;
        }
        return right.every((scope) => left.includes(scope));
    }
    static throwFingerprintScopeMutation(messageCid) {
        throw new DwnError(DwnErrorCode.MessageStoreFingerprintScopeMutation, `index replacement for message ${messageCid} would change its persisted fingerprint scopes`);
    }
    static isCoreProtocolUri(protocolUri) {
        return protocolUri === PermissionsProtocol.uri || protocolUri === EncryptionProtocol.uri;
    }
    static async hashMessageCid(messageCid) {
        const bytes = new TextEncoder().encode(messageCid);
        const hashBuffer = await crypto.subtle.digest('SHA-256', bytes);
        return new Uint8Array(hashBuffer);
    }
    static emptyFingerprint() {
        return new Uint8Array(32);
    }
    static xorFingerprint(fingerprint, contribution) {
        const result = new Uint8Array(32);
        for (let i = 0; i < 32; i++) {
            result[i] = fingerprint[i] ^ contribution[i];
        }
        return result;
    }
    static fingerprintToHex(fingerprint) {
        return Array.from(fingerprint, (b) => b.toString(16).padStart(2, '0')).join('');
    }
    static hexToFingerprint(hex) {
        const bytes = new Uint8Array(32);
        for (let i = 0; i < 32; i++) {
            bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
        }
        return bytes;
    }
    static encodePositionKey(position) {
        const decimal = position.toString();
        if (position < 0n || decimal.length > POSITION_PAD_WIDTH) {
            throw new DwnError(DwnErrorCode.MessageStoreReplicationPositionOverflow, `log position ${decimal} cannot be encoded within ${POSITION_PAD_WIDTH} digits`);
        }
        return decimal.padStart(POSITION_PAD_WIDTH, '0');
    }
}
//# sourceMappingURL=replication.js.map