/**
 * @license MIT
 * author: Cube Dev Team
 * @cube-dev/ui-kit v0.0.0-canary-3803e7e
 * Released under the MIT license.
 */

import { useRef, useState } from 'react';
import { useEvent } from '../../../_internal/index.js';
// ─── Constants ───────────────────────────────────────────────────────
const DEFAULT_NOTIFICATION_DURATION = 5000;
const DEFAULT_PERSISTENT_NOTIFICATION_DURATION = 10000;
const MAX_NOTIFICATIONS = 5;
// ─── Helpers ─────────────────────────────────────────────────────────
function getDuration(notif) {
    if (notif.duration === null)
        return null;
    if (notif.duration !== undefined)
        return notif.duration;
    return notif.persistent
        ? DEFAULT_PERSISTENT_NOTIFICATION_DURATION
        : DEFAULT_NOTIFICATION_DURATION;
}
function matchesNotificationId(item, id) {
    return item.id === id || item.internalId === String(id);
}
function findNotification(list, id) {
    return list.find((n) => matchesNotificationId(n, id));
}
// ─── Hook ────────────────────────────────────────────────────────────
/**
 * @param timersRef - Ref to the shared OverlayTimers instance. Accessed only
 *   inside callbacks (never during render), so it is guaranteed to be populated.
 */
export function useNotificationState(timersRef, persistent) {
    const [notifications, setNotifications] = useState([]);
    const notificationsRef = useRef(notifications);
    notificationsRef.current = notifications;
    const idCounter = useRef(0);
    // ─── Evict Helper ──────────────────────────────────────────────
    /**
     * Evict the oldest active notification to make room for a new one.
     * Clears its timer and moves it to the persistent list if applicable.
     * Must be called OUTSIDE setState updaters (StrictMode-safe).
     */
    const evictOldest = useEvent(() => {
        const active = notificationsRef.current.filter((n) => !n.isExiting);
        if (active.length === 0)
            return null;
        // Oldest by createdAt (active list preserves insertion order)
        const oldest = active[0];
        timersRef.current?.clearNotificationTimer(oldest.internalId);
        // Eviction counts as a timeout dismissal — move to persistent list if applicable
        if (oldest.persistent) {
            persistent.addPersistentItem({
                id: oldest.id ?? oldest.internalId,
                theme: oldest.theme,
                title: oldest.title,
                description: oldest.description,
                icon: oldest.icon,
                actions: oldest.actions,
                createdAt: oldest.createdAt,
                updatedAt: oldest.updatedAt,
                isRead: false,
                ownerId: oldest.ownerId,
            });
        }
        return oldest.internalId;
    });
    // ─── Remove ─────────────────────────────────────────────────────
    const removeNotification = useEvent((id, reason = 'api') => {
        // Read from ref (synchronous) to perform side effects OUTSIDE the updater.
        // This prevents double-fire in StrictMode and avoids calling setState
        // (addPersistentItem) inside another setState updater.
        const notif = findNotification(notificationsRef.current, id);
        if (!notif || notif.isExiting)
            return;
        timersRef.current?.clearNotificationTimer(notif.internalId);
        // If persistent and dismissed by timeout, move to persistent list.
        // User-initiated dismissals (action, close) intentionally skip persistence.
        if (notif.persistent && reason === 'timeout') {
            persistent.addPersistentItem({
                id: notif.id ?? notif.internalId,
                theme: notif.theme,
                title: notif.title,
                description: notif.description,
                icon: notif.icon,
                actions: notif.actions,
                createdAt: notif.createdAt,
                updatedAt: notif.updatedAt,
                isRead: false,
                ownerId: notif.ownerId,
            });
        }
        setNotifications((prev) => prev.map((n) => matchesNotificationId(n, id) ? { ...n, isExiting: true } : n));
    });
    // ─── Finalize ──────────────────────────────────────────────────
    const finalizeNotificationRemoval = useEvent((internalId) => {
        setNotifications((prev) => prev.filter((n) => n.internalId !== internalId));
    });
    // ─── Add ────────────────────────────────────────────────────────
    const addNotification = useEvent((options, ownerId) => {
        idCounter.current += 1;
        const internalId = `notif-${idCounter.current}-${Date.now()}`;
        const now = Date.now();
        const id = options.id ?? internalId;
        // Check for existing notification with same id via the ref (synchronous read).
        const existingNotif = options.id != null
            ? notificationsRef.current.find((n) => n.id === options.id && !n.isExiting)
            : undefined;
        if (existingNotif) {
            // In-place update of an existing notification
            const returnId = existingNotif.id ?? existingNotif.internalId;
            const prevTitle = existingNotif.title;
            const prevDescription = existingNotif.description;
            // Timer reset on string title/description change
            const titleChanged = typeof options.title === 'string' &&
                typeof prevTitle === 'string' &&
                options.title !== prevTitle;
            const descriptionChanged = typeof options.description === 'string' &&
                typeof prevDescription === 'string' &&
                options.description !== prevDescription;
            if (titleChanged || descriptionChanged) {
                const duration = getDuration(options);
                if (duration != null && duration > 0) {
                    timersRef.current?.startNotificationTimer(existingNotif.internalId, existingNotif.id ?? existingNotif.internalId, duration);
                }
            }
            setNotifications((prev) => prev.map((n) => {
                if (n.internalId === existingNotif.internalId) {
                    return {
                        ...n,
                        ...options,
                        internalId: n.internalId,
                        createdAt: n.createdAt,
                        updatedAt: now,
                        ownerId: ownerId ?? n.ownerId,
                    };
                }
                return n;
            }));
            return returnId;
        }
        // New notification — always show immediately, evicting the oldest if at cap.
        // Read from ref (synchronous) to perform side effects OUTSIDE the updater.
        const activeCount = notificationsRef.current.filter((n) => !n.isExiting).length;
        const newNotification = {
            ...options,
            id,
            internalId,
            createdAt: now,
            updatedAt: now,
            ownerId,
        };
        // Evict oldest if at cap (side effects outside updater)
        let evictedInternalId = null;
        if (activeCount >= MAX_NOTIFICATIONS) {
            evictedInternalId = evictOldest();
        }
        // Start timer for the new notification
        const duration = getDuration(options);
        if (duration != null && duration > 0) {
            timersRef.current?.startNotificationTimer(internalId, id, duration);
        }
        setNotifications((prev) => {
            let next = prev;
            if (evictedInternalId) {
                next = next.map((n) => n.internalId === evictedInternalId ? { ...n, isExiting: true } : n);
            }
            return [...next, newNotification];
        });
        return id;
    });
    // ─── Update ─────────────────────────────────────────────────────
    const updateNotification = useEvent((id, options) => {
        setNotifications((prev) => prev.map((n) => matchesNotificationId(n, id)
            ? { ...n, ...options, updatedAt: Date.now() }
            : n));
    });
    // ─── Owner Cleanup ──────────────────────────────────────────────
    const removeByOwner = useEvent((ownerId) => {
        // Clear timers outside the updater (StrictMode-safe).
        const owned = notificationsRef.current.filter((n) => n.ownerId === ownerId && !n.isExiting);
        for (const n of owned) {
            timersRef.current?.clearNotificationTimer(n.internalId);
        }
        setNotifications((prev) => prev.map((n) => {
            if (n.ownerId === ownerId && !n.isExiting) {
                return { ...n, isExiting: true };
            }
            return n;
        }));
    });
    return {
        notifications,
        notificationsRef,
        addNotification,
        removeNotification,
        updateNotification,
        finalizeNotificationRemoval,
        removeByOwner,
    };
}

        