import { EventEmitter } from 'events';

export interface NotificationEvent {
  id: string;
  notificationId: string;
  userId: string;
  eventType: 'sent' | 'delivered' | 'opened' | 'clicked' | 'dismissed';
  timestamp: Date;
  channel: 'push' | 'email' | 'in_app';
  metadata?: Record<string, any>;
}

export interface NotificationMetrics {
  notificationId: string;
  sent: number;
  delivered: number;
  opened: number;
  clicked: number;
  dismissed: number;
  deliveryRate: number;
  openRate: number;
  clickRate: number;
}

class NotificationAnalyticsService extends EventEmitter {
  private events: Map<string, NotificationEvent> = new Map();
  private notificationMetrics: Map<string, NotificationMetrics> = new Map();

  constructor() {
    super();
  }

  /**
   * Track notification event
   */
  trackEvent(
    notificationId: string,
    userId: string,
    eventType: NotificationEvent['eventType'],
    channel: NotificationEvent['channel'],
    metadata?: Record<string, any>
  ): NotificationEvent {
    const eventId = `event-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    const event: NotificationEvent = {
      id: eventId,
      notificationId,
      userId,
      eventType,
      timestamp: new Date(),
      channel,
      metadata,
    };

    this.events.set(eventId, event);
    this.updateMetrics(notificationId, eventType);
    this.emit('notification_event', event);

    return event;
  }

  /**
   * Update metrics for a notification
   */
  private updateMetrics(notificationId: string, eventType: NotificationEvent['eventType']): void {
    let metrics = this.notificationMetrics.get(notificationId);

    if (!metrics) {
      metrics = {
        notificationId,
        sent: 0,
        delivered: 0,
        opened: 0,
        clicked: 0,
        dismissed: 0,
        deliveryRate: 0,
        openRate: 0,
        clickRate: 0,
      };
      this.notificationMetrics.set(notificationId, metrics);
    }

    switch (eventType) {
      case 'sent':
        metrics.sent++;
        break;
      case 'delivered':
        metrics.delivered++;
        break;
      case 'opened':
        metrics.opened++;
        break;
      case 'clicked':
        metrics.clicked++;
        break;
      case 'dismissed':
        metrics.dismissed++;
        break;
    }

    // Recalculate rates
    if (metrics.sent > 0) {
      metrics.deliveryRate = (metrics.delivered / metrics.sent) * 100;
      metrics.openRate = (metrics.opened / metrics.sent) * 100;
      metrics.clickRate = (metrics.clicked / metrics.sent) * 100;
    }
  }

  /**
   * Get metrics for a notification
   */
  getMetrics(notificationId: string): NotificationMetrics | null {
    return this.notificationMetrics.get(notificationId) || null;
  }

  /**
   * Get all metrics
   */
  getAllMetrics(): NotificationMetrics[] {
    return Array.from(this.notificationMetrics.values());
  }

  /**
   * Get events for a notification
   */
  getNotificationEvents(notificationId: string): NotificationEvent[] {
    return Array.from(this.events.values())
      .filter((e) => e.notificationId === notificationId)
      .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
  }

  /**
   * Get events for a user
   */
  getUserEvents(userId: string, limit: number = 100): NotificationEvent[] {
    return Array.from(this.events.values())
      .filter((e) => e.userId === userId)
      .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())
      .slice(0, limit);
  }

  /**
   * Get events by channel
   */
  getEventsByChannel(channel: NotificationEvent['channel']): NotificationEvent[] {
    return Array.from(this.events.values())
      .filter((e) => e.channel === channel)
      .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
  }

  /**
   * Get events by type
   */
  getEventsByType(eventType: NotificationEvent['eventType']): NotificationEvent[] {
    return Array.from(this.events.values())
      .filter((e) => e.eventType === eventType)
      .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
  }

  /**
   * Get analytics summary
   */
  getAnalyticsSummary(startDate?: Date, endDate?: Date): {
    totalEvents: number;
    eventsByType: Record<string, number>;
    eventsByChannel: Record<string, number>;
    averageDeliveryRate: number;
    averageOpenRate: number;
    averageClickRate: number;
  } {
    const start = startDate || new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); // 30 days ago
    const end = endDate || new Date();

    const filteredEvents = Array.from(this.events.values()).filter(
      (e) => e.timestamp >= start && e.timestamp <= end
    );

    const eventsByType: Record<string, number> = {};
    const eventsByChannel: Record<string, number> = {};

    for (const event of filteredEvents) {
      eventsByType[event.eventType] = (eventsByType[event.eventType] || 0) + 1;
      eventsByChannel[event.channel] = (eventsByChannel[event.channel] || 0) + 1;
    }

    // Calculate average rates
    const metrics = Array.from(this.notificationMetrics.values());
    const avgDeliveryRate = metrics.length > 0
      ? metrics.reduce((sum, m) => sum + m.deliveryRate, 0) / metrics.length
      : 0;
    const avgOpenRate = metrics.length > 0
      ? metrics.reduce((sum, m) => sum + m.openRate, 0) / metrics.length
      : 0;
    const avgClickRate = metrics.length > 0
      ? metrics.reduce((sum, m) => sum + m.clickRate, 0) / metrics.length
      : 0;

    return {
      totalEvents: filteredEvents.length,
      eventsByType,
      eventsByChannel,
      averageDeliveryRate: avgDeliveryRate,
      averageOpenRate: avgOpenRate,
      averageClickRate: avgClickRate,
    };
  }

  /**
   * Get top performing notifications
   */
  getTopPerformingNotifications(limit: number = 10): NotificationMetrics[] {
    return Array.from(this.notificationMetrics.values())
      .sort((a, b) => b.openRate - a.openRate)
      .slice(0, limit);
  }

  /**
   * Get channel performance
   */
  getChannelPerformance(): Record<string, { sent: number; delivered: number; opened: number }> {
    const performance: Record<string, { sent: number; delivered: number; opened: number }> = {
      push: { sent: 0, delivered: 0, opened: 0 },
      email: { sent: 0, delivered: 0, opened: 0 },
      in_app: { sent: 0, delivered: 0, opened: 0 },
    };

    for (const event of this.events.values()) {
      const channel = event.channel;
      if (event.eventType === 'sent') performance[channel].sent++;
      if (event.eventType === 'delivered') performance[channel].delivered++;
      if (event.eventType === 'opened') performance[channel].opened++;
    }

    return performance;
  }

  /**
   * Clear old events
   */
  clearOldEvents(daysOld: number = 90): number {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - daysOld);

    let deleted = 0;

    for (const [id, event] of this.events) {
      if (event.timestamp < cutoffDate) {
        this.events.delete(id);
        deleted++;
      }
    }

    return deleted;
  }
}

// Export singleton instance
export const notificationAnalyticsService = new NotificationAnalyticsService();

export default NotificationAnalyticsService;
