(() => {
    let ws = null;
    let reconnectTimer = null;

    function normalizeToken(value) {
        const token = String(value || '').trim();
        if (!token) return '';
        if (token.toLowerCase().startsWith('bearer ')) {
            return token.slice(7).trim();
        }
        return token;
    }

    function getCookie(name) {
        const cookie = document.cookie || '';
        const parts = cookie.split(';').map(p => p.trim());
        const match = parts.find(p => p.startsWith(`${name}=`));
        if (!match) return '';
        return decodeURIComponent(match.slice(name.length + 1));
    }

    function getToken() {
        const fromLocal = normalizeToken(localStorage.getItem('access_token'));
        if (fromLocal) return fromLocal;

        const fromSession = normalizeToken(sessionStorage.getItem('access_token'));
        if (fromSession) return fromSession;

        const fromCookie = normalizeToken(getCookie('access_token'));
        return fromCookie;
    }

    function ensureContainer() {
        let container = document.getElementById('telefoni-toast-container');
        if (!container) {
            container = document.createElement('div');
            container.id = 'telefoni-toast-container';
            container.setAttribute('aria-live', 'polite');
            container.setAttribute('aria-atomic', 'true');
            container.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                z-index: 9999;
                width: 420px;
                max-width: 90%;
            `;
            document.body.appendChild(container);
        }
        return container;
    }

    function escapeHtml(str) {
        return String(str ?? '')
            .replaceAll('&', '&amp;')
            .replaceAll('<', '&lt;')
            .replaceAll('>', '&gt;')
            .replaceAll('"', '&quot;')
            .replaceAll("'", '&#039;');
    }

    function normalizeNamePart(value) {
        return String(value || '')
            .trim()
            .toLocaleLowerCase('da-DK')
            .replace(/\s+/g, ' ');
    }

    function normalizeDigits(value) {
        return String(value || '').replace(/\D+/g, '');
    }

    function buildToastContact(contactData) {
        if (!contactData) return null;

        const firstName = String(contactData.first_name || '').trim();
        const lastName = String(contactData.last_name || '').trim();
        const name = String(contactData.name || `${firstName} ${lastName}` || '').trim();
        const primaryCompany = Array.isArray(contactData.companies) && contactData.companies.length > 0
            ? contactData.companies[0]
            : null;

        return {
            id: contactData.id,
            first_name: firstName,
            last_name: lastName,
            name,
            company: primaryCompany?.name || contactData.company || '',
            company_id: primaryCompany?.id || contactData.company_id || null,
            phone: contactData.phone || null,
            mobile: contactData.mobile || null,
        };
    }

    function formatCaseLabel(caseItem) {
        if (!caseItem) return '';
        const title = String(caseItem.titel || caseItem.title || `Sag #${caseItem.id || ''}`).trim();
        const customer = String(caseItem.customer_name || '').trim();
        return customer ? `${title} · ${customer}` : title;
    }

    function formatContactLabel(contactItem) {
        if (!contactItem) return '';
        const firstName = String(contactItem.first_name || '').trim();
        const lastName = String(contactItem.last_name || '').trim();
        const name = [firstName, lastName].filter(Boolean).join(' ') || `Kontakt #${contactItem.id || ''}`;
        const companyNames = Array.isArray(contactItem.company_names) ? contactItem.company_names.filter(Boolean) : [];
        return companyNames.length ? `${name} · ${companyNames[0]}` : name;
    }

    async function fetchContactByName(firstName, lastName, phoneValue = '') {
        const fullName = [String(firstName || '').trim(), String(lastName || '').trim()]
            .filter(Boolean)
            .join(' ');
        const digits = normalizeDigits(phoneValue);
        if (!fullName && !digits) return null;

        const queries = [];
        if (fullName) queries.push(fullName);
        if (digits && digits !== fullName) queries.push(phoneValue || digits);

        const contactMap = new Map();
        for (const query of queries) {
            const qs = new URLSearchParams({ search: query, limit: '25' });
            const res = await fetch(`/api/v1/contacts?${qs.toString()}`, {
                credentials: 'include'
            });
            if (!res.ok) {
                const text = await res.text();
                throw new Error(text || `HTTP ${res.status}`);
            }

            const payload = await res.json();
            const contacts = Array.isArray(payload?.contacts) ? payload.contacts : [];
            contacts.forEach((item) => {
                if (item?.id) contactMap.set(Number(item.id), item);
            });
        }

        const contacts = Array.from(contactMap.values());
        const wantedFirst = normalizeNamePart(firstName);
        const wantedLast = normalizeNamePart(lastName);
        const wantedDigits = normalizeDigits(phoneValue);

        const exactNameMatch = contacts.find((item) => {
            return wantedFirst && normalizeNamePart(item?.first_name) === wantedFirst
                && normalizeNamePart(item?.last_name) === wantedLast;
        });
        if (exactNameMatch) return exactNameMatch;

        if (wantedDigits) {
            const exactNumberMatch = contacts.find((item) => {
                const numbers = [item?.phone, item?.mobile].map(normalizeDigits).filter(Boolean);
                return numbers.some((number) => number === wantedDigits || number.endsWith(wantedDigits) || wantedDigits.endsWith(number));
            });
            if (exactNumberMatch) return exactNumberMatch;
        }

        return contacts[0] || null;
    }

    async function fetchFullContact(contactId) {
        const res = await fetch(`/api/v1/contacts/${Number(contactId)}`, {
            credentials: 'include'
        });
        if (!res.ok) {
            const text = await res.text();
            throw new Error(text || `HTTP ${res.status}`);
        }
        return res.json();
    }

    async function fetchContactCases(contactId) {
        const res = await fetch(`/api/v1/contacts/${Number(contactId)}/cases`, {
            credentials: 'include'
        });
        if (!res.ok) {
            const text = await res.text();
            throw new Error(text || `HTTP ${res.status}`);
        }
        return res.json();
    }

    async function searchCases(query) {
        const qs = new URLSearchParams({ q: String(query || '').trim() });
        const res = await fetch(`/api/v1/search/sag?${qs.toString()}`, {
            credentials: 'include'
        });
        if (!res.ok) {
            const text = await res.text();
            throw new Error(text || `HTTP ${res.status}`);
        }
        const payload = await res.json();
        return Array.isArray(payload) ? payload : [];
    }

    function showIncomingCallToast(data) {
        const container = ensureContainer();
        let currentData = { ...data };
        let contact = currentData.contact || null;
        const number = data.number || '';
        const callId = data.call_id;

        const toastEl = document.createElement('div');
        toastEl.className = 'toast align-items-stretch';
        toastEl.setAttribute('role', 'alert');
        toastEl.setAttribute('aria-live', 'assertive');
        toastEl.setAttribute('aria-atomic', 'true');

        function closeSmartCaseModal() {
            const existing = document.getElementById('telefoni-smart-case-modal');
            if (existing) existing.remove();
            document.querySelectorAll('[data-telefoni-backdrop]').forEach((el) => el.remove());
            document.body.classList.remove('modal-open');
        }

        function openCaseSmartModal() {
            closeSmartCaseModal();

            const modal = document.createElement('div');
            modal.id = 'telefoni-smart-case-modal';
            modal.className = 'modal fade show';
            modal.style.display = 'block';
            modal.setAttribute('tabindex', '-1');
            modal.setAttribute('aria-modal', 'true');
            modal.setAttribute('role', 'dialog');
            modal.innerHTML = `
                <div class="modal-dialog modal-xl modal-dialog-scrollable">
                    <div class="modal-content border-0 shadow-lg">
                        <div class="modal-header bg-primary text-white">
                            <div>
                                <h5 class="modal-title mb-0"><i class="bi bi-link-45deg me-2"></i>Link sag</h5>
                                <div class="small opacity-75">${escapeHtml(number || 'Ukendt nummer')} · vælg en relevant sag eller søg videre</div>
                            </div>
                            <button type="button" class="btn-close btn-close-white" data-action="close-smart-case"></button>
                        </div>
                        <div class="modal-body">
                            <ul class="nav nav-pills gap-2 mb-3" role="tablist">
                                <li class="nav-item"><button class="nav-link active" type="button" data-tab-target="contact">Kontaktens sager</button></li>
                                <li class="nav-item"><button class="nav-link" type="button" data-tab-target="company">Firmaets sager</button></li>
                                <li class="nav-item"><button class="nav-link" type="button" data-tab-target="related">Relaterede kontakter</button></li>
                                <li class="nav-item"><button class="nav-link" type="button" data-tab-target="search">Søg sager</button></li>
                            </ul>
                            <div class="mb-3">
                                <label class="form-label small text-muted mb-1">Manuel sag-ID</label>
                                <div class="input-group">
                                    <input type="number" min="1" class="form-control" data-role="manual-case-id" placeholder="Fx 1234">
                                    <button type="button" class="btn btn-outline-secondary" data-action="link-manual-case">Link sag</button>
                                </div>
                            </div>
                            <div class="tab-pane-holder" data-pane="contact">
                                <div class="text-muted small p-2"><span class="spinner-border spinner-border-sm me-2"></span>Indlæser...</div>
                            </div>
                            <div class="tab-pane-holder d-none" data-pane="company">
                                <div class="text-muted small p-2"><span class="spinner-border spinner-border-sm me-2"></span>Indlæser...</div>
                            </div>
                            <div class="tab-pane-holder d-none" data-pane="related">
                                <div class="text-muted small p-2"><span class="spinner-border spinner-border-sm me-2"></span>Indlæser...</div>
                            </div>
                            <div class="tab-pane-holder d-none" data-pane="search">
                                <div class="mb-3">
                                    <label class="form-label small text-muted mb-1">Søg i alle sager</label>
                                    <input type="text" class="form-control" data-role="case-search-input" placeholder="Titel, kunde, ID, buzzword...">
                                </div>
                                <div class="alert alert-light border mb-0" data-role="search-hint">Skriv mindst 2 tegn for at søge.</div>
                                <div class="list-group mt-3" data-role="search-results"></div>
                            </div>
                        </div>
                    </div>
                </div>
            `;

            document.body.appendChild(modal);

            const backdrop = document.createElement('div');
            backdrop.className = 'modal-backdrop fade show';
            backdrop.dataset.telefoniBackdrop = '1';
            document.body.appendChild(backdrop);
            document.body.classList.add('modal-open');

            // ── helpers ──────────────────────────────────────────────────────
            const showPane = (paneName) => {
                modal.querySelectorAll('.tab-pane-holder').forEach((pane) => {
                    pane.classList.toggle('d-none', pane.dataset.pane !== paneName);
                });
                modal.querySelectorAll('[data-tab-target]').forEach((tabBtn) => {
                    tabBtn.classList.toggle('active', tabBtn.dataset.tabTarget === paneName);
                });
            };

            const renderCaseList = (pane, items, emptyText) => {
                const paneEl = modal.querySelector(`[data-pane="${pane}"]`);
                if (!paneEl) return;
                if (!items || items.length === 0) {
                    paneEl.innerHTML = `<div class="alert alert-light border mb-0">${escapeHtml(emptyText)}</div>`;
                    return;
                }
                paneEl.innerHTML = `<div class="list-group">${items.map((item) => {
                    const caseId = Number(item.id);
                    const label = formatCaseLabel(item);
                    const meta = [item.status, item.created_at ? new Date(item.created_at).toLocaleDateString('da-DK') : '']
                        .filter(Boolean).join(' · ');
                    return `
                        <button type="button" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center gap-3" data-case-id="${caseId}">
                            <div>
                                <div class="fw-semibold">${escapeHtml(label)}</div>
                                <div class="small text-muted">${escapeHtml(meta || `ID: ${caseId}`)}</div>
                            </div>
                            <i class="bi bi-arrow-right-circle text-primary"></i>
                        </button>`;
                }).join('')}</div>`;
                paneEl.querySelectorAll('[data-case-id]').forEach((button) => {
                    button.addEventListener('click', async () => {
                        const caseId = Number(button.getAttribute('data-case-id'));
                        await patchCallCase(caseId);
                        closeSmartCaseModal();
                        window.location.href = `/sag/${caseId}/v3`;
                    });
                });
            };

            const renderRelatedContacts = (relatedContacts) => {
                const paneEl = modal.querySelector('[data-pane="related"]');
                if (!paneEl) return;
                if (!relatedContacts.length) {
                    paneEl.innerHTML = '<div class="alert alert-light border mb-0">Ingen relaterede kontakter fundet</div>';
                    return;
                }
                paneEl.innerHTML = `<div class="list-group">${relatedContacts.map((item) => {
                    const contactId = Number(item.id);
                    const label = formatContactLabel(item);
                    const phone = String(item.phone || item.mobile || '').trim();
                    return `
                        <button type="button" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center gap-3" data-contact-id="${contactId}">
                            <div>
                                <div class="fw-semibold">${escapeHtml(label)}</div>
                                <div class="small text-muted">${escapeHtml(phone || 'Ingen telefon')}</div>
                            </div>
                            <i class="bi bi-person-lines-fill text-primary"></i>
                        </button>`;
                }).join('')}</div>`;
                paneEl.querySelectorAll('[data-contact-id]').forEach((button) => {
                    button.addEventListener('click', () => {
                        const cid = Number(button.getAttribute('data-contact-id'));
                        if (cid > 0) window.location.href = `/contacts/${cid}`;
                    });
                });
            };

            // ── søg-fane ─────────────────────────────────────────────────────
            const searchInput = modal.querySelector('[data-role="case-search-input"]');
            const searchResults = modal.querySelector('[data-role="search-results"]');
            const searchHint = modal.querySelector('[data-role="search-hint"]');
            let searchTimer = null;
            let searchToken = 0;

            const runSearch = async (query) => {
                const token = ++searchToken;
                const q = String(query || '').trim();
                if (q.length < 2) {
                    searchHint.textContent = 'Skriv mindst 2 tegn for at søge.';
                    searchResults.innerHTML = '';
                    return;
                }
                searchHint.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Søger...';
                searchResults.innerHTML = '';
                try {
                    const results = await searchCases(q);
                    if (token !== searchToken) return;
                    if (!results.length) {
                        searchHint.textContent = 'Ingen sager fundet';
                        return;
                    }
                    searchHint.textContent = `Fandt ${results.length} sager`;
                    searchResults.innerHTML = results.map((item) => {
                        const caseId = Number(item.id);
                        const label = formatCaseLabel(item);
                        return `
                            <button type="button" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" data-search-case-id="${caseId}">
                                <div>
                                    <div class="fw-semibold">${escapeHtml(label)}</div>
                                    <div class="small text-muted">${escapeHtml(item.status || '-')} · ID: ${caseId}</div>
                                </div>
                                <span class="badge text-bg-primary">Link</span>
                            </button>`;
                    }).join('');
                    searchResults.querySelectorAll('[data-search-case-id]').forEach((button) => {
                        button.addEventListener('click', async () => {
                            const caseId = Number(button.getAttribute('data-search-case-id'));
                            await patchCallCase(caseId);
                            closeSmartCaseModal();
                            window.location.href = `/sag/${caseId}/v3`;
                        });
                    });
                } catch (error) {
                    if (token !== searchToken) return;
                    searchHint.textContent = `Fejl: ${error?.message || 'ukendt fejl'}`;
                }
            };

            searchInput?.addEventListener('input', () => {
                if (searchTimer) clearTimeout(searchTimer);
                searchTimer = setTimeout(() => runSearch(searchInput.value), 250);
            });
            searchInput?.addEventListener('keydown', (event) => {
                if (event.key !== 'Enter') return;
                event.preventDefault();
                if (searchTimer) clearTimeout(searchTimer);
                runSearch(searchInput.value);
            });

            modal.querySelector('[data-action="link-manual-case"]')?.addEventListener('click', async () => {
                const manualValue = Number(modal.querySelector('[data-role="manual-case-id"]')?.value || 0);
                if (!Number.isInteger(manualValue) || manualValue <= 0) {
                    window.alert('Ugyldigt sag-ID');
                    return;
                }
                await patchCallCase(manualValue);
                closeSmartCaseModal();
                window.location.href = `/sag/${manualValue}/v3`;
            });

            modal.querySelector('[data-action="close-smart-case"]')?.addEventListener('click', closeSmartCaseModal);
            backdrop.addEventListener('click', closeSmartCaseModal);
            document.addEventListener('keydown', function onKeydown(event) {
                if (event.key === 'Escape') {
                    closeSmartCaseModal();
                    document.removeEventListener('keydown', onKeydown);
                }
            });

            modal.querySelectorAll('[data-tab-target]').forEach((tabBtn) => {
                tabBtn.addEventListener('click', () => showPane(tabBtn.dataset.tabTarget));
            });

            // ── hent altid friske data fra API ────────────────────────────────
            const contactId = contact?.id ? Number(contact.id) : null;
            if (contactId) {
                fetchContactCases(contactId).then((ctx) => {
                    const contactCases = ctx?.contact_cases || [];
                    const companyCases = ctx?.company_cases || [];
                    // Gem i currentData så efterfølgende åbninger også har data
                    currentData.contact_cases = contactCases;
                    currentData.company_cases = companyCases;

                    renderCaseList('contact', contactCases, 'Ingen sager tilknyttet denne kontakt');
                    renderCaseList('company', companyCases, 'Ingen sager på firmaet');

                    // Relaterede kontakter fra WS-payload (hurtig) eller tom
                    const relatedContacts = Array.isArray(currentData.related_contacts)
                        ? currentData.related_contacts : [];
                    renderRelatedContacts(relatedContacts);

                    const hasSuggestions = contactCases.length > 0 || companyCases.length > 0;
                    if (!hasSuggestions) {
                        showPane('search');
                        const term = String(contact?.name || contact?.company || number || '').trim();
                        if (term.length >= 2) {
                            searchInput.value = term;
                            runSearch(term);
                        } else {
                            setTimeout(() => searchInput?.focus(), 50);
                        }
                    }
                }).catch((err) => {
                    console.warn('📞 fetchContactCases fejlede:', err?.message || err);
                    renderCaseList('contact', [], 'Ingen data');
                    renderCaseList('company', [], 'Ingen data');
                    renderRelatedContacts([]);
                    showPane('search');
                    const term = String(contact?.name || contact?.company || number || '').trim();
                    if (term.length >= 2) {
                        searchInput.value = term;
                        runSearch(term);
                    } else {
                        setTimeout(() => searchInput?.focus(), 50);
                    }
                });
            } else {
                // Ingen kontakt — vis søge-fanen med det samme
                renderCaseList('contact', [], 'Ingen kontakt tilknyttet dette opkald');
                renderCaseList('company', [], 'Ingen kontakt tilknyttet dette opkald');
                renderRelatedContacts([]);
                showPane('search');
                const term = String(number || '').trim();
                if (term.length >= 2) {
                    searchInput.value = term;
                    runSearch(term);
                } else {
                    setTimeout(() => searchInput?.focus(), 50);
                }
            }
        }

        function renderToastBody() {
            contact = currentData.contact || null;
            const title = contact?.name ? contact.name : 'Ukendt nummer';
            const company = contact?.company ? contact.company : '';
            const recentCases = currentData.recent_cases || [];
            const lastCall = currentData.last_call;
            const openContactBtn = contact?.id
                ? `<button type="button" class="btn btn-sm btn-outline-secondary" data-action="open-contact">Åbn kontakt</button>`
                : '';

            let casesHtml = '';
            if (recentCases.length > 0) {
                casesHtml = '<div class="mt-2 mb-2"><small class="text-muted fw-semibold">Åbne sager:</small>';
                recentCases.forEach(c => {
                    casesHtml += `<div class="small"><a href="/sag/${c.id}" class="text-decoration-none" target="_blank">${escapeHtml(c.titel)}</a></div>`;
                });
                casesHtml += '</div>';
            }

            let lastCallHtml = '';
            if (lastCall) {
                const callDate = lastCall.started_at ? lastCall.started_at : lastCall;
                const lastCallDate = new Date(callDate);
                const now = new Date();
                const diffMs = now - lastCallDate;
                const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));

                let timeAgo = '';
                if (diffDays === 0) {
                    timeAgo = 'I dag';
                } else if (diffDays === 1) {
                    timeAgo = 'I går';
                } else if (diffDays < 7) {
                    timeAgo = `${diffDays} dage siden`;
                } else {
                    timeAgo = lastCallDate.toLocaleDateString('da-DK');
                }

                const brugerInfo = lastCall.bruger_navn ? ` (${escapeHtml(lastCall.bruger_navn)})` : '';

                let durationInfo = '';
                if (lastCall.duration_sec) {
                    const mins = Math.floor(lastCall.duration_sec / 60);
                    const secs = lastCall.duration_sec % 60;
                    durationInfo = mins > 0 ? ` - ${mins}m ${secs}s` : ` - ${secs}s`;
                }

                lastCallHtml = `<div class="small text-muted mt-2"><i class="bi bi-clock-history me-1"></i>Sidst snakket: ${timeAgo}${brugerInfo}${durationInfo}</div>`;
            }

            const quickCreateHtml = !contact?.id ? `
                <div class="border rounded p-2 mt-3 bg-light-subtle">
                    <div class="small fw-semibold mb-2">Hurtig opret kontakt</div>
                    <div class="row g-2">
                        <div class="col-6">
                            <input type="text" class="form-control form-control-sm" data-role="quick-first-name" placeholder="Fornavn">
                        </div>
                        <div class="col-6">
                            <input type="text" class="form-control form-control-sm" data-role="quick-last-name" placeholder="Efternavn">
                        </div>
                        <div class="col-12">
                            <input type="email" class="form-control form-control-sm" data-role="quick-email" placeholder="E-mail (valgfri)">
                        </div>
                        <div class="col-12">
                            <div class="form-text mt-0">Finder først eksisterende kontakt på navn og opdaterer telefon, hvis den kun mangler nummer.</div>
                        </div>
                    </div>
                    <div class="d-flex gap-2 mt-2">
                        <button type="button" class="btn btn-sm btn-success" data-action="quick-create-contact">Opret kontakt</button>
                    </div>
                </div>
            ` : '';

            toastEl.innerHTML = `
                <div class="toast-header">
                    <strong class="me-auto"><i class="bi bi-telephone me-2"></i>Opkald</strong>
                    <small class="text-muted">${escapeHtml(currentData.direction === 'outbound' ? 'Udgående' : 'Indgående')}</small>
                    <button type="button" class="btn-close ms-2" data-bs-dismiss="toast" aria-label="Close"></button>
                </div>
                <div class="toast-body">
                    <div class="fw-bold">${escapeHtml(number)}</div>
                    <div>${escapeHtml(title)}</div>
                    ${company ? `<div class="text-muted small">${escapeHtml(company)}</div>` : ''}
                    ${lastCallHtml}
                    ${casesHtml}
                    ${quickCreateHtml}
                    <div class="d-flex gap-2 mt-3 flex-wrap">
                        ${openContactBtn}
                        <button type="button" class="btn btn-sm btn-primary" data-action="create-case">Opret sag</button>
                        <button type="button" class="btn btn-sm btn-outline-primary" data-action="link-case">Link sag</button>
                    </div>
                </div>
            `;
        }

        renderToastBody();

        container.appendChild(toastEl);
        const toast = new bootstrap.Toast(toastEl, { autohide: false });
        toast.show();

        toastEl.addEventListener('hidden.bs.toast', () => toastEl.remove());

        async function patchCallCase(caseId) {
            const parsedCaseId = Number(caseId);
            if (!Number.isInteger(parsedCaseId) || parsedCaseId <= 0) {
                throw new Error('Ugyldigt sag-ID');
            }
            if (!Number.isInteger(Number(callId)) || Number(callId) <= 0) {
                throw new Error('Mangler call_id');
            }

            const res = await fetch(`/api/v1/telefoni/calls/${Number(callId)}`, {
                method: 'PATCH',
                headers: { 'Content-Type': 'application/json' },
                credentials: 'include',
                body: JSON.stringify({ sag_id: parsedCaseId })
            });

            if (!res.ok) {
                const t = await res.text();
                throw new Error(t || `HTTP ${res.status}`);
            }
            return parsedCaseId;
        }

        async function patchCallContact(contactId) {
            if (!Number.isInteger(Number(callId)) || Number(callId) <= 0) {
                throw new Error('Mangler call_id');
            }

            const res = await fetch(`/api/v1/telefoni/calls/${Number(callId)}`, {
                method: 'PATCH',
                headers: { 'Content-Type': 'application/json' },
                credentials: 'include',
                body: JSON.stringify({ kontakt_id: Number(contactId) })
            });

            if (!res.ok) {
                const text = await res.text();
                throw new Error(text || `HTTP ${res.status}`);
            }

            return res.json();
        }

        async function createOrUpdateContactFromToast() {
            const firstNameInput = toastEl.querySelector('[data-role="quick-first-name"]');
            const lastNameInput = toastEl.querySelector('[data-role="quick-last-name"]');
            const emailInput = toastEl.querySelector('[data-role="quick-email"]');
            const firstName = String(firstNameInput?.value || '').trim();
            const lastName = String(lastNameInput?.value || '').trim();
            const email = String(emailInput?.value || '').trim();

            if (!firstName) {
                window.alert('Fornavn er påkrævet');
                firstNameInput?.focus();
                return;
            }

            const actionBtn = toastEl.querySelector('[data-action="quick-create-contact"]');
            if (!actionBtn) return;

            actionBtn.disabled = true;
            const originalText = actionBtn.textContent;
            actionBtn.textContent = 'Gemmer...';

            try {
                const phoneValue = String(number || '').trim() || null;
                const existing = await fetchContactByName(firstName, lastName, phoneValue || '');
                let savedContactId = null;

                if (existing?.id) {
                    const existingHasPhone = String(existing.phone || existing.mobile || '').trim();
                    const shouldUpdatePhone = !existingHasPhone && phoneValue;
                    const shouldUpdateEmail = !String(existing.email || '').trim() && email;

                    if (shouldUpdatePhone || shouldUpdateEmail) {
                        const updatePayload = {};
                        if (shouldUpdatePhone) updatePayload.phone = phoneValue;
                        if (shouldUpdateEmail) updatePayload.email = email;

                        const updateRes = await fetch(`/api/v1/contacts/${Number(existing.id)}`, {
                            method: 'PUT',
                            headers: { 'Content-Type': 'application/json' },
                            credentials: 'include',
                            body: JSON.stringify(updatePayload)
                        });
                        if (!updateRes.ok) {
                            const text = await updateRes.text();
                            throw new Error(text || `HTTP ${updateRes.status}`);
                        }
                    }

                    savedContactId = Number(existing.id);
                } else {
                    const createRes = await fetch('/api/v1/contacts', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        credentials: 'include',
                        body: JSON.stringify({
                            first_name: firstName,
                            last_name: lastName,
                            email: email || null,
                            phone: phoneValue,
                            title: null,
                        })
                    });
                    if (!createRes.ok) {
                        const text = await createRes.text();
                        throw new Error(text || `HTTP ${createRes.status}`);
                    }

                    const created = await createRes.json();
                    savedContactId = Number(created?.id || 0);
                }

                if (!Number.isInteger(savedContactId) || savedContactId <= 0) {
                    throw new Error('Kontakt-ID mangler');
                }

                await patchCallContact(savedContactId);
                const [fullContact, caseContext] = await Promise.all([
                    fetchFullContact(savedContactId),
                    fetchContactCases(savedContactId),
                ]);
                currentData = {
                    ...currentData,
                    contact: buildToastContact(fullContact),
                    contact_cases: caseContext?.contact_cases || currentData.contact_cases || [],
                    company_cases: caseContext?.company_cases || currentData.company_cases || [],
                    related_contacts: caseContext?.related_contacts || currentData.related_contacts || [],
                };
                renderToastBody();
            } catch (err) {
                window.alert(`Kunne ikke oprette kontakt: ${err?.message || 'ukendt fejl'}`);
            } finally {
                const refreshedActionBtn = toastEl.querySelector('[data-action="quick-create-contact"]');
                if (refreshedActionBtn) {
                    refreshedActionBtn.disabled = false;
                    refreshedActionBtn.textContent = originalText;
                }
            }
        }

        toastEl.addEventListener('click', async (e) => {
            const btn = e.target.closest('button[data-action]');
            if (!btn) return;
            const action = btn.getAttribute('data-action');
            if (action === 'open-contact' && contact?.id) {
                window.location.href = `/contacts/${contact.id}`;
            }
            if (action === 'create-case') {
                const qs = new URLSearchParams();
                if (contact?.id) qs.set('contact_id', String(contact.id));
                if (contact?.company_id) qs.set('customer_id', String(contact.company_id));
                qs.set('title', `Telefonsamtale – ${number}`);
                qs.set('telefoni_opkald_id', String(callId));
                window.location.href = `/sag/new?${qs.toString()}`;
            }
            if (action === 'link-case') {
                openCaseSmartModal();
            }
            if (action === 'quick-create-contact') {
                await createOrUpdateContactFromToast();
            }
        });
    }

    function scheduleReconnect() {
        if (reconnectTimer) return;
        reconnectTimer = setTimeout(() => {
            reconnectTimer = null;
            connect();
        }, 5000);
    }

    function connect() {
        if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
            return;
        }

        const token = getToken();
        const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
        // Fallback to cookie-auth websocket when token is HttpOnly and cannot be read by JS.
        const url = token
            ? `${proto}://${window.location.host}/api/v1/telefoni/ws?token=${encodeURIComponent(token)}`
            : `${proto}://${window.location.host}/api/v1/telefoni/ws`;
        ws = new WebSocket(url);

        ws.onopen = () => console.log('📞 Telefoni WS connected');
        ws.onclose = (evt) => {
            console.log('📞 Telefoni WS disconnected', evt.code, evt.reason || '');
            scheduleReconnect();
        };
        ws.onerror = () => {
            // onclose handles reconnect
        };
        ws.onmessage = (evt) => {
            try {
                const msg = JSON.parse(evt.data);
                if (msg?.event === 'incoming_call') {
                    showIncomingCallToast(msg.data || {});
                }
            } catch (e) {
                console.warn('Telefoni WS message parse failed', e);
            }
        };
    }

    document.addEventListener('DOMContentLoaded', connect);
    window.addEventListener('focus', connect);
    window.addEventListener('storage', (evt) => {
        if (evt.key === 'access_token') connect();
    });
})();
