import { EventEmitter } from 'events';

export type NotificationType = 'promotion' | 'announcement' | 'event' | 'reward' | 'engagement' | 'system';
export type NotificationChannel = 'push' | 'email' | 'in_app';

export interface UserPreference {
  userId: string;
  notificationType: NotificationType;
  channel: NotificationChannel;
  enabled: boolean;
  frequency: 'instant' | 'daily' | 'weekly' | 'never';
  optimalHour?: number;
  lastUpdated: Date;
}

export interface UserEngagementMetrics {
  userId: string;
  totalNotificationsReceived: number;
  totalNotificationsOpened: number;
  totalNotificationsClicked: number;
  openRate: number;
  clickRate: number;
  preferredNotificationType: NotificationType;
  preferredChannel: NotificationChannel;
  preferredHour: number;
  lastEngagementDate?: Date;
}

export interface SegmentPreferences {
  segment: string;
  notificationTypePreferences: Record<NotificationType, number>;
  channelPreferences: Record<NotificationChannel, number>;
  optimalSendHours: number[];
  averageOpenRate: number;
  averageClickRate: number;
}

class UserPreferenceAnalyticsService extends EventEmitter {
  private userPreferences: Map<string, UserPreference[]> = new Map();
  private engagementMetrics: Map<string, UserEngagementMetrics> = new Map();
  private segmentAnalysis: Map<string, SegmentPreferences> = new Map();

  constructor() {
    super();
  }

  /**
   * Set user preference
   */
  setUserPreference(
    userId: string,
    notificationType: NotificationType,
    channel: NotificationChannel,
    enabled: boolean,
    frequency: 'instant' | 'daily' | 'weekly' | 'never' = 'instant'
  ): UserPreference {
    const preferences = this.userPreferences.get(userId) || [];
    const existingIndex = preferences.findIndex(
      (p) => p.notificationType === notificationType && p.channel === channel
    );

    const preference: UserPreference = {
      userId,
      notificationType,
      channel,
      enabled,
      frequency,
      lastUpdated: new Date(),
    };

    if (existingIndex !== -1) {
      preferences[existingIndex] = preference;
    } else {
      preferences.push(preference);
    }

    this.userPreferences.set(userId, preferences);
    this.emit('preference_updated', preference);

    return preference;
  }

  /**
   * Get user preferences
   */
  getUserPreferences(userId: string): UserPreference[] {
    return this.userPreferences.get(userId) || [];
  }

  /**
   * Track user engagement
   */
  trackUserEngagement(
    userId: string,
    notificationType: NotificationType,
    channel: NotificationChannel,
    eventType: 'received' | 'opened' | 'clicked',
    hour: number
  ): void {
    let metrics = this.engagementMetrics.get(userId);

    if (!metrics) {
      metrics = {
        userId,
        totalNotificationsReceived: 0,
        totalNotificationsOpened: 0,
        totalNotificationsClicked: 0,
        openRate: 0,
        clickRate: 0,
        preferredNotificationType: notificationType,
        preferredChannel: channel,
        preferredHour: hour,
      };
    }

    switch (eventType) {
      case 'received':
        metrics.totalNotificationsReceived++;
        break;
      case 'opened':
        metrics.totalNotificationsOpened++;
        break;
      case 'clicked':
        metrics.totalNotificationsClicked++;
        break;
    }

    // Update rates
    if (metrics.totalNotificationsReceived > 0) {
      metrics.openRate = (metrics.totalNotificationsOpened / metrics.totalNotificationsReceived) * 100;
      metrics.clickRate = (metrics.totalNotificationsClicked / metrics.totalNotificationsReceived) * 100;
    }

    metrics.lastEngagementDate = new Date();
    this.engagementMetrics.set(userId, metrics);

    this.emit('engagement_tracked', { userId, eventType });
  }

  /**
   * Get user engagement metrics
   */
  getUserEngagementMetrics(userId: string): UserEngagementMetrics | null {
    return this.engagementMetrics.get(userId) || null;
  }

  /**
   * Get optimal send time for user
   */
  getOptimalSendTime(userId: string): { hour: number; confidence: number } {
    const metrics = this.engagementMetrics.get(userId);

    if (!metrics) {
      return { hour: 9, confidence: 0 }; // Default morning time
    }

    // Use preferred hour with confidence based on engagement
    const confidence = Math.min(100, metrics.openRate);

    return {
      hour: metrics.preferredHour,
      confidence,
    };
  }

  /**
   * Get notification type preferences for user
   */
  getNotificationTypePreferences(userId: string): Record<NotificationType, boolean> {
    const preferences = this.userPreferences.get(userId) || [];
    const typePrefs: Record<NotificationType, boolean> = {
      promotion: true,
      announcement: true,
      event: true,
      reward: true,
      engagement: true,
      system: true,
    };

    for (const pref of preferences) {
      if (pref.channel === 'push') {
        typePrefs[pref.notificationType] = pref.enabled;
      }
    }

    return typePrefs;
  }

  /**
   * Get channel preferences for user
   */
  getChannelPreferences(userId: string): Record<NotificationChannel, boolean> {
    const preferences = this.userPreferences.get(userId) || [];
    const channelPrefs: Record<NotificationChannel, boolean> = {
      push: true,
      email: true,
      in_app: true,
    };

    for (const pref of preferences) {
      channelPrefs[pref.channel] = pref.enabled;
    }

    return channelPrefs;
  }

  /**
   * Analyze segment preferences
   */
  analyzeSegmentPreferences(userIds: string[], segment: string): SegmentPreferences {
    const typePreferences: Record<NotificationType, number> = {
      promotion: 0,
      announcement: 0,
      event: 0,
      reward: 0,
      engagement: 0,
      system: 0,
    };

    const channelPreferences: Record<NotificationChannel, number> = {
      push: 0,
      email: 0,
      in_app: 0,
    };

    let totalOpenRate = 0;
    let totalClickRate = 0;
    const hourCounts: Record<number, number> = {};

    for (const userId of userIds) {
      const preferences = this.userPreferences.get(userId) || [];
      const metrics = this.engagementMetrics.get(userId);

      for (const pref of preferences) {
        if (pref.enabled) {
          typePreferences[pref.notificationType]++;
          channelPreferences[pref.channel]++;
        }
      }

      if (metrics) {
        totalOpenRate += metrics.openRate;
        totalClickRate += metrics.clickRate;
        hourCounts[metrics.preferredHour] = (hourCounts[metrics.preferredHour] || 0) + 1;
      }
    }

    // Normalize preferences
    const userCount = userIds.length;
    for (const type of Object.keys(typePreferences) as NotificationType[]) {
      typePreferences[type] = (typePreferences[type] / userCount) * 100;
    }
    for (const channel of Object.keys(channelPreferences) as NotificationChannel[]) {
      channelPreferences[channel] = (channelPreferences[channel] / userCount) * 100;
    }

    // Get optimal hours
    const optimalHours = Object.entries(hourCounts)
      .sort(([, a], [, b]) => b - a)
      .slice(0, 3)
      .map(([hour]) => parseInt(hour));

    const segmentPrefs: SegmentPreferences = {
      segment,
      notificationTypePreferences: typePreferences,
      channelPreferences: channelPreferences,
      optimalSendHours: optimalHours,
      averageOpenRate: userCount > 0 ? totalOpenRate / userCount : 0,
      averageClickRate: userCount > 0 ? totalClickRate / userCount : 0,
    };

    this.segmentAnalysis.set(segment, segmentPrefs);
    this.emit('segment_analyzed', segmentPrefs);

    return segmentPrefs;
  }

  /**
   * Get segment preferences
   */
  getSegmentPreferences(segment: string): SegmentPreferences | null {
    return this.segmentAnalysis.get(segment) || null;
  }

  /**
   * Get all segment preferences
   */
  getAllSegmentPreferences(): SegmentPreferences[] {
    return Array.from(this.segmentAnalysis.values());
  }

  /**
   * Get recommendation for user
   */
  getRecommendation(userId: string): {
    bestNotificationType: NotificationType;
    bestChannel: NotificationChannel;
    optimalHour: number;
    frequency: string;
  } {
    const preferences = this.userPreferences.get(userId) || [];
    const metrics = this.engagementMetrics.get(userId);

    // Find best notification type
    let bestType: NotificationType = 'promotion';
    if (preferences.length > 0) {
      const enabledTypes = preferences.filter((p) => p.enabled).map((p) => p.notificationType);
      if (enabledTypes.length > 0) {
        bestType = enabledTypes[0];
      }
    }

    // Find best channel
    let bestChannel: NotificationChannel = 'push';
    const enabledChannels = preferences.filter((p) => p.enabled).map((p) => p.channel);
    if (enabledChannels.length > 0) {
      bestChannel = enabledChannels[0];
    }

    const optimalTime = this.getOptimalSendTime(userId);

    return {
      bestNotificationType: bestType,
      bestChannel: bestChannel,
      optimalHour: optimalTime.hour,
      frequency: metrics?.openRate || 0 > 50 ? 'daily' : 'weekly',
    };
  }

  /**
   * Get user segments by preference
   */
  getUserSegmentsByPreference(
    notificationType: NotificationType,
    channel: NotificationChannel
  ): string[] {
    const segments: string[] = [];

    for (const [segment, prefs] of this.segmentAnalysis) {
      if (
        prefs.notificationTypePreferences[notificationType] > 50 &&
        prefs.channelPreferences[channel] > 50
      ) {
        segments.push(segment);
      }
    }

    return segments;
  }

  /**
   * Get analytics summary
   */
  getAnalyticsSummary(): {
    totalUsers: number;
    totalPreferences: number;
    averageOpenRate: number;
    averageClickRate: number;
    mostPreferredType: NotificationType;
    mostPreferredChannel: NotificationChannel;
  } {
    let totalOpenRate = 0;
    let totalClickRate = 0;
    const typeCount: Record<NotificationType, number> = {
      promotion: 0,
      announcement: 0,
      event: 0,
      reward: 0,
      engagement: 0,
      system: 0,
    };
    const channelCount: Record<NotificationChannel, number> = {
      push: 0,
      email: 0,
      in_app: 0,
    };

    for (const metrics of this.engagementMetrics.values()) {
      totalOpenRate += metrics.openRate;
      totalClickRate += metrics.clickRate;
    }

    for (const prefs of this.userPreferences.values()) {
      for (const pref of prefs) {
        if (pref.enabled) {
          typeCount[pref.notificationType]++;
          channelCount[pref.channel]++;
        }
      }
    }

    const userCount = this.engagementMetrics.size;
    const mostPreferredType = Object.entries(typeCount).reduce((a, b) =>
      b[1] > a[1] ? b : a
    )[0] as NotificationType;
    const mostPreferredChannel = Object.entries(channelCount).reduce((a, b) =>
      b[1] > a[1] ? b : a
    )[0] as NotificationChannel;

    return {
      totalUsers: userCount,
      totalPreferences: this.userPreferences.size,
      averageOpenRate: userCount > 0 ? totalOpenRate / userCount : 0,
      averageClickRate: userCount > 0 ? totalClickRate / userCount : 0,
      mostPreferredType,
      mostPreferredChannel,
    };
  }
}

// Export singleton instance
export const userPreferenceAnalyticsService = new UserPreferenceAnalyticsService();

export default UserPreferenceAnalyticsService;
