/**
 * Notification bell runtime component.
 *
 * Works with the native <details> dropdown. Handles polling,
 * notification list rendering, mark-read and dismiss actions.
 */

import { telemetry } from '/js/core/app-telemetry.js';
import { getJson, postForm } from '/js/core/app-http.js';
import { getAppBase } from '/js/pages/app-list-utils.js';

const POLL_INTERVAL_MS = 60_000;
const TYPE_ICONS = {
    'user.created': 'bi-person-plus',
    'user.deleted': 'bi-person-dash',
    'user.activated': 'bi-person-check',
    'user.deactivated': 'bi-person-x',
    'user.assignment_changed': 'bi-diagram-3',
    'system': 'bi-gear',
};

function getCsrf() {
    const root = document.documentElement;
    return {
        key: root.dataset.csrfKey || '',
        token: root.dataset.csrfToken || '',
    };
}

function timeAgo(dateStr) {
    const now = Date.now();
    const then = new Date(dateStr).getTime();
    const diffSec = Math.floor((now - then) / 1000);

    if (diffSec < 60) return '< 1m';
    if (diffSec < 3600) return Math.floor(diffSec / 60) + 'm';
    if (diffSec < 86400) return Math.floor(diffSec / 3600) + 'h';
    return Math.floor(diffSec / 86400) + 'd';
}

function escapeHtml(str) {
    const d = document.createElement('div');
    d.textContent = str;
    return d.innerHTML;
}

export function initNotificationBell(root = document, config = {}) {
    const details = root.querySelector('[data-notification-bell]');
    const dropdown = root.querySelector('[data-notification-dropdown]');
    if (!details || !dropdown) return { destroy() {} };
    const appBase = getAppBase();
    const endpoint = (path) => new URL(path, appBase).toString();

    const badge = details.querySelector('[data-notification-badge]');
    const list = dropdown.querySelector('[data-notification-list]');
    const emptyEl = list?.querySelector('.app-empty-state');
    const loadingEl = list?.querySelector('[data-notification-loading]');
    const errorEl = list?.querySelector('[data-notification-error]');
    const markAllBtn = dropdown.querySelector('[data-notification-mark-all]');

    const ac = new AbortController();
    const signal = ac.signal;
    let pollTimer = null;

    // --- Badge ---
    function updateBadge(count) {
        if (!badge) return;
        if (count > 0) {
            badge.textContent = count > 99 ? '99+' : String(count);
            badge.style.display = '';
        } else {
            badge.textContent = '';
            badge.style.display = 'none';
        }
    }

    // --- State management ---
    function showState(state) {
        if (loadingEl) loadingEl.style.display = state === 'loading' ? '' : 'none';
        if (errorEl) errorEl.style.display = state === 'error' ? '' : 'none';
        if (emptyEl) emptyEl.style.display = state === 'empty' ? '' : 'none';
    }

    // --- Render ---
    function renderNotifications(notifications) {
        if (!list) return;

        // Remove rendered items, keep state elements
        list.querySelectorAll('.app-notification-item').forEach(el => el.remove());

        if (notifications.length === 0) {
            showState('empty');
            return;
        }
        showState('success');

        const frag = document.createDocumentFragment();
        for (const n of notifications) {
            frag.appendChild(createNotificationItem(n));
        }
        list.insertBefore(frag, emptyEl);
    }

    function createNotificationItem(n) {
        const item = document.createElement('div');
        item.className = 'app-notification-item' + (n.is_read === 0 ? ' app-notification-item--unread' : '');
        item.dataset.notificationId = n.id;

        const iconClass = TYPE_ICONS[n.type] || 'bi-bell';
        const bodyHtml = n.body ? `<p class="app-notification-item__body">${escapeHtml(n.body)}</p>` : '';

        item.innerHTML =
            `<span class="app-notification-item__icon"><i class="bi ${escapeHtml(iconClass)}"></i></span>` +
            `<div class="app-notification-item__content">` +
                `<p class="app-notification-item__title">${escapeHtml(n.title)}</p>` +
                bodyHtml +
                `<span class="app-notification-item__time">${escapeHtml(timeAgo(n.created))}</span>` +
            `</div>` +
            `<div class="app-notification-item__actions">` +
                (n.is_read === 0 ? `<button type="button" class="app-notification-item__action-btn" data-action="mark-read" title="${escapeHtml(dropdown.dataset.labelMarkRead || '')}" aria-label="${escapeHtml(dropdown.dataset.labelMarkRead || '')}"><i class="bi bi-check2"></i></button>` : '') +
                `<button type="button" class="app-notification-item__action-btn" data-action="dismiss" title="${escapeHtml(dropdown.dataset.labelDismiss || '')}" aria-label="${escapeHtml(dropdown.dataset.labelDismiss || '')}"><i class="bi bi-x"></i></button>` +
            `</div>`;

        // Keyboard + click navigation for items with a link
        if (n.link) {
            item.classList.add('app-notification-item--link');
            item.setAttribute('role', 'link');
            item.setAttribute('tabindex', '0');

            const navigate = () => {
                if (n.is_read === 0) markRead(n.id);
                window.location.href = endpoint(String(n.link));
            };

            item.addEventListener('click', (e) => {
                if (e.target.closest('[data-action]')) return;
                navigate();
            }, { signal });

            item.addEventListener('keydown', (e) => {
                if (e.target.closest('[data-action]')) return;
                if (e.key === 'Enter') {
                    e.preventDefault();
                    navigate();
                }
            }, { signal });
        } else {
            item.addEventListener('click', (e) => {
                if (e.target.closest('[data-action]')) return;
            }, { signal });
        }

        // Action buttons
        item.addEventListener('click', (e) => {
            const actionBtn = e.target.closest('[data-action]');
            if (!actionBtn) return;
            e.stopPropagation();
            const action = actionBtn.dataset.action;
            if (action === 'mark-read') markRead(n.id);
            if (action === 'dismiss') dismiss(n.id);
        }, { signal });

        return item;
    }

    // --- API ---
    async function loadNotifications() {
        showState('loading');
        list?.querySelectorAll('.app-notification-item').forEach(el => el.remove());
        try {
            const data = await getJson(endpoint('notifications/list-data'), { signal });
            if (data.ok) {
                renderNotifications(data.notifications || []);
                updateBadge(data.unread_count ?? 0);
            } else {
                showState('error');
            }
        } catch (err) {
            if (err instanceof DOMException && err.name === 'AbortError') return;
            showState('error');
            telemetry.capture('warn_once', {
                message: 'Notification bell: load failed',
                meta: { module: 'notifications', component: 'notification-bell', action: 'load' },
            });
        }
    }

    async function pollUnreadCount() {
        try {
            const data = await getJson(endpoint('notifications/unread-count-data'), { signal });
            if (data.ok) {
                updateBadge(data.unread_count ?? 0);
            }
        } catch (err) {
            if (err instanceof DOMException && err.name === 'AbortError') return;
            telemetry.capture('warn_once', {
                message: 'Notification bell: poll failed',
                meta: { module: 'notifications', component: 'notification-bell', action: 'poll' },
            });
        }
    }

    async function markRead(id) {
        try {
            const csrf = getCsrf();
            const body = new URLSearchParams();
            body.set('id', String(id));
            if (csrf.key) body.set(csrf.key, csrf.token);

            const data = await postForm(endpoint('notifications/mark-read-data'), body, { signal });
            if (data.ok) {
                updateBadge(data.unread_count ?? 0);
                if (details.open) loadNotifications();
            }
        } catch (err) {
            if (err instanceof DOMException && err.name === 'AbortError') return;
            telemetry.capture('warn_once', {
                message: 'Notification bell: mark-read failed',
                meta: { module: 'notifications', component: 'notification-bell', action: 'mark-read' },
            });
        }
    }

    async function markAllRead() {
        try {
            const csrf = getCsrf();
            const body = new URLSearchParams();
            body.set('all', '1');
            if (csrf.key) body.set(csrf.key, csrf.token);

            const data = await postForm(endpoint('notifications/mark-read-data'), body, { signal });
            if (data.ok) {
                updateBadge(data.unread_count ?? 0);
                if (details.open) loadNotifications();
            }
        } catch (err) {
            if (err instanceof DOMException && err.name === 'AbortError') return;
            telemetry.capture('warn_once', {
                message: 'Notification bell: mark-all-read failed',
                meta: { module: 'notifications', component: 'notification-bell', action: 'mark-all-read' },
            });
        }
    }

    async function dismiss(id) {
        try {
            const csrf = getCsrf();
            const body = new URLSearchParams();
            body.set('id', String(id));
            if (csrf.key) body.set(csrf.key, csrf.token);

            const data = await postForm(endpoint('notifications/delete-data'), body, { signal });
            if (data.ok) {
                updateBadge(data.unread_count ?? 0);
                const el = list?.querySelector(`[data-notification-id="${id}"]`);
                if (el) {
                    el.remove();
                    if (list && !list.querySelector('.app-notification-item') && emptyEl) {
                        emptyEl.style.display = '';
                    }
                }
            }
        } catch (err) {
            if (err instanceof DOMException && err.name === 'AbortError') return;
            telemetry.capture('warn_once', {
                message: 'Notification bell: dismiss failed',
                meta: { module: 'notifications', component: 'notification-bell', action: 'dismiss' },
            });
        }
    }

    // --- Events ---
    details.addEventListener('toggle', () => {
        if (details.open) loadNotifications();
    }, { signal });

    if (markAllBtn) {
        markAllBtn.addEventListener('click', (e) => {
            e.stopPropagation();
            markAllRead();
        }, { signal });
    }

    // --- Polling ---
    function startPolling() {
        stopPolling();
        pollTimer = setInterval(() => {
            if (document.visibilityState !== 'hidden') {
                pollUnreadCount();
            }
        }, POLL_INTERVAL_MS);
    }

    function stopPolling() {
        if (pollTimer !== null) {
            clearInterval(pollTimer);
            pollTimer = null;
        }
    }

    document.addEventListener('visibilitychange', () => {
        if (document.visibilityState === 'visible') {
            pollUnreadCount();
            startPolling();
        } else {
            stopPolling();
        }
    }, { signal });

    startPolling();

    return {
        destroy() {
            ac.abort();
            stopPolling();
        },
    };
}
