import { writeAuditLog } from './db.ts';

export type NotificationType = 'achievement' | 'challenge' | 'leaderboard' | 'event' | 'friend' | 'promotion' | 'system';
export type NotificationPriority = 'low' | 'normal' | 'high' | 'critical';

export interface PushNotification {
  id: string;
  userId: number;
  type: NotificationType;
  priority: NotificationPriority;
  title: string;
  body: string;
  icon?: string;
  image?: string;
  data?: Record<string, any>;
  actionUrl?: string;
  sent: boolean;
  sentAt?: Date;
  read: boolean;
  readAt?: Date;
  createdAt: Date;
  expiresAt: Date;
}

export interface NotificationPreferences {
  userId: number;
  achievements: boolean;
  challenges: boolean;
  leaderboard: boolean;
  events: boolean;
  friends: boolean;
  promotions: boolean;
  system: boolean;
  quietHoursEnabled: boolean;
  quietHoursStart?: string; // HH:MM
  quietHoursEnd?: string; // HH:MM
  timezone?: string;
  pushEnabled: boolean;
  emailEnabled: boolean;
  smsEnabled: boolean;
  updatedAt: Date;
}

/**
 * Push Notifications Manager
 */
export class PushNotificationsManager {
  private notifications: Map<string, PushNotification> = new Map();
  private userNotifications: Map<number, string[]> = new Map(); // userId -> notificationIds
  private preferences: Map<number, NotificationPreferences> = new Map();
  private notificationQueue: string[] = [];

  /**
   * Initialize default preferences for user
   */
  async initializeUserPreferences(userId: number): Promise<NotificationPreferences> {
    if (this.preferences.has(userId)) {
      return this.preferences.get(userId)!;
    }

    const prefs: NotificationPreferences = {
      userId,
      achievements: true,
      challenges: true,
      leaderboard: true,
      events: true,
      friends: true,
      promotions: true,
      system: true,
      quietHoursEnabled: false,
      pushEnabled: true,
      emailEnabled: false,
      smsEnabled: false,
      updatedAt: new Date(),
    };

    this.preferences.set(userId, prefs);
    return prefs;
  }

  /**
   * Get user preferences
   */
  async getUserPreferences(userId: number): Promise<NotificationPreferences> {
    let prefs = this.preferences.get(userId);
    if (!prefs) {
      prefs = await this.initializeUserPreferences(userId);
    }
    return prefs;
  }

  /**
   * Update user preferences
   */
  async updatePreferences(userId: number, updates: Partial<NotificationPreferences>): Promise<NotificationPreferences> {
    const prefs = await this.getUserPreferences(userId);

    Object.assign(prefs, updates, { updatedAt: new Date() });
    this.preferences.set(userId, prefs);

    await writeAuditLog({
      actorId: userId,
      actorRole: 'user',
      action: 'notification_preferences_updated',
      category: 'notification',
      details: updates,
    });

    return prefs;
  }

  /**
   * Check if notification type is enabled for user
   */
  private async isNotificationTypeEnabled(userId: number, type: NotificationType): Promise<boolean> {
    const prefs = await this.getUserPreferences(userId);

    switch (type) {
      case 'achievement':
        return prefs.achievements;
      case 'challenge':
        return prefs.challenges;
      case 'leaderboard':
        return prefs.leaderboard;
      case 'event':
        return prefs.events;
      case 'friend':
        return prefs.friends;
      case 'promotion':
        return prefs.promotions;
      case 'system':
        return prefs.system;
      default:
        return false;
    }
  }

  /**
   * Check if user is in quiet hours
   */
  private isInQuietHours(prefs: NotificationPreferences): boolean {
    if (!prefs.quietHoursEnabled || !prefs.quietHoursStart || !prefs.quietHoursEnd) {
      return false;
    }

    const now = new Date();
    const currentTime = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0');

    // Simple comparison (doesn't handle midnight crossing)
    return currentTime >= prefs.quietHoursStart && currentTime <= prefs.quietHoursEnd;
  }

  /**
   * Send notification
   */
  async sendNotification(
    userId: number,
    type: NotificationType,
    title: string,
    body: string,
    options?: {
      priority?: NotificationPriority;
      icon?: string;
      image?: string;
      data?: Record<string, any>;
      actionUrl?: string;
      expiresIn?: number; // milliseconds
    }
  ): Promise<PushNotification | null> {
    // Check if notification type is enabled
    const isEnabled = await this.isNotificationTypeEnabled(userId, type);
    if (!isEnabled) {
      return null;
    }

    const prefs = await this.getUserPreferences(userId);

    // Check quiet hours
    if (this.isInQuietHours(prefs) && options?.priority !== 'critical') {
      return null;
    }

    const notification: PushNotification = {
      id: `notif_${Date.now()}_${Math.random().toString(36).substring(7)}`,
      userId,
      type,
      priority: options?.priority || 'normal',
      title,
      body,
      icon: options?.icon,
      image: options?.image,
      data: options?.data,
      actionUrl: options?.actionUrl,
      sent: false,
      read: false,
      createdAt: new Date(),
      expiresAt: new Date(Date.now() + (options?.expiresIn || 7 * 24 * 60 * 60 * 1000)), // 7 days default
    };

    this.notifications.set(notification.id, notification);

    if (!this.userNotifications.has(userId)) {
      this.userNotifications.set(userId, []);
    }
    this.userNotifications.get(userId)!.push(notification.id);

    // Queue for sending
    this.notificationQueue.push(notification.id);

    // Log notification
    await writeAuditLog({
      actorId: userId,
      actorRole: 'system',
      action: 'notification_sent',
      category: 'notification',
      details: { notificationType: type, title },
    });

    return notification;
  }

  /**
   * Send achievement notification
   */
  async notifyAchievementUnlock(userId: number, achievementName: string, reward: number): Promise<void> {
    await this.sendNotification(
      userId,
      'achievement',
      '🏆 Achievement Unlocked!',
      `You unlocked "${achievementName}" and earned ${reward} points!`,
      {
        priority: 'high',
        icon: '🏆',
        data: { achievementName, reward },
      }
    );
  }

  /**
   * Send challenge notification
   */
  async notifyChallengeCompletion(userId: number, challengeName: string, reward: number): Promise<void> {
    await this.sendNotification(
      userId,
      'challenge',
      '🎯 Challenge Completed!',
      `You completed "${challengeName}" and earned ${reward} SC!`,
      {
        priority: 'high',
        icon: '🎯',
        data: { challengeName, reward },
      }
    );
  }

  /**
   * Send leaderboard notification
   */
  async notifyLeaderboardMilestone(userId: number, rank: number, category: string): Promise<void> {
    let message = '';
    if (rank === 1) {
      message = `🥇 You're #1 on the ${category} leaderboard!`;
    } else if (rank <= 10) {
      message = `⭐ You're in the top 10 on the ${category} leaderboard!`;
    } else if (rank <= 50) {
      message = `✨ You're in the top 50 on the ${category} leaderboard!`;
    }

    await this.sendNotification(
      userId,
      'leaderboard',
      '📊 Leaderboard Update',
      message,
      {
        priority: 'normal',
        icon: '📊',
        data: { rank, category },
      }
    );
  }

  /**
   * Send event notification
   */
  async notifyEventUpdate(userId: number, eventName: string, message: string): Promise<void> {
    await this.sendNotification(
      userId,
      'event',
      `🎉 ${eventName}`,
      message,
      {
        priority: 'normal',
        icon: '🎉',
        data: { eventName },
      }
    );
  }

  /**
   * Send friend notification
   */
  async notifyFriendRequest(userId: number, friendName: string): Promise<void> {
    await this.sendNotification(
      userId,
      'friend',
      '👥 Friend Request',
      `${friendName} sent you a friend request!`,
      {
        priority: 'normal',
        icon: '👥',
        data: { friendName },
      }
    );
  }

  /**
   * Send promotion notification
   */
  async notifyPromotion(userId: number, title: string, message: string, actionUrl?: string): Promise<void> {
    await this.sendNotification(
      userId,
      'promotion',
      `🎁 ${title}`,
      message,
      {
        priority: 'normal',
        icon: '🎁',
        actionUrl,
      }
    );
  }

  /**
   * Send system notification
   */
  async notifySystem(userId: number, title: string, message: string): Promise<void> {
    await this.sendNotification(
      userId,
      'system',
      title,
      message,
      {
        priority: 'critical',
        icon: '⚠️',
      }
    );
  }

  /**
   * Get user notifications
   */
  async getUserNotifications(userId: number, limit: number = 50): Promise<PushNotification[]> {
    const notificationIds = this.userNotifications.get(userId) || [];
    const notifications: PushNotification[] = [];

    for (const id of notificationIds) {
      const notif = this.notifications.get(id);
      if (notif && notif.expiresAt > new Date()) {
        notifications.push(notif);
      }
    }

    // Sort by created date descending
    notifications.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());

    return notifications.slice(0, limit);
  }

  /**
   * Mark notification as read
   */
  async markAsRead(notificationId: string): Promise<void> {
    const notif = this.notifications.get(notificationId);
    if (notif) {
      notif.read = true;
      notif.readAt = new Date();
    }
  }

  /**
   * Mark all notifications as read
   */
  async markAllAsRead(userId: number): Promise<void> {
    const notificationIds = this.userNotifications.get(userId) || [];

    for (const id of notificationIds) {
      const notif = this.notifications.get(id);
      if (notif && !notif.read) {
        notif.read = true;
        notif.readAt = new Date();
      }
    }
  }

  /**
   * Delete notification
   */
  async deleteNotification(notificationId: string): Promise<void> {
    this.notifications.delete(notificationId);
  }

  /**
   * Get unread count
   */
  async getUnreadCount(userId: number): Promise<number> {
    const notificationIds = this.userNotifications.get(userId) || [];
    let count = 0;

    for (const id of notificationIds) {
      const notif = this.notifications.get(id);
      if (notif && !notif.read && notif.expiresAt > new Date()) {
        count++;
      }
    }

    return count;
  }

  /**
   * Get notification stats
   */
  async getStats(): Promise<{
    totalNotifications: number;
    totalUsers: number;
    queuedNotifications: number;
    averageUnreadPerUser: number;
  }> {
    let totalUnread = 0;
    for (const notificationIds of this.userNotifications.values()) {
      for (const id of notificationIds) {
        const notif = this.notifications.get(id);
        if (notif && !notif.read) {
          totalUnread++;
        }
      }
    }

    return {
      totalNotifications: this.notifications.size,
      totalUsers: this.userNotifications.size,
      queuedNotifications: this.notificationQueue.length,
      averageUnreadPerUser: this.userNotifications.size > 0 ? totalUnread / this.userNotifications.size : 0,
    };
  }

  /**
   * Process notification queue
   */
  async processQueue(): Promise<void> {
    while (this.notificationQueue.length > 0) {
      const notificationId = this.notificationQueue.shift();
      if (!notificationId) continue;

      const notification = this.notifications.get(notificationId);
      if (notification) {
        notification.sent = true;
        notification.sentAt = new Date();

        // In production, send to push service (Firebase, OneSignal, etc.)
        // For now, just mark as sent
      }
    }
  }
}

export const pushNotificationsManager = new PushNotificationsManager();
