document.addEventListener('DOMContentLoaded', () => {
    const chatEl = document.getElementById('chat');
    const promptEl = document.getElementById('prompt');
    const sendBtn = document.getElementById('send');
    const abortBtn = document.getElementById('abort');
    const clearBtn = document.getElementById('clear');
    const aiCloudEl = document.getElementById('ai-cloud');
    const retriexCardsToggleEl = document.getElementById('toggle-retriex-cards');
    const LAST_TURN_STORAGE_KEY = 'retriex:lastCompletedTurn';
    const DETAIL_CARDS_STORAGE_KEY = 'retriex:showDetailCards';
    const JOB_STATUS_POLL_INTERVAL_MS = 2500;
    const JOB_COMPLETION_CATCHUP_GRACE_MS = 10000;
    const JOB_CLIENT_STALE_GRACE_MS = 150000;

    const state = {
        abortRequested: false,
        isStreaming: false,
        renderTimer: null,
        abortController: null,
        reader: null,
        eventSource: null,
        completeStream: null,
        failStream: null,
        lastCompletedUserPrompt: '',
        lastCompletedAssistantText: '',
    };

    marked.setOptions({breaks: true});

    function setRetriexDetailCardsVisible(isVisible, persist = true) {
        document.body.classList.toggle('retriex-show-detail-cards', isVisible);

        if (retriexCardsToggleEl) {
            retriexCardsToggleEl.checked = isVisible;
        }

        if (!persist) {
            return;
        }

        try {
            window.localStorage?.setItem(DETAIL_CARDS_STORAGE_KEY, isVisible ? '1' : '0');
        } catch (err) {
            console.debug('Could not persist detail card visibility:', err);
        }
    }

    function initRetriexDetailCardsToggle() {
        let isVisible = false;

        try {
            isVisible = window.localStorage?.getItem(DETAIL_CARDS_STORAGE_KEY) === '1';
        } catch (err) {
            console.debug('Could not read detail card visibility:', err);
        }

        setRetriexDetailCardsVisible(isVisible, false);

        retriexCardsToggleEl?.addEventListener('change', () => {
            setRetriexDetailCardsVisible(retriexCardsToggleEl.checked, true);
        });
    }

    initRetriexDetailCardsToggle();

    function renderMarkdown(text) {
        return DOMPurify.sanitize(marked.parse(text));
    }

    function normalizeContextHintText(value) {
        return String(value || '')
            .replace(/\r\n/g, '\n')
            .replace(/\r/g, '\n')
            .replace(/[\t ]+/g, ' ')
            .replace(/\n{3,}/g, '\n\n')
            .trim();
    }

    function tokenizeClientMetaGuardText(value) {
        return normalizeContextHintText(value)
            .toLowerCase()
            .replace(/[-/_]/g, ' ')
            .replace(/[^\p{L}\p{N}]+/gu, ' ')
            .trim()
            .split(/\s+/u)
            .filter(Boolean);
    }

    function extractAssistantContextText(bubble) {
        if (!bubble) {
            return '';
        }

        const clone = bubble.cloneNode(true);
        clone.querySelectorAll('.think, .retriex-meta-card, .retriex-alert').forEach((element) => {
            element.remove();
        });

        return normalizeContextHintText(clone.innerText || clone.textContent || '');
    }

    function isClientMetaOnlyShopPrompt(value) {
        const tokens = tokenizeClientMetaGuardText(value);

        if (!tokens.length) {
            return true;
        }

        const metaTerms = new Set([
            'shop', 'shopsuche', 'suche', 'suchen', 'such', 'finde', 'find',
            'zeige', 'zeig', 'bitte', 'mal', 'im', 'in', 'nach', 'den', 'die',
            'das', 'der', 'dem',
        ]);

        return tokens.every((token) => metaTerms.has(token));
    }

    function isNoConcreteShopResponse(value) {
        const normalized = normalizeContextHintText(value).toLowerCase();

        return normalized.includes('keine konkrete shop-suchanfrage erkannt')
            || normalized.includes('shop-suche noch nicht belastbar auflösen')
            || normalized.includes('shop-suche noch nicht belastbar aufloesen');
    }

    function rememberCompletedTurn(userPrompt, assistantText) {
        const normalizedPrompt = normalizeContextHintText(userPrompt);
        const normalizedAssistantText = normalizeContextHintText(assistantText);

        if (!normalizedPrompt) {
            return;
        }

        if (isClientMetaOnlyShopPrompt(normalizedPrompt) && isNoConcreteShopResponse(normalizedAssistantText)) {
            return;
        }

        state.lastCompletedUserPrompt = normalizedPrompt.slice(0, 800);
        state.lastCompletedAssistantText = normalizedAssistantText.slice(0, 3000);

        try {
            window.sessionStorage?.setItem(LAST_TURN_STORAGE_KEY, JSON.stringify({
                userPrompt: state.lastCompletedUserPrompt,
                assistantText: state.lastCompletedAssistantText,
                rememberedAt: Date.now(),
            }));
        } catch (err) {
            console.debug('Could not persist last completed turn:', err);
        }
    }

    function loadStoredCompletedTurn() {
        try {
            const raw = window.sessionStorage?.getItem(LAST_TURN_STORAGE_KEY) || '';

            if (!raw) {
                return null;
            }

            const data = JSON.parse(raw);
            const userPrompt = normalizeContextHintText(data?.userPrompt || '');
            const assistantText = normalizeContextHintText(data?.assistantText || '');

            if (!userPrompt) {
                return null;
            }

            return {
                userPrompt: userPrompt.slice(0, 800),
                assistantText: assistantText.slice(0, 3000),
            };
        } catch (err) {
            console.debug('Could not read last completed turn:', err);
            return null;
        }
    }

    function extractLatestVisibleCompletedTurn() {
        if (!chatEl) {
            return null;
        }

        const messages = Array.from(chatEl.querySelectorAll('.message'));
        let pendingUserPrompt = '';
        let latestTurn = null;

        messages.forEach((message) => {
            const bubble = message.querySelector('.bubble');
            const text = message.classList.contains('assistant')
                ? extractAssistantContextText(bubble)
                : normalizeContextHintText(bubble?.innerText || bubble?.textContent || '');

            if (!text) {
                return;
            }

            if (message.classList.contains('user')) {
                pendingUserPrompt = text;
                return;
            }

            if (!message.classList.contains('assistant') || !pendingUserPrompt) {
                return;
            }

            if (bubble?.classList.contains('loader')) {
                return;
            }

            if (isClientMetaOnlyShopPrompt(pendingUserPrompt) && isNoConcreteShopResponse(text)) {
                pendingUserPrompt = '';
                return;
            }

            latestTurn = {
                userPrompt: pendingUserPrompt,
                assistantText: text,
            };
            pendingUserPrompt = '';
        });

        return latestTurn;
    }

    function buildClientContextHint() {
        const visibleTurn = extractLatestVisibleCompletedTurn();
        const storedTurn = loadStoredCompletedTurn();
        const userPrompt = visibleTurn?.userPrompt || state.lastCompletedUserPrompt || storedTurn?.userPrompt || '';
        const assistantText = visibleTurn?.assistantText || state.lastCompletedAssistantText || storedTurn?.assistantText || '';

        if (!userPrompt) {
            return '';
        }

        const lines = [`Question: ${userPrompt.slice(0, 800)}`];

        if (assistantText) {
            lines.push(assistantText.slice(0, 3000));
        }

        return normalizeContextHintText(lines.join('\n')).slice(0, 4000);
    }

    function scrollChatToBottom() {
        if (!chatEl) {
            return;
        }

        chatEl.scrollTop = chatEl.scrollHeight;
    }

    function enhanceChatLinks(container = chatEl) {
        if (!container) {
            return;
        }

        container.querySelectorAll('a[href]').forEach((link) => {
            link.setAttribute('target', '_blank');
            link.setAttribute('rel', 'noopener noreferrer');
        });
    }

    function addMessage(role, html = '', extra = '') {
        const msg = document.createElement('div');
        msg.className = 'message ' + role;

        const bubble = document.createElement('div');
        bubble.className = 'bubble ' + extra;
        bubble.innerHTML = html;

        msg.appendChild(bubble);
        chatEl.appendChild(msg);

        enhanceChatLinks(bubble);
        scrollChatToBottom();

        return bubble;
    }

    function addLoader() {
        return addMessage('assistant', 'Antwort wird vorbereitet…', 'loader');
    }

    function hasMeaningfulChildContent(element) {
        return element.querySelector(
            'img, table, pre, code, ul, ol, h1, h2, h3, h4, h5, h6, a, hr, .badge, .retriex-meta-card, .retriex-alert, .retriex-action-chip'
        ) !== null;
    }

    function isWhitespaceTextNode(node) {
        return node.nodeType === Node.TEXT_NODE && (node.textContent || '').trim() === '';
    }

    function isBreakNode(node) {
        return node.nodeType === Node.ELEMENT_NODE && node.tagName === 'BR';
    }

    function trimEdgeBreaks(element) {
        while (element.firstChild && (isWhitespaceTextNode(element.firstChild) || isBreakNode(element.firstChild))) {
            element.firstChild.remove();
        }

        while (element.lastChild && (isWhitespaceTextNode(element.lastChild) || isBreakNode(element.lastChild))) {
            element.lastChild.remove();
        }
    }

    function cleanupEmptyBlocks(container) {
        container.querySelectorAll('p, div, li, blockquote').forEach((el) => {
            trimEdgeBreaks(el);

            const html = el.innerHTML
                .replace(/<br\s*\/?>/gi, '')
                .replace(/&nbsp;/gi, '')
                .trim();

            const text = (el.textContent || '').trim();

            if (html === '' && text === '') {
                el.remove();
                return;
            }

            if (text === '' && !hasMeaningfulChildContent(el)) {
                el.remove();
            }
        });

        trimEdgeBreaks(container);
    }

    function removeThinkSpansOnly(container) {
        container.querySelectorAll('.think').forEach((span) => {
            span.remove();
        });

        cleanupEmptyBlocks(container);
    }

    function cloneWithoutThinkContent(container) {
        const clone = container.cloneNode(true);
        clone.querySelectorAll('.think').forEach((span) => span.remove());
        cleanupEmptyBlocks(clone);
        return clone;
    }

    function hasNonThinkContent(container) {
        const clone = cloneWithoutThinkContent(container);

        if ((clone.textContent || '').trim() !== '') {
            return true;
        }

        return hasMeaningfulChildContent(clone);
    }

    function keepOnlyLastThink(container) {
        const thinkSpans = Array.from(container.querySelectorAll('.think'));

        if (thinkSpans.length <= 1) {
            cleanupEmptyBlocks(container);
            return;
        }

        const lastThink = thinkSpans[thinkSpans.length - 1];

        thinkSpans.slice(0, -1).forEach((span) => {
            span.remove();
        });

        const blockSelector = 'p, div, li, blockquote';
        const lastBlock = lastThink.closest(blockSelector) || lastThink.parentElement;

        if (lastBlock && lastThink.parentElement === lastBlock) {
            Array.from(lastBlock.childNodes).forEach((node) => {
                if (node === lastThink) {
                    return;
                }

                if (
                    node.nodeType === Node.TEXT_NODE &&
                    node.textContent.trim() === ''
                ) {
                    node.remove();
                    return;
                }

                if (
                    node.nodeType === Node.ELEMENT_NODE &&
                    node.tagName === 'BR'
                ) {
                    node.remove();
                }
            });
        }

        cleanupEmptyBlocks(container);
    }

    function hasVisibleContentAfterNode(container, markerNode) {
        const walker = document.createTreeWalker(
            container,
            NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
            null
        );

        let afterMarker = false;

        while (walker.nextNode()) {
            const node = walker.currentNode;

            if (node === markerNode) {
                afterMarker = true;
                continue;
            }

            if (!afterMarker) {
                continue;
            }

            if (node.nodeType === Node.TEXT_NODE) {
                if (node.parentElement?.closest('.think')) {
                    continue;
                }

                if ((node.textContent || '').trim() !== '') {
                    return true;
                }

                continue;
            }

            if (node.nodeType !== Node.ELEMENT_NODE) {
                continue;
            }

            if (node.classList?.contains('think') || node.closest?.('.think')) {
                continue;
            }

            if (node.tagName === 'BR') {
                continue;
            }

            if ((node.textContent || '').trim() !== '' || hasMeaningfulChildContent(node)) {
                return true;
            }
        }

        return false;
    }

    function cleanupThinkSpans(container) {
        if (!container) {
            return;
        }

        const thinkSpans = Array.from(container.querySelectorAll('.think'));

        if (thinkSpans.length === 0) {
            cleanupEmptyBlocks(container);
            return;
        }

        const lastThink = thinkSpans[thinkSpans.length - 1];

        if (!hasVisibleContentAfterNode(container, lastThink)) {
            keepOnlyLastThink(container);
            return;
        }

        removeThinkSpansOnly(container);
    }

    function copyElementAttributes(target, source) {
        Array.from(target.attributes).forEach((attribute) => {
            target.removeAttribute(attribute.name);
        });

        Array.from(source.attributes).forEach((attribute) => {
            target.setAttribute(attribute.name, attribute.value);
        });
    }

    function canonicalRetriexSourceChipKey(value) {
        return String(value || '')
            .replace(/\u00a0/g, ' ')
            .replace(/[‐‑‒–—]/g, '-')
            .replace(/\s+/g, ' ')
            .trim()
            .toLowerCase()
            .replace(/[^\p{L}\p{N}]+/gu, '');
    }

    function deduplicateRetriexSourceChips(container) {
        if (!container) {
            return;
        }

        container.querySelectorAll('.retriex-source-overview__badges').forEach((badgeGroup) => {
            const seen = new Set();

            badgeGroup.querySelectorAll('.retriex-source-chip').forEach((chip) => {
                let key = canonicalRetriexSourceChipKey(chip.textContent);

                if (key === 'shopsystem' || key === 'liveshopdaten') {
                    chip.textContent = 'Live-Shopdaten';
                    key = 'liveshopdaten';
                }

                if (seen.has(key)) {
                    chip.remove();
                    return;
                }

                seen.add(key);
            });
        });
    }

    function deduplicateRetriexMetaCards(container) {
        if (!container) {
            return;
        }

        deduplicateRetriexSourceChips(container);

        const cards = Array.from(container.querySelectorAll('.retriex-meta-card[data-retriex-meta-id]'));
        const cardsById = new Map();

        cards.forEach((card) => {
            const cardId = card.getAttribute('data-retriex-meta-id');

            if (!cardId) {
                return;
            }

            if (!cardsById.has(cardId)) {
                cardsById.set(cardId, []);
            }

            cardsById.get(cardId).push(card);
        });

        cardsById.forEach((group) => {
            if (group.length <= 1) {
                return;
            }

            const firstCard = group[0];
            const latestCard = group[group.length - 1];

            firstCard.innerHTML = latestCard.innerHTML;
            copyElementAttributes(firstCard, latestCard);

            group.slice(1).forEach((card) => card.remove());
        });

        cleanupEmptyBlocks(container);
    }

    function renderBubbleContent(bubble, raw) {
        bubble.innerHTML = renderMarkdown(raw);
        cleanupThinkSpans(bubble);
        deduplicateRetriexMetaCards(bubble);
        enhanceChatLinks(bubble);
        scrollChatToBottom();
    }

    function setBusyUi(isBusy) {
        state.isStreaming = isBusy;

        sendBtn.disabled = isBusy;
        abortBtn.disabled = !isBusy;
        clearBtn.disabled = isBusy;

        if (isBusy) {
            aiCloudEl?.classList.remove('d-none');
            return;
        }

        aiCloudEl?.classList.add('d-none');
    }

    function clearScheduledRender() {
        if (!state.renderTimer) {
            return;
        }

        clearTimeout(state.renderTimer);
        state.renderTimer = null;
    }

    function scheduleRender(bubble, getRaw) {
        if (state.renderTimer) {
            return;
        }

        state.renderTimer = setTimeout(() => {
            try {
                renderBubbleContent(bubble, getRaw());
            } finally {
                state.renderTimer = null;
            }
        }, 100);
    }

    function parseSseEvents(buffer) {
        const normalizedBuffer = buffer.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
        const chunks = normalizedBuffer.split('\n\n');

        return {
            events: chunks.slice(0, -1),
            rest: chunks[chunks.length - 1] || '',
        };
    }

    function readSseEvent(rawEvent) {
        const lines = rawEvent.split('\n');
        let eventName = 'message';
        const dataLines = [];

        lines.forEach((line) => {
            if (line.startsWith('event:')) {
                eventName = line.slice(6).trim() || 'message';
                return;
            }

            if (line.startsWith('data:')) {
                dataLines.push(line.slice(5).trimStart());
            }
        });

        return {
            eventName,
            data: dataLines.join('\n'),
        };
    }

    function finalizeRetriexRunMetaCards(container, options = {}) {
        if (!container) {
            return;
        }

        const isError = options.state === 'error';
        const titleText = isError ? 'Antwort wurde unterbrochen' : 'Abgeschlossen';
        const statusText = isError ? 'Status: unterbrochen' : 'Status: abgeschlossen';
        const emptySourceText = isError ? 'nicht vollständig geprüft' : 'keine belastbare Datenbasis';

        container.querySelectorAll('.retriex-run-meta[data-retriex-meta-id="run-status"]').forEach((card) => {
            card.setAttribute('data-retriex-meta-state', isError ? 'error' : 'completed');

            const title = card.querySelector('.retriex-meta-card__title');

            if (title) {
                title.textContent = titleText;
            }

            const statusPill = card.querySelector('.retriex-meta-pill--status');

            if (statusPill) {
                statusPill.textContent = statusText;
            }

            const emptySource = card.querySelector('.retriex-source-overview__empty');

            if (emptySource && emptySource.textContent.trim() === 'wird geprüft') {
                emptySource.textContent = emptySourceText;
            }
        });
    }

    function finalizeStream(bubble, raw, options = {}) {
        clearScheduledRender();
        bubble.innerHTML = renderMarkdown(raw);
        removeThinkSpansOnly(bubble);
        deduplicateRetriexMetaCards(bubble);
        finalizeRetriexRunMetaCards(bubble, options);
        bubble.classList.remove('loader');
        enhanceChatLinks(bubble);
        scrollChatToBottom();
    }

    async function releaseStreamResources() {
        if (state.eventSource) {
            state.eventSource.close();
            state.eventSource = null;
        }

        try {
            await state.reader?.cancel();
        } catch (err) {
            console.debug('Reader cancel failed:', err);
        }

        state.reader = null;
        state.abortController = null;
        state.completeStream = null;
        state.failStream = null;
    }

    async function loadHistory() {
        try {
            const res = await fetch('/history');

            if (!res.ok) {
                console.error('History request failed with status:', res.status);
                return;
            }

            const messages = await res.json();
            let latestLoadedUserPrompt = '';

            messages.forEach((message) => {
                const bubble = addMessage(message.role);
                renderBubbleContent(bubble, message.text);

                if (message.role === 'user') {
                    latestLoadedUserPrompt = normalizeContextHintText(message.text);
                    return;
                }

                if (message.role === 'assistant' && latestLoadedUserPrompt) {
                    rememberCompletedTurn(latestLoadedUserPrompt, message.text);
                }
            });

            enhanceChatLinks(chatEl);
        } catch (err) {
            console.error('History load failed:', err);
        }
    }

    chatEl?.addEventListener('click', (event) => {
        const actionButton = event.target?.closest?.('.retriex-action-chip[data-retriex-action-prompt]');

        if (!actionButton || state.isStreaming) {
            return;
        }

        const actionPrompt = normalizeContextHintText(actionButton.getAttribute('data-retriex-action-prompt') || '');

        if (!actionPrompt) {
            return;
        }

        promptEl.value = actionPrompt;
        promptEl.focus();
        sendBtn.click();
    });

    loadHistory();

    promptEl.addEventListener('keydown', (event) => {
        if (event.key === 'Enter' && !event.shiftKey) {
            event.preventDefault();
            sendBtn.click();
        }
    });

    sendBtn.addEventListener('click', async () => {
        const prompt = promptEl.value.trim();

        if (!prompt || state.isStreaming) {
            return;
        }

        const contextHint = buildClientContextHint();

        addMessage('user', renderMarkdown(prompt));
        promptEl.value = '';

        const bubble = addLoader();
        let raw = '';
        let firstChunk = true;

        state.abortRequested = false;
        state.abortController = new AbortController();

        setBusyUi(true);

        const appendChunk = (chunk) => {
            if (firstChunk) {
                bubble.classList.remove('loader');
                bubble.innerHTML = '';
                firstChunk = false;
            }

            raw += chunk;
            scheduleRender(bubble, () => raw);
        };

        const appendError = (message) => {
            const safeMessage = String(message || '').trim();

            if (!safeMessage) {
                return;
            }

            if (firstChunk) {
                bubble.classList.remove('loader');
                bubble.innerHTML = '';
                firstChunk = false;
            }

            const formattedMessage = `<em>${safeMessage}</em>`;
            raw += raw.trim() === '' ? formattedMessage : `\n\n${formattedMessage}`;
            finalizeStream(bubble, raw, {state: 'error'});
        };

        const finishEventStream = () => {
            state.eventSource?.close();
            state.eventSource = null;
        };

        try {
            const jobRes = await fetch('/ask-jobs', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    prompt,
                    fullContext: false,
                    contextHint,
                }),
                signal: state.abortController.signal,
            });

            if (!jobRes.ok) {
                throw new Error(`HTTP ${jobRes.status} ${jobRes.statusText}`);
            }

            const jobPayload = await jobRes.json();
            const jobId = String(jobPayload.jobId || '');

            if (!/^[a-f0-9]{48}$/.test(jobId)) {
                throw new Error('Invalid stream job response');
            }

            await new Promise((resolve, reject) => {
                let finished = false;
                let lastSseEventId = 0;
                let completedStatusSeenAt = null;
                let lastClientProgressAt = Date.now();
                const source = new EventSource(`/ask-sse/${encodeURIComponent(jobId)}`);
                state.eventSource = source;

                let networkErrorTimer = null;
                let jobStatusTimer = null;

                const clearNetworkErrorTimer = () => {
                    if (!networkErrorTimer) {
                        return;
                    }

                    clearTimeout(networkErrorTimer);
                    networkErrorTimer = null;
                };

                const clearJobStatusTimer = () => {
                    if (!jobStatusTimer) {
                        return;
                    }

                    clearTimeout(jobStatusTimer);
                    jobStatusTimer = null;
                };

                const clearStreamTimers = () => {
                    clearNetworkErrorTimer();
                    clearJobStatusTimer();
                };

                const parsePositiveInteger = (value) => {
                    const parsed = Number.parseInt(String(value || ''), 10);
                    return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
                };

                const complete = () => {
                    if (finished) {
                        return;
                    }

                    finished = true;
                    clearStreamTimers();
                    finishEventStream();
                    resolve();
                };

                const fail = (err) => {
                    if (finished) {
                        return;
                    }

                    finished = true;
                    clearStreamTimers();
                    finishEventStream();
                    reject(err);
                };

                const completeFromStatus = (statusPayload) => {
                    if (finished || state.abortRequested) {
                        complete();
                        return;
                    }

                    const statusLastEventId = parsePositiveInteger(statusPayload?.lastEventId);

                    if (statusLastEventId > lastSseEventId) {
                        completedStatusSeenAt ??= Date.now();

                        if (Date.now() - completedStatusSeenAt < JOB_COMPLETION_CATCHUP_GRACE_MS) {
                            return;
                        }
                    }

                    finalizeStream(bubble, raw);
                    rememberCompletedTurn(prompt, raw);
                    complete();
                };

                const completeWithJobError = (message) => {
                    appendError(message || 'Der Antwort-Stream wurde beendet, bevor die Antwort abgeschlossen werden konnte.');
                    complete();
                };

                const pollJobStatus = async () => {
                    if (finished || state.abortRequested) {
                        return;
                    }

                    try {
                        const res = await fetch(`/ask-jobs/${encodeURIComponent(jobId)}`, {
                            method: 'GET',
                            cache: 'no-store',
                            headers: {'Accept': 'application/json'},
                        });

                        if (!res.ok) {
                            if (res.status === 404 && Date.now() - lastClientProgressAt > JOB_CLIENT_STALE_GRACE_MS) {
                                completeWithJobError('Der Antwort-Job wurde nicht mehr gefunden. Bitte sende die Anfrage erneut.');
                            }
                            return;
                        }

                        const statusPayload = await res.json();
                        const status = String(statusPayload?.status || '');
                        const message = String(statusPayload?.message || '').trim();

                        if (status === 'completed') {
                            completeFromStatus(statusPayload);
                            return;
                        }

                        if (status === 'failed') {
                            completeWithJobError(message || 'Der Antwort-Stream ist fehlgeschlagen. Bitte sende die Anfrage erneut.');
                            return;
                        }

                        if (status === 'interrupted') {
                            completeWithJobError(message || 'Der Antwort-Stream wurde durch einen Verbindungsabbruch unterbrochen. Bitte sende die Anfrage erneut, falls die Antwort unvollständig ist.');
                            return;
                        }

                        if (status === 'missing') {
                            completeWithJobError(message || 'Der Antwort-Job wurde nicht gefunden. Bitte sende die Anfrage erneut.');
                            return;
                        }

                        if (status === 'running') {
                            const updatedAt = parsePositiveInteger(statusPayload?.updatedAt);
                            const serverTime = parsePositiveInteger(statusPayload?.serverTime);
                            const staleAfterSeconds = parsePositiveInteger(statusPayload?.runningStaleAfterSeconds);

                            if (updatedAt > 0 && serverTime > 0 && staleAfterSeconds > 0 && serverTime - updatedAt > staleAfterSeconds + 15) {
                                completeWithJobError(message || 'Der Antwort-Job liefert seit längerer Zeit keine neuen Daten. Der Stream wurde beendet.');
                                return;
                            }
                        }
                    } catch (err) {
                        console.debug('Stream job status polling failed:', err);
                    } finally {
                        if (!finished && !state.abortRequested) {
                            jobStatusTimer = setTimeout(pollJobStatus, JOB_STATUS_POLL_INTERVAL_MS);
                        }
                    }
                };

                state.completeStream = complete;
                state.failStream = fail;

                jobStatusTimer = setTimeout(pollJobStatus, JOB_STATUS_POLL_INTERVAL_MS);

                source.onopen = () => {
                    clearNetworkErrorTimer();
                    lastClientProgressAt = Date.now();
                };

                source.onmessage = (event) => {
                    if (state.abortRequested || finished) {
                        complete();
                        return;
                    }

                    clearNetworkErrorTimer();
                    lastClientProgressAt = Date.now();
                    completedStatusSeenAt = null;

                    if (event.data === undefined || event.data === null || event.data === '') {
                        return;
                    }

                    const numericEventId = Number.parseInt(event.lastEventId || '', 10);

                    if (Number.isFinite(numericEventId) && numericEventId > 0) {
                        if (numericEventId <= lastSseEventId) {
                            return;
                        }

                        lastSseEventId = numericEventId;
                    }

                    appendChunk(event.data);
                };

                source.addEventListener('done', () => {
                    if (!state.abortRequested) {
                        finalizeStream(bubble, raw);
                        rememberCompletedTurn(prompt, raw);
                    }

                    complete();
                });

                source.addEventListener('error', (event) => {
                    if (state.abortRequested || finished) {
                        complete();
                        return;
                    }

                    if (event instanceof MessageEvent && typeof event.data === 'string') {
                        appendError(event.data);
                        complete();
                        return;
                    }

                    if (source.readyState === EventSource.CLOSED) {
                        fail(new Error('EventSource connection closed'));
                        return;
                    }

                    if (!networkErrorTimer) {
                        networkErrorTimer = setTimeout(() => {
                            if (!finished && !state.abortRequested) {
                                fail(new Error('EventSource connection error'));
                            }
                        }, 20000);
                    }
                });
            });
        } catch (err) {
            if (err?.name === 'AbortError' || state.abortRequested) {
                console.info('SSE request aborted by user');
            } else {
                console.error('SSE stream failed:', err);

                bubble.classList.remove('loader');

                const userMessage = 'Die Verbindung zum Antwort-Stream wurde unterbrochen. Bitte sende die Anfrage erneut, falls die Antwort unvollständig ist.';

                if (raw.trim() !== '') {
                    const formattedMessage = `<em>${userMessage}</em>`;
                    raw += raw.trim() === '' ? formattedMessage : `\n\n${formattedMessage}`;
                    finalizeStream(bubble, raw, {state: 'error'});
                } else {
                    bubble.innerHTML = `<em>${userMessage}</em>`;
                    enhanceChatLinks(bubble);
                    scrollChatToBottom();
                }
            }
        } finally {
            clearScheduledRender();
            await releaseStreamResources();
            setBusyUi(false);
        }
    });

    abortBtn.addEventListener('click', async () => {
        if (!state.isStreaming) {
            return;
        }

        state.abortRequested = true;
        state.abortController?.abort();
        state.completeStream?.();
        await releaseStreamResources();
        setBusyUi(false);
        addMessage('assistant', '<em>[aborted]</em>');
    });

    clearBtn.addEventListener('click', async () => {
        if (state.isStreaming) {
            return;
        }

        try {
            await fetch('/history/delete', {method: 'POST'});
        } catch (err) {
            console.error('History delete failed:', err);
        }

        state.lastCompletedUserPrompt = '';
        state.lastCompletedAssistantText = '';
        try {
            window.sessionStorage?.removeItem(LAST_TURN_STORAGE_KEY);
        } catch (err) {
            console.debug('Could not clear last completed turn:', err);
        }
        chatEl.innerHTML = '';
        addMessage('assistant', '<em>History cleared.</em>');
    });
});
