import { __esDecorate, __runInitializers } from "tslib";
import { LitElement, html } from 'lit';
import { PayloadSerializer, } from '@api-client/core/lib/transformers/PayloadSerializer.js';
import { property, state } from 'lit/decorators.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
import { PrismHighlighter } from '../highlight/PrismHighlighter.js';
import { getLanguage } from '../highlight/PrismAutoDetect.js';
import { ensureBodyString, imageBody, isBinaryBody, isPdfBody, isTextBody } from '../../lib/http/Payload.js';
import 'prismjs/prism.js';
import 'prismjs/components/prism-json.js';
import 'prismjs/components/prism-javascript.js';
import 'prismjs/components/prism-markdown.js';
import 'prismjs/components/prism-markup.js';
import 'prismjs/components/prism-yaml.js';
import 'prismjs/components/prism-xml-doc.js';
import 'prismjs/components/prism-http.js';
import 'prismjs/plugins/line-numbers/prism-line-numbers.min.js';
var BodyType;
(function (BodyType) {
    // null, undefined, empty
    BodyType[BodyType["nil"] = 0] = "nil";
    // a string value
    BodyType[BodyType["String"] = 1] = "String";
    BodyType[BodyType["Blob"] = 2] = "Blob";
    BodyType[BodyType["File"] = 3] = "File";
    // Multipart FormData object
    BodyType[BodyType["FormData"] = 4] = "FormData";
    // Buffer, ArrayBuffer, UInt8Array, generally binary.
    BodyType[BodyType["Buffer"] = 5] = "Buffer";
})(BodyType || (BodyType = {}));
var ViewType;
(function (ViewType) {
    ViewType[ViewType["Parsed"] = 0] = "Parsed";
    ViewType[ViewType["ImageSvg"] = 1] = "ImageSvg";
    ViewType[ViewType["ImageBinary"] = 2] = "ImageBinary";
    ViewType[ViewType["ImageError"] = 3] = "ImageError";
    ViewType[ViewType["Pdf"] = 4] = "Pdf";
    ViewType[ViewType["Binary"] = 5] = "Binary";
    // application/x-www-form-urlencoded
    ViewType[ViewType["UrlEncoded"] = 6] = "UrlEncoded";
})(ViewType || (ViewType = {}));
let LogBody = (() => {
    let _classSuper = LitElement;
    let _payload_decorators;
    let _payload_initializers = [];
    let _payload_extraInitializers = [];
    let _contentType_decorators;
    let _contentType_initializers = [];
    let _contentType_extraInitializers = [];
    let _raw_decorators;
    let _raw_initializers = [];
    let _raw_extraInitializers = [];
    let _renderBody_decorators;
    let _renderBody_initializers = [];
    let _renderBody_extraInitializers = [];
    let _bodyType_decorators;
    let _bodyType_initializers = [];
    let _bodyType_extraInitializers = [];
    let _viewType_decorators;
    let _viewType_initializers = [];
    let _viewType_extraInitializers = [];
    let _renderTokens_decorators;
    let _renderTokens_initializers = [];
    let _renderTokens_extraInitializers = [];
    return class LogBody extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _payload_decorators = [property()];
            _contentType_decorators = [property({ type: String, reflect: true })];
            _raw_decorators = [property({ type: Boolean })];
            _renderBody_decorators = [state()];
            _bodyType_decorators = [state()];
            _viewType_decorators = [state()];
            _renderTokens_decorators = [state()];
            __esDecorate(this, null, _payload_decorators, { kind: "accessor", name: "payload", static: false, private: false, access: { has: obj => "payload" in obj, get: obj => obj.payload, set: (obj, value) => { obj.payload = value; } }, metadata: _metadata }, _payload_initializers, _payload_extraInitializers);
            __esDecorate(this, null, _contentType_decorators, { kind: "accessor", name: "contentType", static: false, private: false, access: { has: obj => "contentType" in obj, get: obj => obj.contentType, set: (obj, value) => { obj.contentType = value; } }, metadata: _metadata }, _contentType_initializers, _contentType_extraInitializers);
            __esDecorate(this, null, _raw_decorators, { kind: "accessor", name: "raw", static: false, private: false, access: { has: obj => "raw" in obj, get: obj => obj.raw, set: (obj, value) => { obj.raw = value; } }, metadata: _metadata }, _raw_initializers, _raw_extraInitializers);
            __esDecorate(this, null, _renderBody_decorators, { kind: "accessor", name: "renderBody", static: false, private: false, access: { has: obj => "renderBody" in obj, get: obj => obj.renderBody, set: (obj, value) => { obj.renderBody = value; } }, metadata: _metadata }, _renderBody_initializers, _renderBody_extraInitializers);
            __esDecorate(this, null, _bodyType_decorators, { kind: "accessor", name: "bodyType", static: false, private: false, access: { has: obj => "bodyType" in obj, get: obj => obj.bodyType, set: (obj, value) => { obj.bodyType = value; } }, metadata: _metadata }, _bodyType_initializers, _bodyType_extraInitializers);
            __esDecorate(this, null, _viewType_decorators, { kind: "accessor", name: "viewType", static: false, private: false, access: { has: obj => "viewType" in obj, get: obj => obj.viewType, set: (obj, value) => { obj.viewType = value; } }, metadata: _metadata }, _viewType_initializers, _viewType_extraInitializers);
            __esDecorate(this, null, _renderTokens_decorators, { kind: "accessor", name: "renderTokens", static: false, private: false, access: { has: obj => "renderTokens" in obj, get: obj => obj.renderTokens, set: (obj, value) => { obj.renderTokens = value; } }, metadata: _metadata }, _renderTokens_initializers, _renderTokens_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #payload_accessor_storage = __runInitializers(this, _payload_initializers, void 0);
        /**
         * The payload to render. When the payload requires de-serialization it will be performed.
         */
        get payload() { return this.#payload_accessor_storage; }
        set payload(value) { this.#payload_accessor_storage = value; }
        #contentType_accessor_storage = (__runInitializers(this, _payload_extraInitializers), __runInitializers(this, _contentType_initializers, void 0));
        /**
         * The content type of the payload.
         * @attribute
         */
        get contentType() { return this.#contentType_accessor_storage; }
        set contentType(value) { this.#contentType_accessor_storage = value; }
        #raw_accessor_storage = (__runInitializers(this, _contentType_extraInitializers), __runInitializers(this, _raw_initializers, false));
        /**
         * When set it does not apply formatting for the code. However, it keeps the highlighting.
         * @attribute
         */
        get raw() { return this.#raw_accessor_storage; }
        set raw(value) { this.#raw_accessor_storage = value; }
        #renderBody_accessor_storage = (__runInitializers(this, _raw_extraInitializers), __runInitializers(this, _renderBody_initializers, void 0));
        get renderBody() { return this.#renderBody_accessor_storage; }
        set renderBody(value) { this.#renderBody_accessor_storage = value; }
        #bodyType_accessor_storage = (__runInitializers(this, _renderBody_extraInitializers), __runInitializers(this, _bodyType_initializers, BodyType.nil));
        get bodyType() { return this.#bodyType_accessor_storage; }
        set bodyType(value) { this.#bodyType_accessor_storage = value; }
        #viewType_accessor_storage = (__runInitializers(this, _bodyType_extraInitializers), __runInitializers(this, _viewType_initializers, ViewType.Parsed
        /**
         * The tokens rendered in the view.
         */
        ));
        get viewType() { return this.#viewType_accessor_storage; }
        set viewType(value) { this.#viewType_accessor_storage = value; }
        #renderTokens_accessor_storage = (__runInitializers(this, _viewType_extraInitializers), __runInitializers(this, _renderTokens_initializers, void 0));
        /**
         * The tokens rendered in the view.
         */
        get renderTokens() { return this.#renderTokens_accessor_storage; }
        set renderTokens(value) { this.#renderTokens_accessor_storage = value; }
        willUpdate(cp) {
            if (cp.has('payload') || cp.has('contentType')) {
                this.prepareContent();
            }
        }
        updated(cp) {
            super.updated(cp);
            if (cp.has('bodyType') || cp.has('viewType')) {
                this.prismComplete();
            }
        }
        /**
         * When processing a payload from the request editor then the value can be any of the `DeserializedPayload` types.
         * However, when processing a response that came from the `CoreEngine` it is always a Buffer / ArrayBuffer
         * and the contents is determined by the content type.
         */
        async prepareContent() {
            const { contentType = '', payload } = this;
            this.renderBody = undefined;
            this.renderTokens = undefined;
            this.viewType = ViewType.Parsed;
            this.bodyType = BodyType.nil;
            if (!payload) {
                this.bodyType = BodyType.nil;
                return;
            }
            const body = await this.deserialize(payload);
            if (body === undefined || body === null || !body) {
                this.bodyType = BodyType.nil;
                return;
            }
            const typedSerialized = payload;
            if (contentType) {
                const image = imageBody(contentType);
                if (isTextBody(contentType)) {
                    this.renderBody = ensureBodyString(body);
                    if (image === 'svg') {
                        this.viewType = ViewType.ImageSvg;
                    }
                }
                else if (image === 'binary') {
                    this.viewType = ViewType.ImageBinary;
                    this.renderBody = body;
                }
                else if (isPdfBody(contentType)) {
                    this.viewType = ViewType.Pdf;
                    this.renderBody = body;
                }
                else if (isBinaryBody(contentType)) {
                    this.viewType = ViewType.Binary;
                    this.renderBody = body;
                }
                else if (body instanceof FormData) {
                    this.viewType = ViewType.Parsed;
                    this.renderBody = body;
                }
                else if (typedSerialized.type === 'file' || typedSerialized.type === 'blob') {
                    this.viewType = ViewType.Binary;
                    this.renderBody = body;
                }
                else if (contentType.includes('application/x-www-form-urlencoded')) {
                    this.viewType = ViewType.UrlEncoded;
                    this.renderBody = body;
                }
                else {
                    this.viewType = ViewType.Parsed;
                    this.renderBody = ensureBodyString(body);
                }
            }
            else {
                this.renderBody = body;
                this.viewType = ViewType.Parsed;
            }
            this.bodyType = this.typeFromType(this.renderBody);
            this.tokenize();
        }
        typeFromType(type) {
            if (type === undefined || type === null || !type) {
                return BodyType.nil;
            }
            if (typeof type === 'string') {
                return BodyType.String;
            }
            if (type instanceof File) {
                return BodyType.File;
            }
            if (type instanceof Blob) {
                return BodyType.Blob;
            }
            if (type instanceof FormData) {
                return BodyType.FormData;
            }
            return BodyType.Buffer;
        }
        async deserialize(value) {
            if (value === undefined || value === null) {
                // both values should be stored correctly
                return undefined;
            }
            if (typeof value === 'string') {
                return value;
            }
            if (value.data) {
                return PayloadSerializer.deserialize(value);
            }
            return value;
        }
        /**
         * Depending on the body type and the contentType, it prepares the tokens to be rendered in the view.
         */
        tokenize() {
            const { contentType, bodyType: type, renderBody: body } = this;
            // if (raw && type !== BodyType.FormData) {
            //   this._tokens = body as string;
            //   return;
            // }
            switch (type) {
                case BodyType.String:
                    this.renderTokens = this.tokenizeStringContent(body, contentType);
                    break;
                case BodyType.FormData:
                    this.renderTokens = this.tokenizeFormDataContent(body);
                    break;
                case BodyType.Buffer:
                    this.renderTokens = this.tokenizeBuffer(body, contentType);
                    break;
                case BodyType.File:
                case BodyType.Blob:
                    this.renderTokens = this.tokenizeFile();
                    break;
                default:
                    this.renderTokens = undefined;
                    break;
            }
        }
        tokenizeStringContent(body, contentType = '') {
            const { raw } = this;
            if (raw) {
                return body;
            }
            const finalBody = this.formatBody(body, contentType);
            const info = getLanguage(contentType);
            const env = {
                code: finalBody,
                grammar: info.grammar,
                language: info.lang,
            };
            // @ts-expect-error There are not types for Prism
            Prism.hooks.run('before-highlight', env);
            // @ts-expect-error There are not types for Prism
            return Prism.highlight(finalBody, info.grammar, info.lang);
        }
        formatBody(body, contentType = '') {
            let result = body;
            if (contentType.includes('json')) {
                try {
                    const parsed = JSON.parse(result);
                    result = JSON.stringify(parsed, null, 2);
                }
                catch {
                    // ...
                }
            }
            return result;
        }
        tokenizeFormDataContent(body) {
            const { raw } = this;
            const boundary = '7MA4YWxkTrZu0gW';
            const lineBreak = '\r\n';
            const templates = [];
            body.forEach((value, key) => {
                if (typeof value === 'string') {
                    templates.push(`--${boundary}${lineBreak}`);
                    templates.push(`Content-Disposition: form-data; name="${key}"${lineBreak}${lineBreak}`);
                    templates.push(`${value}${lineBreak}`);
                }
                else {
                    templates.push(`--${boundary}${lineBreak}`);
                    templates.push(`Content-Disposition: form-data; name="${key}"; filename="${value.name}"${lineBreak}`);
                    templates.push(`Content-Type: ${value.type || 'application/octet-stream'}${lineBreak}${lineBreak}`);
                    templates.push(`{…file content…}${lineBreak}`);
                }
            });
            templates.push(`--${boundary}--${lineBreak}`);
            const code = templates.join('');
            if (raw) {
                return code;
            }
            const highlight = new PrismHighlighter();
            return highlight.tokenize(code, 'http');
        }
        tokenizeImageData(body, contentType) {
            try {
                const arr = new Uint8Array(body);
                const str = arr.reduce((data, byte) => data + String.fromCharCode(byte), '');
                const enc = btoa(str);
                return `data:${contentType};base64, ${enc}`;
            }
            catch {
                return undefined;
            }
        }
        tokenizeBuffer(body, contentType) {
            if (typeof body === 'string') {
                return undefined;
            }
            if (this.viewType === ViewType.ImageBinary) {
                return this.tokenizeImageData(body, contentType);
            }
            try {
                const array = new Uint8Array(body).join(', ');
                return `[${array}]`;
            }
            catch {
                return undefined;
            }
        }
        tokenizeFile() {
            const { payload } = this;
            if (!payload) {
                return undefined;
            }
            const typed = payload;
            if (typed.type !== 'file' && typed.type !== 'blob') {
                return undefined;
            }
            if (!Array.isArray(typed.data)) {
                return undefined;
            }
            const array = typed.data.join(', ');
            return `[${array}]`;
        }
        prismComplete() {
            const { renderBody, renderTokens } = this;
            const element = this.shadowRoot?.querySelector('#rawCode');
            if (!element) {
                return;
            }
            let body = '';
            if (typeof renderBody === 'string') {
                body = renderBody;
            }
            else if (typeof renderTokens === 'string') {
                body = renderTokens;
            }
            const env = {
                code: body,
                element,
            };
            try {
                // @ts-expect-error There are not types for Prism
                Prism.hooks.run('complete', env);
                // @ts-expect-error There are not types for Prism
                Prism.plugins.lineNumbers.resize(element.parentElement);
            }
            catch {
                // ...
            }
        }
        /**
         * A handler for the image error event.
         * This occurs when rendering an image via the img tag but the image URL is invalid.
         */
        handleImageError() {
            this.viewType = ViewType.ImageError;
        }
        render() {
            const { renderBody: body, bodyType: type, viewType: view } = this;
            if (type === BodyType.nil) {
                return html `<p class="no-info">No data available.</p>`;
            }
            if (view === ViewType.ImageError) {
                return this.renderImageError();
            }
            if (view === ViewType.Parsed) {
                return this.renderStringBody();
            }
            if (view === ViewType.ImageSvg) {
                return this.renderImageSvgBody();
            }
            if (view === ViewType.ImageBinary) {
                return this.renderImageBinaryBody();
            }
            if (view === ViewType.Pdf) {
                return this.renderPdfBody();
            }
            if (view === ViewType.UrlEncoded) {
                return this.renderUrlEncodedBody();
            }
            if (type === BodyType.Blob || type === BodyType.File) {
                return this.renderBlobBody(body);
            }
            // only buffers left at this point
            return this.renderBufferBody(body);
        }
        renderStringBody() {
            const { raw, renderTokens } = this;
            const content = raw ? renderTokens : unsafeHTML(renderTokens);
            return html `
      <pre
        class="raw-panel string language-snippet line-numbers"
      ><code id="rawCode" class="code">${content}</code></pre>
    `;
        }
        renderBlobBody(body) {
            const { renderTokens, raw } = this;
            if (renderTokens && raw) {
                return html ` <div class="raw-panel padded-panel">${renderTokens}</div> `;
            }
            return html ` <div class="raw-panel padded-panel">File data of ${body.size} bytes.</div> `;
        }
        renderBufferBody(body) {
            const { renderTokens, raw } = this;
            if (renderTokens && raw) {
                return html ` <div class="raw-panel padded-panel">${renderTokens}</div> `;
            }
            return html ` <div class="raw-panel padded-panel">Binary data of ${body.byteLength} bytes.</div> `;
        }
        renderPdfBody() {
            return html `
      <div class="content-info pdf padded-panel">
        <p>The response is a <b>PDF</b> data.</p>
        <p>Save the file to preview its contents.</p>
      </div>
    `;
        }
        renderImageSvgBody() {
            const { renderBody, raw } = this;
            if (raw) {
                return this.renderStringBody();
            }
            const div = window.document.createElement('div');
            div.innerHTML = String(renderBody);
            const svgEl = div.firstElementChild;
            if (!svgEl) {
                return this.renderError('Invalid SVG response.');
            }
            const all = Array.from(svgEl.querySelectorAll('*'));
            all.forEach((node) => {
                if (!node.attributes) {
                    return;
                }
                const remove = Array.from(node.attributes).filter((attr) => attr.name.startsWith('on') || ['xlink:href'].includes(attr.name));
                remove.forEach(({ name }) => node.removeAttribute(name));
            });
            const scripts = Array.from(svgEl.getElementsByTagName('script'));
            scripts.forEach((node) => node.parentNode.removeChild(node));
            return html ` <div class="image-container">${unsafeSVG(svgEl.outerHTML)}</div> `;
        }
        renderImageBinaryBody() {
            const { renderTokens } = this;
            if (!renderTokens) {
                return this.renderNoData('No image data');
            }
            return html ` <div class="image-container">
      <img class="img-preview" src="${renderTokens}" alt="" @error="${this.handleImageError}" />
    </div>`;
        }
        renderImageError() {
            const { renderTokens } = this;
            if (!renderTokens) {
                return this.renderNoData('No image data');
            }
            return html ` <div class="error-container padded-panel"><b>The image URL is invalid</b>: "${renderTokens}"</div>`;
        }
        renderNoData(label = 'Unsupported view.') {
            return html `<p class="no-info">${label}</p>`;
        }
        renderError(message) {
            return html `<p class="error">${message}</p>`;
        }
        renderUrlEncodedBody() {
            const { renderBody, raw } = this;
            if (raw) {
                return this.renderStringBody();
            }
            let params;
            try {
                params = new URLSearchParams(renderBody);
            }
            catch {
                return this.renderError(`Unable to parse URL encoded data.`);
            }
            const content = [];
            for (const [name, value] of params) {
                content.push(html `
        <tr>
          <td class="name">${name}</td>
          <td class="value">${value}</td>
        </tr>
      `);
            }
            return html `
      <table class="url-params">
        <caption>
          URL encoded parameters
        </caption>
        <thead>
          <tr>
            <th scope="col" class="name">Name</th>
            <th scope="col" class="value">Value</th>
          </tr>
        </thead>
        <tbody>
          ${content}
        </tbody>
      </table>
    `;
        }
        constructor() {
            super(...arguments);
            __runInitializers(this, _renderTokens_extraInitializers);
        }
    };
})();
/**
 * Renders the view of the payload message.
 */
export default LogBody;
//# sourceMappingURL=LogBody.js.map