import { authenticate } from '../core/auth.js';
import { DataStream } from '../utils/data-stream.js';
import { Encoder } from '../utils/encoder.js';
import { Message } from '../core/message.js';
import { messageReplyFromError } from '../core/message-reply.js';
import { MessagesGrantAuthorization } from '../core/messages-grant-authorization.js';
import { MessagesRead } from '../interfaces/messages-read.js';
import { Records } from '../utils/records.js';
import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
export class MessagesReadHandler {
    deps;
    constructor(deps) {
        this.deps = deps;
    }
    async handle({ tenant, message }) {
        let messagesRead;
        try {
            messagesRead = await MessagesRead.parse(message);
        }
        catch (e) {
            return messageReplyFromError(e, 400);
        }
        try {
            await authenticate(message.authorization, this.deps.didResolver);
        }
        catch (e) {
            return messageReplyFromError(e, 401);
        }
        const messageResult = await this.deps.messageStore.get(tenant, message.descriptor.messageCid);
        if (messageResult === undefined) {
            return { status: { code: 404, detail: 'Not Found' } };
        }
        try {
            await MessagesReadHandler.authorizeMessagesRead(tenant, messagesRead, messageResult, this.deps);
        }
        catch (error) {
            return messageReplyFromError(error, 401);
        }
        // If the message is a RecordsWrite, we include the data in the response if it is available
        const entry = { message: messageResult, messageCid: message.descriptor.messageCid };
        if (Records.isRecordsWrite(messageResult)) {
            const recordsWrite = entry.message;
            // RecordsWrite specific handling, if MessageStore has embedded `encodedData` return it with the entry.
            // we store `encodedData` along with the message if the data is below a certain threshold.
            if (recordsWrite.encodedData === undefined) {
                // check the data store for the associated data
                const result = await this.deps.dataStore.get(tenant, recordsWrite.recordId, recordsWrite.descriptor.dataCid);
                if (result?.dataStream !== undefined) {
                    entry.data = result.dataStream;
                }
            }
            else {
                const dataBytes = Encoder.base64UrlToBytes(recordsWrite.encodedData);
                entry.data = DataStream.fromBytes(dataBytes);
                delete recordsWrite.encodedData;
            }
        }
        return {
            status: { code: 200, detail: 'OK' },
            entry
        };
    }
    /**
     * @param deps Used to fetch related permission grant, permission revocation, and/or RecordsWrites for permission scope validation.
     */
    static async authorizeMessagesRead(tenant, messagesRead, matchedMessage, deps) {
        if (messagesRead.author === tenant) {
            // If the author is the tenant, no further authorization is needed
            return;
        }
        const permissionGrantIds = Message.getPermissionGrantIds(messagesRead.signaturePayload);
        if (messagesRead.author !== undefined && permissionGrantIds.length > 0) {
            const permissionGrants = await MessagesGrantAuthorization.fetchPermissionGrants(tenant, deps.validationStateReader, permissionGrantIds);
            await MessagesGrantAuthorization.authorizeMessagesRead({
                messagesReadMessage: messagesRead.message,
                messageToRead: matchedMessage,
                expectedGrantor: tenant,
                expectedGrantee: messagesRead.author,
                permissionGrants,
                validationStateReader: deps.validationStateReader
            });
        }
        else {
            throw new DwnError(DwnErrorCode.MessagesReadAuthorizationFailed, 'protocol message failed authorization');
        }
    }
}
//# sourceMappingURL=messages-read.js.map