/**
 * In-App Notification Channel
 * Handles in-application notifications with persistence
 */

import { InAppNotification, NotificationType } from '../types';

export class InAppChannel {
  private notifications: Map<string, InAppNotification[]> = new Map();
  private maxNotificationsPerUser = 100;

  /**
   * Send an in-app notification
   */
  async send(notification: InAppNotification): Promise<void> {
    const userNotifications = this.notifications.get(notification.userId) || [];

    // Add new notification at the beginning
    userNotifications.unshift(notification);

    // Trim to max
    if (userNotifications.length > this.maxNotificationsPerUser) {
      userNotifications.splice(this.maxNotificationsPerUser);
    }

    this.notifications.set(notification.userId, userNotifications);
    console.log(`[InAppChannel] Notification added for user ${notification.userId}`);
  }

  /**
   * Get notifications for a user
   */
  getNotifications(userId: string, options?: {
    unreadOnly?: boolean;
    type?: NotificationType;
    limit?: number;
    offset?: number;
  }): InAppNotification[] {
    let userNotifications = this.notifications.get(userId) || [];

    // Filter by unread
    if (options?.unreadOnly) {
      userNotifications = userNotifications.filter(n => !n.read);
    }

    // Filter by type
    if (options?.type) {
      userNotifications = userNotifications.filter(n => n.type === options.type);
    }

    // Filter expired
    const now = new Date().toISOString();
    userNotifications = userNotifications.filter(n =>
      !n.expiresAt || n.expiresAt > now
    );

    // Apply pagination
    const offset = options?.offset || 0;
    const limit = options?.limit || 50;

    return userNotifications.slice(offset, offset + limit);
  }

  /**
   * Get notification by ID
   */
  getNotification(notificationId: string): InAppNotification | undefined {
    for (const userNotifications of this.notifications.values()) {
      const notification = userNotifications.find(n => n.id === notificationId);
      if (notification) return notification;
    }
    return undefined;
  }

  /**
   * Mark notification as read
   */
  markAsRead(notificationId: string): boolean {
    for (const [userId, userNotifications] of this.notifications.entries()) {
      const notification = userNotifications.find(n => n.id === notificationId);
      if (notification) {
        notification.read = true;
        return true;
      }
    }
    return false;
  }

  /**
   * Mark all notifications as read for a user
   */
  markAllAsRead(userId: string): number {
    const userNotifications = this.notifications.get(userId);
    if (!userNotifications) return 0;

    let count = 0;
    userNotifications.forEach(n => {
      if (!n.read) {
        n.read = true;
        count++;
      }
    });

    return count;
  }

  /**
   * Get unread count for a user
   */
  getUnreadCount(userId: string): number {
    const userNotifications = this.notifications.get(userId) || [];
    return userNotifications.filter(n => !n.read).length;
  }

  /**
   * Delete a notification
   */
  delete(notificationId: string): boolean {
    for (const [userId, userNotifications] of this.notifications.entries()) {
      const index = userNotifications.findIndex(n => n.id === notificationId);
      if (index !== -1) {
        userNotifications.splice(index, 1);
        return true;
      }
    }
    return false;
  }

  /**
   * Delete all notifications for a user
   */
  deleteAll(userId: string): number {
    const userNotifications = this.notifications.get(userId);
    if (!userNotifications) return 0;

    const count = userNotifications.length;
    this.notifications.delete(userId);
    return count;
  }

  /**
   * Clean up expired notifications
   */
  cleanupExpired(): number {
    const now = new Date().toISOString();
    let removed = 0;

    for (const [userId, userNotifications] of this.notifications.entries()) {
      const originalLength = userNotifications.length;
      const filtered = userNotifications.filter(n =>
        !n.expiresAt || n.expiresAt > now
      );
      removed += originalLength - filtered.length;
      this.notifications.set(userId, filtered);
    }

    return removed;
  }

  /**
   * Get stats for a user
   */
  getStats(userId: string): {
    total: number;
    unread: number;
    byType: Record<NotificationType, number>;
  } {
    const userNotifications = this.notifications.get(userId) || [];

    const byType: Record<string, number> = {};
    userNotifications.forEach(n => {
      byType[n.type] = (byType[n.type] || 0) + 1;
    });

    return {
      total: userNotifications.length,
      unread: userNotifications.filter(n => !n.read).length,
      byType: byType as Record<NotificationType, number>
    };
  }

  /**
   * Group notifications by date
   */
  getGroupedByDate(userId: string): {
    today: InAppNotification[];
    yesterday: InAppNotification[];
    thisWeek: InAppNotification[];
    older: InAppNotification[];
  } {
    const userNotifications = this.notifications.get(userId) || [];
    const now = new Date();
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
    const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);

    const result = {
      today: [] as InAppNotification[],
      yesterday: [] as InAppNotification[],
      thisWeek: [] as InAppNotification[],
      older: [] as InAppNotification[]
    };

    userNotifications.forEach(n => {
      const createdAt = new Date(n.createdAt);
      if (createdAt >= today) {
        result.today.push(n);
      } else if (createdAt >= yesterday) {
        result.yesterday.push(n);
      } else if (createdAt >= weekAgo) {
        result.thisWeek.push(n);
      } else {
        result.older.push(n);
      }
    });

    return result;
  }
}
