
        function _escapeCommentHtml(value) {
            return String(value || '')
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;');
        }

        function _removeQuotedMailLines(text) {
            const source = String(text || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
            const lines = source.split('\n');
            const kept = [];

            const headerRe = /^(fra|from|sent|date|dato|to|til|emne|subject|cc):\s*/i;
            const originalMessageRe = /^(original message|oprindelig besked|videresendt besked)/i;

            for (let i = 0; i < lines.length; i += 1) {
                const line = lines[i];
                const trimmed = line.trim();

                if (trimmed.startsWith('>')) break;
                if (originalMessageRe.test(trimmed)) break;

                if (/^[-_]{3,}$/.test(trimmed)) {
                    const lookahead = lines.slice(i + 1, i + 4);
                    if (lookahead.some((candidate) => headerRe.test(String(candidate || '').trim()))) {
                        break;
                    }
                }

                if (i > 0 && headerRe.test(trimmed) && String(lines[i - 1] || '').trim() === '') {
                    break;
                }

                kept.push(line);
            }

            while (kept.length > 0 && String(kept[kept.length - 1] || '').trim() === '') {
                kept.pop();
            }

            return kept.join('\n').trim();
        }

        function _parseEmailComment(rawText) {
            const normalized = String(rawText || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
            const emailIdMatch = normalized.match(/^Email-ID:\s*(\d+)\s*$/m);
            const emailId = emailIdMatch ? Number(emailIdMatch[1]) : null;
            const withoutMeta = normalized.replace(/^Email-ID:\s*\d+\s*\n?/m, '').trim();
            return {
                emailId,
                visibleText: _removeQuotedMailLines(withoutMeta)
            };
        }

        function _formatEmailHeaderTimestamp(value) {
            if (!value) return '';
            const parsed = new Date(value);
            if (Number.isNaN(parsed.getTime())) return String(value);
            return parsed.toLocaleString('da-DK');
        }

        function _buildEmailHeaderAndBody(visibleText) {
            const text = String(visibleText || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim();
            const lines = text.split('\n');

            let idx = 0;
            let typeLabel = 'Indgaaende email';
            const firstLine = String(lines[0] || '').trim();
            if (/^📧\s*Indgående email/i.test(firstLine)) {
                typeLabel = 'Indgaaende email';
                idx = 1;
            } else if (/^📧\s*Udgående email/i.test(firstLine)) {
                typeLabel = 'Udgaaende email';
                idx = 1;
            }

            let fra = '';
            let til = '';
            let cc = '';
            let emne = '';
            let modtaget = '';

            while (idx < lines.length) {
                const line = String(lines[idx] || '').trim();
                if (!line) {
                    idx += 1;
                    break;
                }
                if (/^Fra:\s*/i.test(line)) fra = line.replace(/^Fra:\s*/i, '').trim();
                else if (/^Til:\s*/i.test(line)) til = line.replace(/^Til:\s*/i, '').trim();
                else if (/^Cc:\s*/i.test(line)) cc = line.replace(/^Cc:\s*/i, '').trim();
                else if (/^Emne:\s*/i.test(line)) emne = line.replace(/^Emne:\s*/i, '').trim();
                else if (/^Modtaget:\s*/i.test(line)) modtaget = line.replace(/^Modtaget:\s*/i, '').trim();
                else break;
                idx += 1;
            }

            const bodyText = lines.slice(idx).join('\n').trim();
            const summaryParts = [typeLabel];
            if (fra) summaryParts.push(`Fra: ${fra}`);
            if (til) summaryParts.push(`Til: ${til}`);
            if (cc) summaryParts.push(`Cc: ${cc}`);
            if (emne) summaryParts.push(`Emne: ${emne}`);
            if (modtaget) summaryParts.push(`Modtaget: ${_formatEmailHeaderTimestamp(modtaget)}`);

            return {
                summary: summaryParts.join('  •  '),
                bodyText
            };
        }

        function _extractEmailHeaderFields(visibleText) {
            const text = String(visibleText || '').replace(/\r\n/g, '\n').replace(/\r/g, '\n').trim();
            const lines = text.split('\n');
            let idx = 0;

            const firstLine = String(lines[0] || '').trim();
            const isOutgoing = /^📧\s*Udgående email/i.test(firstLine);
            if (/^📧\s*(Indgående|Udgående)\s+email/i.test(firstLine)) {
                idx = 1;
            }

            let fra = '';
            let til = '';
            let emne = '';
            let modtaget = '';

            while (idx < lines.length) {
                const line = String(lines[idx] || '').trim();
                if (!line) break;
                if (/^Fra:\s*/i.test(line)) fra = line.replace(/^Fra:\s*/i, '').trim();
                else if (/^Til:\s*/i.test(line)) til = line.replace(/^Til:\s*/i, '').trim();
                else if (/^Emne:\s*/i.test(line)) emne = line.replace(/^Emne:\s*/i, '').trim();
                else if (/^Modtaget:\s*/i.test(line)) modtaget = line.replace(/^Modtaget:\s*/i, '').trim();
                else break;
                idx += 1;
            }

            return { fra, til, emne, modtaget, isOutgoing };
        }

        function _normalizeReplySubject(value) {
            const subject = String(value || '').trim();
            return subject.replace(/^(re|fw|fwd)\s*:\s*/ig, '').toLowerCase();
        }

        function _findBestLinkedEmailByHeader(header) {
            const targetSubject = _normalizeReplySubject(header?.emne || '');
            const targetFrom = String(header?.fra || '').trim().toLowerCase();
            const targetTo = String(header?.til || '').trim().toLowerCase();

            const candidates = (linkedEmailsCache || []).filter((email) => {
                const emailSubject = _normalizeReplySubject(email?.subject || '');
                if (targetSubject && emailSubject !== targetSubject) {
                    return false;
                }

                const sender = String(email?.sender_email || email?.sender_name || '').toLowerCase();
                const recipient = String(email?.recipient_email || '').toLowerCase();

                if (targetFrom && sender && sender.includes(targetFrom)) {
                    return true;
                }
                if (targetTo && recipient && recipient.includes(targetTo)) {
                    return true;
                }

                return !targetFrom && !targetTo;
            });

            if (!candidates.length) {
                return null;
            }

            candidates.sort((a, b) => {
                const aTs = a?.received_date ? new Date(a.received_date).getTime() : 0;
                const bTs = b?.received_date ? new Date(b.received_date).getTime() : 0;
                return bTs - aTs;
            });

            return Number(candidates[0]?.id) || null;
        }

        function _extractEmailAddress(value) {
            const raw = String(value || '').trim();
            const match = raw.match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i);
            return match ? match[0] : raw;
        }

        function _commentInitials(name) {
            const clean = String(name || '').trim();
            if (!clean) return 'EM';
            const parts = clean.split(/\s+/).filter(Boolean);
            if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
            return `${parts[0][0] || ''}${parts[1][0] || ''}`.toUpperCase();
        }

        function _formatCommentTime(value) {
            const parsed = new Date(value || Date.now());
            if (Number.isNaN(parsed.getTime())) return '';
            const pad = (n) => String(n).padStart(2, '0');
            return `${pad(parsed.getDate())}/${pad(parsed.getMonth() + 1)}-${parsed.getFullYear()} ${pad(parsed.getHours())}:${pad(parsed.getMinutes())}`;
        }

        function _refreshCommentCountBadge() {
            const container = document.getElementById('comments-container');
            const badge = document.querySelector('#beskrivelse-comments-wrap .badge.bg-secondary');
            if (!container || !badge) return;
            badge.textContent = String(container.querySelectorAll('.comment-item').length);
        }

        function prependCommentToThread(comment) {
            const container = document.getElementById('comments-container');
            if (!container || !comment || !comment.indhold) return;

            const emptyState = container.querySelector('p.text-center.text-muted.my-3');
            if (emptyState) emptyState.remove();

            const author = String(comment.forfatter || 'Email Bot');
            const createdAtIso = String(comment.created_at || new Date().toISOString());
            const createdAtMs = new Date(createdAtIso).getTime();
            const createdAtUnix = Number.isFinite(createdAtMs) ? Math.floor(createdAtMs / 1000) : Math.floor(Date.now() / 1000);

            const item = document.createElement('div');
            item.className = 'comment-item comment-system';
            item.dataset.createdAt = String(createdAtUnix);

            const meta = document.createElement('div');
            meta.className = 'comment-meta';
            meta.innerHTML = `
                <span class="comment-avatar">${_escapeCommentHtml(_commentInitials(author))}</span>
                <b>${_escapeCommentHtml(author)}</b>
                <span class="comment-time">${_escapeCommentHtml(_formatCommentTime(createdAtIso))}</span>
            `;

            const body = document.createElement('div');
            body.className = 'comment-body';
            body.setAttribute('data-comment-raw', String(comment.indhold));
            body.textContent = String(comment.indhold);

            item.appendChild(meta);
            item.appendChild(body);
            container.insertBefore(item, container.firstChild);

            processCommentBodies();
            sortCommentsNewestFirst();
            _refreshCommentCountBadge();
        }

        let activeCommentQuickReply = null;

        window.closeInlineCommentQuickReply = function() {
            const host = document.getElementById('comment-quick-reply-host');
            if (host) host.innerHTML = '';
            activeCommentQuickReply = null;
        }

        window.sendInlineCommentQuickReply = async function() {
            const host = document.getElementById('comment-quick-reply-host');
            const textarea = document.getElementById('commentQuickReplyText');
            const sendBtn = document.getElementById('commentQuickReplySendBtn');
            const statusEl = document.getElementById('commentQuickReplyStatus');
            if (!host || !textarea || !sendBtn || !statusEl || !activeCommentQuickReply) return;

            const bodyText = String(textarea.value || '').trim();
            if (!bodyText) {
                statusEl.className = 'comment-quick-reply-status text-danger';
                statusEl.textContent = 'Skriv et svar';
                return;
            }

            const recipient = _extractEmailAddress(activeCommentQuickReply.recipient);
            if (!recipient || recipient.indexOf('@') === -1) {
                statusEl.className = 'comment-quick-reply-status text-danger';
                statusEl.textContent = 'Ingen gyldig modtager fundet i kommentaren';
                return;
            }

            sendBtn.disabled = true;
            statusEl.className = 'comment-quick-reply-status';
            statusEl.textContent = 'Sender...';

            try {
                await loadLinkedEmails();

                let threadEmailId = Number(activeCommentQuickReply.emailId) || null;
                if (!threadEmailId) {
                    threadEmailId = _findBestLinkedEmailByHeader(activeCommentQuickReply.header);
                }

                let threadKey = null;
                if (threadEmailId) {
                    const linked = linkedEmailsCache.find((entry) => Number(entry.id) === Number(threadEmailId));
                    threadKey = linked?.thread_key || linked?.resolved_thread_key || null;
                }

                const response = await fetch(`/api/v1/sag/${caseIds}/emails/send`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        to: [recipient],
                        subject: activeCommentQuickReply.subject,
                        body_text: bodyText,
                        thread_email_id: threadEmailId,
                        thread_key: threadKey
                    })
                });

                if (!response.ok) {
                    let message = `HTTP ${response.status}`;
                    try {
                        const payload = await response.json();
                        message = payload?.detail || payload?.message || message;
                    } catch (_) {
                    }
                    throw new Error(message);
                }

                const result = await response.json();
                if (result?.comment) {
                    prependCommentToThread(result.comment);
                }

                statusEl.className = 'comment-quick-reply-status text-success';
                statusEl.textContent = 'Svar sendt';
                textarea.value = '';
                await loadLinkedEmails();
                setTimeout(() => {
                    window.closeInlineCommentQuickReply();
                }, 500);
            } catch (error) {
                statusEl.className = 'comment-quick-reply-status text-danger';
                statusEl.textContent = error?.message || 'Kunne ikke sende svar';
            } finally {
                sendBtn.disabled = false;
            }
        }

        function openInlineCommentQuickReply(rawText, emailId) {
            const host = document.getElementById('comment-quick-reply-host');
            if (!host) return;

            const parsed = _parseEmailComment(rawText || '');
            const header = _extractEmailHeaderFields(parsed.visibleText || '');
            const fallbackRecipient = header.isOutgoing ? (header.til || header.fra) : (header.fra || header.til);
            const subject = /^re:\s*/i.test(header.emne || '')
                ? (header.emne || `Sag #${caseIds}`)
                : `Re: ${header.emne || `Sag #${caseIds}`}`;

            activeCommentQuickReply = {
                rawText,
                header,
                emailId: Number(emailId) || parsed.emailId || null,
                recipient: fallbackRecipient,
                subject
            };

            host.innerHTML = `
                <div class="comment-quick-reply-box">
                    <div class="small text-muted mb-1">Quick svar til ${_escapeCommentHtml(String(fallbackRecipient || 'ukendt modtager'))}</div>
                    <textarea id="commentQuickReplyText" class="form-control" rows="2" placeholder="Skriv hurtigt svar..."></textarea>
                    <div class="comment-quick-reply-actions">
                        <div id="commentQuickReplyStatus" class="comment-quick-reply-status"></div>
                        <div class="d-flex gap-2">
                            <button type="button" class="btn btn-sm btn-outline-secondary" onclick="closeInlineCommentQuickReply()">Annuller</button>
                            <button type="button" class="btn btn-sm btn-primary" id="commentQuickReplySendBtn" onclick="sendInlineCommentQuickReply()"><i class="bi bi-send me-1"></i>Send</button>
                        </div>
                    </div>
                </div>
            `;

            const textarea = document.getElementById('commentQuickReplyText');
            if (textarea) {
                textarea.focus();
            }
        }

        async function quickReplyToEmailFromCommentText(rawText) {
            openCaseEmailTab();

            const parsed = _parseEmailComment(rawText || '');
            const header = _extractEmailHeaderFields(parsed.visibleText || '');

            try {
                await loadLinkedEmails();

                const matchedEmailId = _findBestLinkedEmailByHeader(header);
                if (matchedEmailId) {
                    await loadLinkedEmailDetail(matchedEmailId);
                    openReplyToLinkedEmail();
                    return;
                }
            } catch (error) {
                console.error('Kunne ikke finde trådmail fra kommentar:', error);
            }

            const composeModalEl = document.getElementById('caseEmailComposeModal');
            if (!composeModalEl) return;

            const toInput = document.getElementById('caseEmailTo');
            const subjectInput = document.getElementById('caseEmailSubject');
            const bodyInput = document.getElementById('caseEmailBody');

            const fallbackRecipient = (header.isOutgoing ? header.til : header.fra) || header.fra || header.til || '';
            if (toInput && !toInput.value.trim() && fallbackRecipient) {
                toInput.value = fallbackRecipient;
            }

            if (subjectInput && !subjectInput.value.trim()) {
                subjectInput.value = escapeHtmlForInput(
                    /^re:\s*/i.test(header.emne || '')
                        ? (header.emne || `Sag #${caseIds}`)
                        : `Re: ${header.emne || `Sag #${caseIds}`}`
                );
            }

            if (bodyInput && !bodyInput.value.trim()) {
                bodyInput.value = `\n\n---\nFra: ${header.fra || '-'}\nDato: ${header.modtaget || '-'}\nEmne: ${header.emne || '(Ingen emne)'}\n`;
            }

            bootstrap.Modal.getOrCreateInstance(composeModalEl).show();
        }

        async function openEmailFromComment(emailId) {
            const parsedId = Number(emailId);
            if (!Number.isFinite(parsedId)) return;

            if (typeof openCaseEmailTab === 'function') {
                openCaseEmailTab();
            }

            try {
                if (typeof loadLinkedEmails === 'function') {
                    await loadLinkedEmails();
                }
                if (typeof loadLinkedEmailDetail === 'function') {
                    await loadLinkedEmailDetail(parsedId);
                }
                const emailTabPane = document.getElementById('emails');
                if (emailTabPane) {
                    emailTabPane.scrollIntoView({ behavior: 'smooth', block: 'start' });
                }
            } catch (error) {
                console.error('Kunne ikke åbne email fra kommentar:', error);
            }
        }

        function processCommentBodies() {
            const commentItems = Array.from(document.querySelectorAll('#comments-container .comment-item'));
            commentItems.forEach((item) => {
                const body = item.querySelector('.comment-body');
                if (!body) return;

                const rawText = body.dataset.commentRaw || body.textContent || '';
                if (!item.classList.contains('comment-system')) {
                    body.innerHTML = _escapeCommentHtml(String(rawText)).replace(/\n/g, '<br>');
                    return;
                }

                const hasEmailHeader = /(^|\n)\s*📧\s*(Indgående|Udgående)\s+email/i.test(String(rawText));
                if (!hasEmailHeader) {
                    body.innerHTML = _escapeCommentHtml(String(rawText)).replace(/\n/g, '<br>');
                    return;
                }

                const parsed = _parseEmailComment(rawText);
                const display = _buildEmailHeaderAndBody(parsed.visibleText || '');
                const safeHeader = _escapeCommentHtml(display.summary || 'Indgaaende email');
                const safeBody = _escapeCommentHtml(display.bodyText || '').replace(/\n/g, '<br>');
                body.innerHTML = `
                    <div class="comment-email-header" title="${safeHeader}">${safeHeader}</div>
                    ${display.bodyText ? `<div class="comment-email-text">${safeBody}</div>` : ''}
                `;

                const existingActions = item.querySelector('.comment-actions');
                if (existingActions) {
                    existingActions.remove();
                }

                if (parsed.emailId) {
                    const actions = document.createElement('div');
                    actions.className = 'comment-actions';
                    actions.innerHTML = `
                        <button type="button" class="btn btn-link btn-sm" onclick="openEmailFromComment(${parsed.emailId})"><i class="bi bi-envelope-open me-1"></i>Aabn fuld mail</button>
                        <button type="button" class="btn btn-link btn-sm" onclick="quickReplyToEmailFromComment(${parsed.emailId})"><i class="bi bi-reply me-1"></i>Svar</button>
                        <button type="button" class="btn btn-link btn-sm js-quick-inline-reply"><i class="bi bi-lightning-charge me-1"></i>Quick svar</button>
                    `;
                    item.appendChild(actions);
                    const quickInlineBtn = actions.querySelector('.js-quick-inline-reply');
                    if (quickInlineBtn) {
                        quickInlineBtn.addEventListener('click', () => {
                            openInlineCommentQuickReply(rawText, parsed.emailId);
                        });
                    }
                } else {
                    const actions = document.createElement('div');
                    actions.className = 'comment-actions';
                    actions.innerHTML = `
                        <button type="button" class="btn btn-link btn-sm" onclick="openCaseEmailTab()"><i class="bi bi-envelope me-1"></i>Aabn email-fane</button>
                        <button type="button" class="btn btn-link btn-sm js-reply-fallback"><i class="bi bi-reply me-1"></i>Svar</button>
                        <button type="button" class="btn btn-link btn-sm js-quick-reply-fallback"><i class="bi bi-lightning-charge me-1"></i>Quick svar</button>
                    `;
                    item.appendChild(actions);
                    const replyBtn = actions.querySelector('.js-reply-fallback');
                    if (replyBtn) {
                        replyBtn.addEventListener('click', () => {
                            quickReplyToEmailFromCommentText(rawText);
                        });
                    }
                    const quickReplyBtn = actions.querySelector('.js-quick-reply-fallback');
                    if (quickReplyBtn) {
                        quickReplyBtn.addEventListener('click', () => {
                            openInlineCommentQuickReply(rawText, null);
                        });
                    }
                }
            });
        }

        function sortCommentsNewestFirst() {
            const container = document.getElementById('comments-container');
            if (!container) return;

            const items = Array.from(container.querySelectorAll('.comment-item'));
            if (items.length < 2) return;

            items
                .sort((a, b) => Number(b.dataset.createdAt || 0) - Number(a.dataset.createdAt || 0))
                .forEach((item) => container.appendChild(item));
        }

        async function submitComment(event) {
            event.preventDefault();
            const form = event.target;
            const content = form.indhold.value;
            const btn = form.querySelector('button');
            const originalText = btn.innerHTML;
            
            btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span> Sender...';
            btn.disabled = true;
            
            try {
                const response = await fetch('/api/v1/sag/{{ case.id }}/kommentarer', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        indhold: content
                    })
                });

                if (response.ok) {
                    location.reload();
                } else {
                    alert('Fejl ved oprettelse af kommentar');
                    btn.innerHTML = originalText;
                    btn.disabled = false;
                }
            } catch (error) {
                console.error('Error:', error);
                alert('Der skete en fejl. Prøv igen.');
                btn.innerHTML = originalText;
                btn.disabled = false;
            }
        }
        
        // Keep newest comments visible at top
        document.addEventListener('DOMContentLoaded', function() {
            sortCommentsNewestFirst();
            processCommentBodies();
            const container = document.getElementById('comments-container');
            if(container) {
                container.scrollTop = 0;
            }
        });
    