import * as ipldDagCbor from '@ipld/dag-cbor';
import * as ipldDagJson from '@ipld/dag-json';
import toBuffer from 'it-to-buffer';
import { code as jsonCode } from 'multiformats/codecs/json';
import { CODEC_CBOR } from "../constants.js";
import { notAcceptableResponse, okRangeResponse } from '../utils/responses.js';
import { BasePlugin } from './plugin-base.js';
/**
 * Handles `dag-json` content, including requests with Accept: `application/vnd.ipld.dag-cbor` and `application/cbor`.
 */
export class JsonPlugin extends BasePlugin {
    id = 'json-plugin';
    codes = [ipldDagJson.code, jsonCode];
    canHandle({ cid, accept, byteRangeContext }) {
        if (byteRangeContext == null) {
            return false;
        }
        if (accept?.mimeType === 'application/vnd.ipld.dag-json' && cid.code !== ipldDagCbor.code && cid.code !== CODEC_CBOR) {
            // we can handle application/vnd.ipld.dag-json, but if the CID codec is
            // cbor related, cbor related plugins will handle it
            // TODO: remove the need for deny-listing cases in plugins
            return true;
        }
        return ipldDagJson.code === cid.code || jsonCode === cid.code;
    }
    async handle(context) {
        const { path, resource, cid, accept, options } = context;
        const { getBlockstore } = this.pluginOptions;
        const session = options?.session ?? true;
        this.log.trace('fetching %c/%s', cid, path);
        const terminalCid = context.pathDetails?.terminalElement.cid ?? context.cid;
        const blockstore = getBlockstore(terminalCid, resource, session, options);
        const block = await toBuffer(blockstore.get(terminalCid, options));
        let body;
        if (accept?.mimeType === 'application/vnd.ipld.dag-cbor' || accept?.mimeType === 'application/cbor') {
            try {
                // if vnd.ipld.dag-cbor has been specified, convert to the format - note
                // that this supports more data types than regular JSON, the content-type
                // response header is set so the user knows to process it differently
                const obj = ipldDagJson.decode(block);
                body = ipldDagCbor.encode(obj);
            }
            catch (err) {
                this.log.error('could not transform %c to application/vnd.ipld.dag-cbor - %e', err);
                return notAcceptableResponse(resource);
            }
        }
        else {
            // skip decoding
            body = block;
        }
        let contentType;
        if (accept == null) {
            if (ipldDagJson.code === cid.code) {
                contentType = 'application/vnd.ipld.dag-json';
            }
            else {
                contentType = 'application/json';
            }
        }
        else {
            contentType = accept?.mimeType.split(';')[0];
        }
        context.byteRangeContext.setBody(body);
        const response = okRangeResponse(resource, context.byteRangeContext.getBody(contentType), { byteRangeContext: context.byteRangeContext, log: this.log });
        response.headers.set('content-type', context.byteRangeContext.getContentType() ?? contentType);
        if (!context.byteRangeContext.isValidRangeRequest) {
            response.headers.set('content-length', body.length.toString());
        }
        return response;
    }
}
//# sourceMappingURL=plugin-handle-json.js.map