'use strict';

var index = require('./index-BjHIBY-I.js');
var translations = require('./translations-B0hzD08N.js');

/**
 * Checks whether the given HTML contains any images with a `data-remote-src`
 * attribute, indicating they reference external (remote) resources.
 *
 * @param html - The HTML string to inspect.
 */
function containsRemoteImages(html) {
    const parser = new DOMParser();
    const document = parser.parseFromString(html, 'text/html');
    return document.querySelector('img[data-remote-src]') !== null;
}
/**
 * If `allowRemoteImages` is `true`, replaces the `src` attribute of every
 * `<img data-remote-src="...">` element with the value of `data-remote-src`
 * (provided it points to an http(s) URL) and removes the data attribute.
 *
 * When `allowRemoteImages` is `false` the HTML is returned unchanged.
 *
 * Returns an HTML fragment that is safe to assign to `innerHTML` on a regular
 * container element (not a full `<html>/<head>/<body>` document string).
 *
 * @param html - The HTML string to process.
 * @param allowRemoteImages - Whether to restore remote image sources.
 */
function applyRemoteImagesPolicy(html, allowRemoteImages) {
    if (!allowRemoteImages) {
        return html;
    }
    const parser = new DOMParser();
    const document = parser.parseFromString(html, 'text/html');
    const images = document.querySelectorAll('img[data-remote-src]');
    for (const image of images) {
        const remoteSrc = image.dataset.remoteSrc;
        if (!remoteSrc || !isAllowedRemoteImageUrl(remoteSrc)) {
            delete image.dataset.remoteSrc;
            continue;
        }
        image.setAttribute('src', remoteSrc);
        delete image.dataset.remoteSrc;
    }
    const headStyles = [...document.head.querySelectorAll('style')]
        .map((style) => style.outerHTML)
        .join('');
    return `${headStyles}${document.body.innerHTML}`;
}
function isAllowedRemoteImageUrl(url) {
    const trimmed = url.trim();
    const lower = trimmed.toLowerCase();
    return lower.startsWith('https://') || lower.startsWith('http://');
}

/**
 * Splits a comma-separated email address list (e.g. `To:` / `Cc:`) into individual
 * recipient strings.
 *
 * In RFC 5322, address lists are comma-separated. However, commas can also appear
 * inside quoted display names (quoted-string) and must then be ignored as separators.
 * This splitter only treats a comma as a separator when it is outside quoted strings
 * and outside angle-bracketed address parts (`<...>`).
 *
 * Notes:
 * - If a display name contains a comma, it should be quoted or encoded to be
 *   unambiguous, e.g. `"Doe, Jane" <jane.doe@example.com>` or
 *   `=?UTF-8?Q?Doe,_Jane?= <jane.doe@example.com>`.
 * - Real-world `.eml` files are usually RFC-ish but not always perfectly compliant.
 *   Malformed input with unquoted commas in display names may be split incorrectly.
 *
 * @param value - A comma-separated list of recipients.
 * @returns An array of trimmed recipient strings.
 *
 * @example
 * splitEmailAddressList('"Doe, Jane" <jane@example.com>, Team <team@example.com>');
 * // => ['"Doe, Jane" <jane@example.com>', 'Team <team@example.com>']
 */
function splitEmailAddressList(value) {
    const parts = [];
    let current = '';
    const state = {
        inQuotes: false,
        escapeNext: false,
        angleDepth: 0,
    };
    const append = (character) => {
        current += character;
    };
    const flush = () => {
        const trimmed = current.trim();
        if (trimmed) {
            parts.push(trimmed);
        }
        current = '';
    };
    for (const character of value) {
        if (consumeEscaped(character, state, append)) {
            continue;
        }
        if (beginEscape(character, state, append)) {
            continue;
        }
        if (toggleQuote(character, state, append)) {
            continue;
        }
        if (adjustAngleDepth(character, state, append)) {
            continue;
        }
        if (isAddressSeparator(character, state)) {
            flush();
            continue;
        }
        append(character);
    }
    flush();
    return parts;
}
function consumeEscaped(character, state, append) {
    if (!state.escapeNext) {
        return false;
    }
    append(character);
    state.escapeNext = false;
    return true;
}
function beginEscape(character, state, append) {
    if (!state.inQuotes || character !== '\\') {
        return false;
    }
    append(character);
    state.escapeNext = true;
    return true;
}
function toggleQuote(character, state, append) {
    if (character !== '"' || state.angleDepth !== 0) {
        return false;
    }
    append(character);
    state.inQuotes = !state.inQuotes;
    return true;
}
function adjustAngleDepth(character, state, append) {
    if (state.inQuotes) {
        return false;
    }
    if (character === '<') {
        append(character);
        state.angleDepth += 1;
        return true;
    }
    if (character !== '>' || state.angleDepth === 0) {
        return false;
    }
    append(character);
    state.angleDepth -= 1;
    return true;
}
function isAddressSeparator(character, state) {
    return character === ',' && !state.inQuotes && state.angleDepth === 0;
}

/**
 * Format a file size in bytes into a human readable string.
 *
 * Uses base 1024 units (binary prefixes without the "i" designation)
 * and applies adaptive precision: one decimal for values < 10 of the
 * chosen unit, otherwise no decimals.
 *
 * Examples:
 *  - 0 => "0 B"
 *  - 512 => "512 B"
 *  - 1536 => "1.5 KB"
 *  - 1048576 => "1 MB"
 *  - 5368709120 => "5 GB"
 *  - formatBytes(5347737600, 2) => "4.98 GB" (value < 10 so two decimals)
 *
 * @param bytes - the size in bytes
 * @param decimals - max number of decimals for small unit values (default: 1)
 * @returns formatted size string
 */
function formatBytes(bytes, decimals = 1) {
    if (bytes == null || Number.isNaN(bytes)) {
        return '';
    }
    if (bytes < 0) {
        return '';
    }
    if (bytes === 0) {
        return '0 B';
    }
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const i = Math.max(0, Math.min(sizes.length - 1, Math.floor(Math.log(bytes) / Math.log(k))));
    const value = bytes / Math.pow(k, i);
    const safeDecimals = Number.isFinite(decimals) ? Math.trunc(decimals) : 0;
    const precision = value < 10 ? Math.min(100, Math.max(0, safeDecimals)) : 0; // only keep decimals for small values
    const rounded = Number.parseFloat(value.toFixed(precision));
    return `${rounded} ${sizes[i]}`;
}

const emailViewerCss = () => `@charset "UTF-8";:host(limel-email-viewer){display:block;width:100%;height:100%;box-sizing:border-box}*,*::before,*::after{box-sizing:border-box;min-width:0;min-height:0}.email{display:flex;flex-direction:column;width:100%;height:100%;padding-bottom:0.5rem;box-shadow:var(--shadow-depth-8)}.email-headers{position:relative;flex-shrink:0;display:flex;flex-direction:column}.email-headers dl,.email-headers dt,.email-headers dd{margin:0}.email-headers dl{display:flex;flex-wrap:wrap;gap:0 0.5rem;padding:0.5rem 0.75rem;font-size:0.75rem}.email-headers dl:nth-child(even){background-color:rgb(var(--contrast-800), 0.1)}.email-headers dl dt{opacity:0.6;min-width:3rem}.email-headers dl dt::after{content:":"}.email-headers dl dd:not(:last-child)::after{content:",";opacity:0.6}.email-headers dl.subject dd{font-weight:bold}.email-headers dl.date{position:absolute;right:0.25rem;transform:translateY(-50%);font-size:0.625rem;border-radius:9rem;padding:0.25rem 0.5rem;background-color:rgb(var(--contrast-100), 0.3)}.email-headers dl.date dt{position:absolute;width:0;height:0;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0, 0, 0, 0);clip-path:inset(50%);white-space:nowrap}.attachments{flex-shrink:0;padding:0.5rem;border-bottom:1px dashed rgba(var(--contrast-700))}.attachments span{padding-left:0.25rem;font-size:0.75rem;opacity:0.6}.attachments span:first-child::after{content:":"}.attachments ul{all:unset;display:grid;grid-template-columns:repeat(auto-fill, minmax(8rem, 1fr));gap:0.5rem;padding:0.5rem 0}.attachments li{all:unset;position:relative;display:flex;flex-direction:column;gap:0.25rem;font-size:0.6875rem;line-height:normal;padding:0.5rem 0.5rem 1rem 0.5rem;border-radius:0.5rem;border:1px solid rgba(var(--contrast-600));background-color:rgba(var(--contrast-400))}.attachments .attachment-filename{font-weight:500}.attachments .attachment-mime-type{opacity:0.7}.attachments limel-badge{--badge-max-width:auto;--badge-background-color:rgb(var(--contrast-1000), 0.7);--badge-text-color:rgb(var(--color-white));position:absolute;bottom:0.125rem;right:0.125rem;box-shadow:var(--shadow-brighten-edges-outside)}section{flex-grow:1;display:flex;flex-direction:column;border-top:1px dashed rgba(var(--contrast-700));min-height:2rem;overflow-y:auto}limel-collapsible-section{--closed-header-background-color:var(       --lime-elevated-surface-background-color   );flex-grow:1;flex-shrink:0;margin:0.5rem;border-radius:0.75rem;box-shadow:var(--shadow-depth-8)}limel-collapsible-section button{all:unset;flex-shrink:0;border-radius:0.375rem;padding:0.25rem 0.5rem;font-size:var(--limel-theme-small-font-size);margin:0 0.5rem}limel-collapsible-section button.load-images{transition:color var(--limel-clickable-transition-speed, 0.4s) ease, background-color var(--limel-clickable-transition-speed, 0.4s) ease, box-shadow var(--limel-clickable-transform-speed, 0.4s) ease, transform var(--limel-clickable-transform-speed, 0.4s) var(--limel-clickable-transform-timing-function, ease);cursor:pointer;color:var(--lime-primary-color, var(--limel-theme-primary-color));background-color:var(--lime-elevated-surface-background-color);box-shadow:var(--button-shadow-normal)}limel-collapsible-section button.load-images:hover,limel-collapsible-section button.load-images:focus,limel-collapsible-section button.load-images:focus-visible{will-change:color, background-color, box-shadow, transform}limel-collapsible-section button.load-images:hover,limel-collapsible-section button.load-images:focus-visible{transform:translate3d(0, -0.04rem, 0);color:var(--limel-theme-on-surface-color);background-color:var(--lime-elevated-surface-background-color);box-shadow:var(--button-shadow-hovered)}limel-collapsible-section button.load-images:active{--limel-clickable-transform-timing-function:cubic-bezier(       0.83,       -0.15,       0.49,       1.16   );transform:translate3d(0, 0.05rem, 0);box-shadow:var(--button-shadow-pressed)}limel-collapsible-section button.load-images:hover,limel-collapsible-section button.load-images:active{--limel-clickable-transition-speed:0.2s;--limel-clickable-transform-speed:0.16s}limel-collapsible-section limel-markdown{padding:0.5rem}.body{flex-grow:1;max-width:100%;padding:0.75rem}.body.plain-text{white-space:pre-wrap;overflow-wrap:anywhere;margin:0;font-family:inherit}.body img{max-width:100% !important}`;

const EmailViewer = class {
    constructor(hostRef) {
        index.registerInstance(this, hostRef);
        this.allowRemoteImagesChange = index.createEvent(this, "allowRemoteImagesChange");
        /**
         * Defines the localization for translations.
         */
        this.language = 'en';
        this.allowRemoteImagesState = false;
        this.renderAttachment = (attachment, index$1) => {
            var _a, _b;
            const filename = ((_a = attachment.filename) === null || _a === void 0 ? void 0 : _a.trim()) ||
                this.getTranslation('file-viewer.email.attachment.unnamed');
            const mimeType = ((_b = attachment.mimeType) === null || _b === void 0 ? void 0 : _b.trim()) || '';
            return (index.h("li", { key: `attachment-${index$1}` }, index.h("span", { class: "attachment-filename" }, filename), index.h("span", { class: "attachment-mime-type" }, " ", mimeType), this.renderSizeBadge(attachment.size)));
        };
        this.renderSizeBadge = (size) => {
            if (typeof size !== 'number') {
                return;
            }
            return (index.h("limel-badge", { class: "attachment-size", label: formatBytes(size) }));
        };
        this.onEnableRemoteImagesClick = (event) => {
            var _a;
            (_a = event === null || event === void 0 ? void 0 : event.stopPropagation) === null || _a === void 0 ? void 0 : _a.call(event);
            this.enableRemoteImages();
        };
        this.enableRemoteImages = () => {
            if (this.allowRemoteImages !== undefined) {
                this.allowRemoteImagesChange.emit(true);
                return;
            }
            this.allowRemoteImagesState = true;
        };
    }
    resetAllowRemoteImages(newEmail, oldEmail) {
        if (!newEmail) {
            this.allowRemoteImagesState = false;
            return;
        }
        if (newEmail.from !== (oldEmail === null || oldEmail === void 0 ? void 0 : oldEmail.from)) {
            this.allowRemoteImagesState = false;
        }
    }
    render() {
        return (index.h(index.Host, { key: 'a004af12989a477f4a9404ee86e0429bc5c2d427' }, index.h("div", { key: 'fc3bdeb81d823ff1cd39b4f6d52b4a88d9326392', class: "email", part: "email" }, this.renderHeaders(), this.renderRemoteImageBanner(), index.h("section", { key: '6fdd1cfe50cc4b41f7e70413541dc5950d7c44fa' }, this.renderAttachments(), this.renderBody()))));
    }
    renderHeaders() {
        const headerFields = [
            'subject',
            'from',
            'to',
            'cc',
            'date',
        ];
        return (index.h("div", { class: "email-headers", part: "email-headers" }, headerFields.map((type) => {
            var _a;
            return this.renderEmailHeader(type, this.getTranslation(`file-viewer.email.${type}`), (_a = this.email) === null || _a === void 0 ? void 0 : _a[type]);
        })));
    }
    renderBody() {
        return (this.renderBodyHtml() ||
            this.renderBodyText() ||
            this.renderFallbackUrl() || index.h("slot", { name: "fallback" }));
    }
    renderBodyHtml() {
        var _a;
        const bodyHtml = (_a = this.email) === null || _a === void 0 ? void 0 : _a.bodyHtml;
        if (!bodyHtml) {
            return;
        }
        const innerHtml = applyRemoteImagesPolicy(bodyHtml, this.getAllowRemoteImages());
        return index.h("div", { class: "body", innerHTML: innerHtml, part: "email-body" });
    }
    renderBodyText() {
        var _a;
        const bodyText = (_a = this.email) === null || _a === void 0 ? void 0 : _a.bodyText;
        if (!bodyText) {
            return;
        }
        return (index.h("pre", { class: "body plain-text", part: "email-body" }, bodyText));
    }
    renderFallbackUrl() {
        if (!this.fallbackUrl) {
            return;
        }
        return (index.h("object", { data: this.fallbackUrl, type: "text/plain" }, index.h("slot", { name: "fallback" })));
    }
    renderEmailHeader(type, label, value) {
        if (!value) {
            return;
        }
        const values = this.getHeaderValues(type, value);
        return (index.h("dl", { class: `headers ${type}` }, index.h("dt", null, label), values.map((headerValue, index$1) => (index.h("dd", { key: `${type}-${index$1}` }, headerValue)))));
    }
    getHeaderValues(type, value) {
        if (type === 'to' || type === 'cc') {
            return splitEmailAddressList(value);
        }
        return [value];
    }
    renderAttachments() {
        var _a;
        const attachments = (_a = this.email) === null || _a === void 0 ? void 0 : _a.attachments;
        if (!(attachments === null || attachments === void 0 ? void 0 : attachments.length)) {
            return;
        }
        const label = this.getTranslation('file-viewer.email.attachments');
        return (index.h("div", { class: "attachments" }, index.h("span", null, label), index.h("ul", null, attachments.map((attachment, index) => this.renderAttachment(attachment, index)))));
    }
    getTranslation(key) {
        return translations.translate.get(key, this.language);
    }
    shouldShowRemoteImagesBanner() {
        var _a;
        const bodyHtml = (_a = this.email) === null || _a === void 0 ? void 0 : _a.bodyHtml;
        if (!bodyHtml || this.getAllowRemoteImages()) {
            return false;
        }
        return containsRemoteImages(bodyHtml);
    }
    renderRemoteImageBanner() {
        if (!this.shouldShowRemoteImagesBanner()) {
            return;
        }
        const icon = {
            name: 'warning_shield',
            color: 'rgb(var(--color-orange-default))',
        };
        const heading = this.getTranslation('file-viewer.email.remote-images.warning');
        const description = this.getTranslation('file-viewer.email.remote-images.warning.description');
        const buttonLabel = this.getTranslation('file-viewer.email.remote-images.load');
        return (index.h("limel-collapsible-section", { header: heading, icon: icon, language: this.language }, index.h("button", { type: "button", class: "load-images", slot: "header", onClick: this.onEnableRemoteImagesClick }, buttonLabel), index.h("limel-markdown", { value: description })));
    }
    getAllowRemoteImages() {
        var _a;
        return (_a = this.allowRemoteImages) !== null && _a !== void 0 ? _a : this.allowRemoteImagesState;
    }
    static get watchers() { return {
        "email": [{
                "resetAllowRemoteImages": 0
            }]
    }; }
};
EmailViewer.style = emailViewerCss();

exports.limel_email_viewer = EmailViewer;
