/**
 * @fileoverview Manages application-wide notifications.
 * Handles persistence via IndexedDB, real-time updates via SSE, and state management.
 */

import dssDB from '../db/indexed-db.js';

const NOTIFICATION_STORE = 'notifications';

class NotificationService extends EventTarget {
  constructor() {
    super();
    this.notifications = [];
    this.unreadCount = 0;
    this._eventSource = null;
    this._initialized = false;
    this._broadcastChannel = null;
  }

  async init() {
    if (this._initialized) return;
    this._initialized = true;

    await this._loadFromStorage();
    this._connectToEvents();
    this._setupCrossTabSync();
  }

  /**
   * Setup cross-tab synchronization via BroadcastChannel API
   * When notifications are modified in another tab, reload them
   */
  _setupCrossTabSync() {
    try {
      this._broadcastChannel = new BroadcastChannel('dss-notifications');

      this._broadcastChannel.onmessage = (event) => {
        if (event.data?.type === 'notifications-updated') {
          console.log('[NotificationService] Notifications updated in another tab, reloading...');
          this._loadFromStorage();
        }
      };
    } catch (error) {
      console.warn('[NotificationService] BroadcastChannel not available:', error);
    }
  }

  /**
   * Notify other tabs about notification changes
   * @private
   */
  _notifyOtherTabs() {
    if (this._broadcastChannel) {
      try {
        this._broadcastChannel.postMessage({ type: 'notifications-updated' });
      } catch (error) {
        console.warn('[NotificationService] Failed to broadcast update:', error);
      }
    }
  }

  async _loadFromStorage() {
    try {
      this.notifications = await dssDB.getAll(NOTIFICATION_STORE) || [];
      this._sortNotifications();
      this._updateUnreadCount();
      this.dispatchEvent(new CustomEvent('notifications-updated', {
        detail: { notifications: this.notifications }
      }));
    } catch (error) {
      console.error('Failed to load notifications from storage:', error);
      this.notifications = [];
    }
  }

  _connectToEvents() {
    // Only connect if SSE endpoint exists and we have an auth token
    try {
      // Get access token from localStorage
      const authTokens = localStorage.getItem('auth_tokens');
      if (!authTokens) {
        console.log('[NotificationService] No auth token available, skipping SSE connection');
        return;
      }

      const { accessToken } = JSON.parse(authTokens);
      if (!accessToken) {
        console.log('[NotificationService] No access token found, skipping SSE connection');
        return;
      }

      // Construct SSE URL with token parameter (EventSource can't send custom headers)
      const sseUrl = `/api/notifications/events?token=${encodeURIComponent(accessToken)}`;
      console.log('[NotificationService] Connecting to SSE with authentication...');

      this._eventSource = new EventSource(sseUrl);

      this._eventSource.onmessage = (event) => {
        try {
          const notificationData = JSON.parse(event.data);
          console.log('[NotificationService] SSE notification received:', notificationData.type);
          // From SSE, persist to local storage
          this.create(notificationData, true);
        } catch (error) {
          console.error('[NotificationService] Error parsing SSE notification:', error);
        }
      };

      this._eventSource.onerror = (err) => {
        console.warn('[NotificationService] SSE connection error, using local-only mode');
        this._eventSource.close();
        this._eventSource = null;
      };

      this._eventSource.onopen = () => {
        console.log('[NotificationService] ✅ SSE connection established successfully');
      };
    } catch (error) {
      console.warn('[NotificationService] SSE setup error:', error);
    }
  }

  _sortNotifications() {
    this.notifications.sort((a, b) => b.timestamp - a.timestamp);
  }

  _updateUnreadCount() {
    const newCount = this.notifications.filter(n => !n.read).length;
    if (newCount !== this.unreadCount) {
      this.unreadCount = newCount;
      this.dispatchEvent(new CustomEvent('unread-count-changed', {
        detail: { count: this.unreadCount }
      }));
    }
  }

  _dispatchUpdate() {
    this._sortNotifications();
    this._updateUnreadCount();
    this.dispatchEvent(new CustomEvent('notifications-updated', {
      detail: { notifications: this.notifications }
    }));
  }

  /**
   * Creates a new notification.
   * @param {object} notificationData - The notification data.
   * @param {string} notificationData.title - Notification title
   * @param {string} [notificationData.message] - Notification message
   * @param {string} [notificationData.type='info'] - 'info', 'success', 'warning', 'error'
   * @param {string} [notificationData.source] - Source service name
   * @param {Array} [notificationData.actions] - Action buttons
   * @param {boolean} [persist=true] - Whether to save to IndexedDB.
   * @returns {Promise<object>} The created notification.
   */
  async create(notificationData, persist = true) {
    const newNotification = {
      id: crypto.randomUUID(),
      timestamp: Date.now(),
      read: false,
      type: 'info',
      ...notificationData,
    };

    this.notifications.unshift(newNotification);

    if (persist) {
      try {
        await dssDB.put(NOTIFICATION_STORE, newNotification);
      } catch (error) {
        console.error('Failed to persist notification:', error);
      }
    }

    this._dispatchUpdate();
    this._notifyOtherTabs();

    // Also show as toast for immediate visibility
    if (typeof window.showToast === 'function') {
      window.showToast({
        message: `<strong>${newNotification.title}</strong>${newNotification.message ? `<br>${newNotification.message}` : ''}`,
        type: newNotification.type,
        duration: 5000,
        dismissible: true
      });
    }

    return newNotification;
  }

  /**
   * Retrieves all notifications.
   * @returns {object[]}
   */
  getAll() {
    return [...this.notifications];
  }

  /**
   * Get unread count
   * @returns {number}
   */
  getUnreadCount() {
    return this.unreadCount;
  }

  /**
   * Marks a specific notification as read.
   * @param {string} id - The ID of the notification to update.
   * @returns {Promise<void>}
   */
  async markAsRead(id) {
    const notification = this.notifications.find(n => n.id === id);
    if (notification && !notification.read) {
      notification.read = true;
      try {
        await dssDB.put(NOTIFICATION_STORE, notification);
      } catch (error) {
        console.error('Failed to update notification:', error);
      }
      this._dispatchUpdate();
      this._notifyOtherTabs();
    }
  }

  /**
   * Marks all unread notifications as read.
   * @returns {Promise<void>}
   */
  async markAllAsRead() {
    const updates = [];
    this.notifications.forEach(n => {
      if (!n.read) {
        n.read = true;
        updates.push(dssDB.put(NOTIFICATION_STORE, n));
      }
    });

    if (updates.length > 0) {
      try {
        await Promise.all(updates);
      } catch (error) {
        console.error('Failed to mark all as read:', error);
      }
      this._dispatchUpdate();
      this._notifyOtherTabs();
    }
  }

  /**
   * Deletes a notification by its ID.
   * @param {string} id
   * @returns {Promise<void>}
   */
  async delete(id) {
    this.notifications = this.notifications.filter(n => n.id !== id);
    try {
      await dssDB.delete(NOTIFICATION_STORE, id);
    } catch (error) {
      console.error('Failed to delete notification:', error);
    }
    this._dispatchUpdate();
    this._notifyOtherTabs();
  }

  /**
   * Clears all notifications.
   * @returns {Promise<void>}
   */
  async clearAll() {
    this.notifications = [];
    try {
      await dssDB.clear(NOTIFICATION_STORE);
    } catch (error) {
      console.error('Failed to clear notifications:', error);
    }
    this._dispatchUpdate();
    this._notifyOtherTabs();
  }

  /**
   * Cleanup on app shutdown
   */
  destroy() {
    if (this._eventSource) {
      this._eventSource.close();
      this._eventSource = null;
    }
    if (this._broadcastChannel) {
      this._broadcastChannel.close();
      this._broadcastChannel = null;
    }
  }
}

// Export a singleton instance
const notificationService = new NotificationService();
export default notificationService;
