import { Injectable } from '@angular/core';
import { AuthService } from './auth.service';
import { LoggerService } from './logger.service';
import { BehaviorSubject, Observable } from 'rxjs';
import { ChatService, Message, Conversation } from './chat.service';
import { UserService } from './user.service';

export interface Notification {
  id: string;
  type: 'message' | 'system' | 'missed-call';
  title: string;
  message: string;
  timestamp: Date;
  read: boolean;
  actionUrl?: string;
}

@Injectable({
  providedIn: 'root'
})
export class NotificationService {
  private notifications: Notification[] = [];
  private notificationsSubject = new BehaviorSubject<Notification[]>([]);
  public notifications$ = this.notificationsSubject.asObservable();
  private currentUserId: string | null = null;

  private previousConversations: Conversation[] = [];
  private processedMessageIds = new Set<string>();
  private isInitialized = false;
  private activeConversationUserId: string | null = null; // Track which conversation is currently open

  constructor(
    private chatService: ChatService,
    private userService: UserService,
    private authService: AuthService,
    private logger: LoggerService
  ) {
    // Only initialize when authenticated
    if (!this.authService.isAuthenticated()) {
      this.logger.debug('NotificationService: Skipping initialization - user not authenticated');
      return;
    }

    // Initialize current user ID
    this.initCurrentUserId();
    
    // Subscribe to new WebSocket messages - only show notifications for incoming messages
    this.chatService.messages$.subscribe(async (message: Message) => {
      await this.handleNewMessage(message);
    });
    
    // Subscribe to conversation updates to catch unread messages
    this.chatService.conversations$.subscribe(async (conversations: Conversation[]) => {
      await this.handleConversationUpdates(conversations);
    });
    
    // Mark as initialized after a short delay to process existing unread messages first
    setTimeout(() => {
      this.isInitialized = true;
      this.logger.debug('NotificationService: Initialized, will now process new messages');
      // Check for unread messages periodically (every 10 seconds)
      this.startPeriodicCheck();
      
      // Force check conversations again after initialization to catch any missed messages
      setTimeout(async () => {
        try {
          if (this.authService.isAuthenticated()) {
            await this.chatService.getConversations();
          }
        } catch (error) {
          this.logger.error('NotificationService: Error in post-init check:', error);
        }
      }, 1000);
    }, 1500);
  }

  private startPeriodicCheck(): void {
    // Check for unread messages every 10 seconds
    setInterval(async () => {
      try {
        if (!this.authService.isAuthenticated()) {
          return;
        }
        await this.chatService.getConversations();
      } catch (error) {
        this.logger.error('NotificationService: Error in periodic check:', error);
      }
    }, 10000);
  }

  private async handleNewMessage(message: Message): Promise<void> {
    if (!this.authService.isAuthenticated()) return;
    this.logger.debug('NotificationService: Received WebSocket message', message);
    
    // Skip call event messages (these are system messages, not user messages)
    if (this.isCallEventMessage(message.content)) {
      this.logger.debug('NotificationService: Skipping call event message');
      return;
    }
    
    // Get current user ID to filter out messages we sent ourselves
    if (!this.currentUserId) {
      await this.initCurrentUserId();
      this.logger.debug('NotificationService: Current user ID initialized:', this.currentUserId);
    }
    
    // Normalize IDs for comparison (convert to strings and trim)
    const currentId = this.currentUserId?.toString().trim() || null;
    const receiverId = message.receiverId?.toString().trim() || '';
    const senderId = message.senderId?.toString().trim() || '';
    const messageId = message.id?.toString() || '';
    const activeConversationId = this.activeConversationUserId?.toString().trim() || null;
    
    // Skip if we've already processed this message
    if (messageId && this.processedMessageIds.has(messageId)) {
      this.logger.debug('NotificationService: Message already processed, skipping');
      return;
    }
    
    // Check if chat is open for this conversation - don't show notification if user is viewing this chat
    if (activeConversationId && senderId === activeConversationId) {
      this.logger.debug('NotificationService: Chat is open for this conversation, skipping notification');
      // Still mark as processed to avoid duplicate notifications
      if (messageId) {
        this.processedMessageIds.add(messageId);
      }
      return;
    }
    
    // Debug logging
    this.logger.debug('NotificationService: Checking WebSocket message', {
      currentUserId: currentId,
      receiverId: receiverId,
      senderId: senderId,
      messageId: messageId,
      activeConversationId: activeConversationId,
      isReceived: currentId && receiverId === currentId && senderId !== currentId,
      messageContent: message.content?.substring(0, 50)
    });
    
    // Only show notification if message is received (not sent by current user)
    if (currentId && receiverId === currentId && senderId !== currentId) {
      this.logger.debug('NotificationService: ✅ Showing notification for WebSocket message from', message.senderName);
      if (messageId) {
        this.processedMessageIds.add(messageId);
      }
      this.showNotification({
        type: 'message',
        title: `New message from ${message.senderName}`,
        message: message.content.length > 100 ? message.content.substring(0, 100) + '...' : message.content,
        timestamp: message.createdAt ? new Date(message.createdAt) : new Date(),
        read: false,
        actionUrl: `/messages?userId=${message.senderId}`
      });
    } else {
      this.logger.debug('NotificationService: ❌ Skipping WebSocket notification -', 
        !currentId ? 'no current user ID' :
        receiverId !== currentId ? `receiver ID (${receiverId}) doesn't match current (${currentId})` :
        senderId === currentId ? 'message sent by current user' :
        'unknown reason');
    }
  }

  private isCallEventMessage(content: string): boolean {
    if (!content) return false;
    // Check if message starts with call emoji or contains call-related keywords
    const callEventPatterns = [
      /^📞/,
      /started a (video|audio) call/i,
      /call ended/i,
      /missed the call/i,
      /rejected the call/i,
      /call started/i
    ];
    return callEventPatterns.some(pattern => pattern.test(content));
  }

  private async handleConversationUpdates(conversations: Conversation[]): Promise<void> {
    if (!this.authService.isAuthenticated()) return;
    if (!this.currentUserId) {
      await this.initCurrentUserId();
    }

    // On initial load, create notifications for unread messages
    if (!this.isInitialized || this.previousConversations.length === 0) {
      this.logger.debug('NotificationService: Initial conversation load, checking for unread messages', {
        conversationsCount: conversations.length,
        currentUserId: this.currentUserId
      });
      
      // Create notifications for existing unread messages on initial load
      for (const conversation of conversations) {
        this.logger.debug('NotificationService: Checking conversation', {
          otherUserId: conversation.otherUserId,
          otherUserName: conversation.otherUserName,
          unreadCount: conversation.unreadCount,
          hasLastMessage: !!conversation.lastMessage
        });
        
        if (conversation.unreadCount > 0 && conversation.lastMessage) {
          const message = conversation.lastMessage;
          const messageId = message.id?.toString() || '';
          
          // Skip call event messages
          if (this.isCallEventMessage(message.content || '')) {
            this.logger.debug('NotificationService: Skipping call event message in conversation');
            continue;
          }
          
          // Since conversations are fetched with user's auth token, any unread message is for this user
          // Verify it's not from ourselves by checking sender
          const currentId = this.currentUserId?.toString().trim() || null;
          const senderId = message.senderId?.toString().trim() || '';
          
          // Check if message is from ourselves (should not notify)
          const isFromSelf = currentId && senderId && 
                            currentId.toLowerCase() === senderId.toLowerCase();
          
          // Check if chat is open for this conversation - don't show notification if user is viewing this chat
          const activeConversationId = this.activeConversationUserId?.toString().trim() || null;
          const isActiveConversation = activeConversationId && senderId === activeConversationId;
          
          this.logger.debug('NotificationService: Checking unread message', {
            currentId,
            senderId,
            otherUserName: conversation.otherUserName,
            unreadCount: conversation.unreadCount,
            isFromSelf,
            activeConversationId,
            isActiveConversation,
            willNotify: !isFromSelf && !isActiveConversation
          });
          
          // Show notification if message is not from ourselves and chat is not open for this conversation
          if (!isFromSelf && !isActiveConversation) {
            // Check if notification already exists for this message/conversation
            const existingNotification = this.notifications.find(n => 
              n.actionUrl?.includes(conversation.otherUserId)
            );
            
            // Only create if we don't have a recent notification for this conversation
            // or if the unread count increased
            const shouldNotify = !existingNotification || 
                                (existingNotification && conversation.unreadCount > 1);
            
            if (shouldNotify) {
              this.logger.debug('NotificationService: ✅ Creating notification for unread message from', conversation.otherUserName);
              this.showNotification({
                type: 'message',
                title: `Unread message${conversation.unreadCount > 1 ? `s (${conversation.unreadCount})` : ''} from ${conversation.otherUserName}`,
                message: message.content ? (message.content.length > 100 ? message.content.substring(0, 100) + '...' : message.content) : 'New message',
                timestamp: message.createdAt ? new Date(message.createdAt) : new Date(),
                read: false,
                actionUrl: `/messages?userId=${conversation.otherUserId}`
              });
            } else {
              this.logger.debug('NotificationService: Notification already exists for this conversation');
            }
            
            // Mark as processed to avoid duplicate notifications
            if (messageId) {
              this.processedMessageIds.add(messageId);
            }
          } else {
            this.logger.debug('NotificationService: Skipping - message is from current user');
          }
        } else if (conversation.unreadCount > 0 && !conversation.lastMessage) {
          // Handle case where there are unread messages but no lastMessage in the DTO
          this.logger.debug('NotificationService: Found unread messages but no lastMessage, creating generic notification');
          const existingNotification = this.notifications.find(n => 
            n.actionUrl?.includes(conversation.otherUserId)
          );
          
          if (!existingNotification) {
            this.showNotification({
              type: 'message',
              title: `Unread message${conversation.unreadCount > 1 ? `s (${conversation.unreadCount})` : ''} from ${conversation.otherUserName}`,
              message: 'You have unread messages',
              timestamp: new Date(),
              read: false,
              actionUrl: `/messages?userId=${conversation.otherUserId}`
            });
          }
        }
      }
      
      this.previousConversations = [...conversations];
      return;
    }

    // Find conversations with new unread messages
    for (const conversation of conversations) {
      if (conversation.unreadCount > 0 && conversation.lastMessage) {
        const message = conversation.lastMessage;
        const messageId = message.id?.toString() || '';
        
        // Skip if already processed
        if (messageId && this.processedMessageIds.has(messageId)) {
          continue;
        }
        
        // Skip call event messages
        if (this.isCallEventMessage(message.content || '')) {
          this.logger.debug('NotificationService: Skipping call event message in conversation update');
          continue;
        }
        
        // Check if this conversation had unread messages before
        const previousConversation = this.previousConversations.find(
          c => c.otherUserId === conversation.otherUserId
        );
        
        // Only notify if unread count increased
        const previousUnreadCount = previousConversation?.unreadCount || 0;
        if (conversation.unreadCount > previousUnreadCount) {
          
          // Verify message is for current user
          const currentId = this.currentUserId?.toString().trim() || null;
          const receiverId = message.receiverId?.toString().trim() || '';
          const senderId = message.senderId?.toString().trim() || '';
          const activeConversationId = this.activeConversationUserId?.toString().trim() || null;
          
          // Check if chat is open for this conversation - don't show notification if user is viewing this chat
          if (activeConversationId && senderId === activeConversationId) {
            this.logger.debug('NotificationService: Chat is open for this conversation, skipping notification');
            // Still mark as processed to avoid duplicate notifications
            if (messageId) {
              this.processedMessageIds.add(messageId);
            }
            continue;
          }
          
          if (currentId && receiverId === currentId && senderId !== currentId) {
            this.logger.debug('NotificationService: ✅ Showing notification for new unread message from', conversation.otherUserName);
            if (messageId) {
              this.processedMessageIds.add(messageId);
            }
            this.showNotification({
              type: 'message',
              title: `New message from ${conversation.otherUserName}`,
              message: message.content.length > 100 ? message.content.substring(0, 100) + '...' : message.content,
              timestamp: message.createdAt ? new Date(message.createdAt) : new Date(),
              read: false,
              actionUrl: `/messages?userId=${conversation.otherUserId}`
            });
          }
        }
      }
    }
    
    // Update previous conversations for next comparison
    this.previousConversations = [...conversations];
  }

  private async initCurrentUserId(): Promise<void> {
    try {
      if (!this.authService.isAuthenticated()) {
        this.currentUserId = null;
        return;
      }
      const user = await this.userService.getCurrentUser();
      this.currentUserId = user?.id || null;
      this.logger.debug('NotificationService: Initialized user ID:', this.currentUserId);
      
      // Also try to get from chat service if available
      if (!this.currentUserId && this.chatService.getCurrentUserId) {
        this.currentUserId = this.chatService.getCurrentUserId();
        this.logger.debug('NotificationService: Got user ID from chat service:', this.currentUserId);
      }
    } catch (error) {
      this.logger.error('NotificationService: Error initializing user ID:', error);
      this.currentUserId = null;
    }
  }
  
  // Public method to refresh user ID (call when user logs in or changes)
  async refreshUserId(): Promise<void> {
    if (!this.authService.isAuthenticated()) {
      this.currentUserId = null;
      return;
    }
    await this.initCurrentUserId();
  }

  showNotification(notification: Omit<Notification, 'id'>): void {
    const newNotification: Notification = {
      ...notification,
      id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
    };
    this.notifications.unshift(newNotification);
    
    // Keep only the last 50 notifications to prevent memory issues
    if (this.notifications.length > 50) {
      this.notifications = this.notifications.slice(0, 50);
    }
    
    this.notificationsSubject.next([...this.notifications]);
    this.logger.debug('NotificationService: Notification added. Total:', this.notifications.length, 'Unread:', this.getUnreadCount());

    // Request browser notification permission and show
    if ('Notification' in window && Notification.permission === 'granted') {
      new Notification(notification.title, {
        body: notification.message,
        icon: '/assets/icon.png',
      });
    } else if ('Notification' in window && Notification.permission !== 'denied') {
      Notification.requestPermission().then(permission => {
        if (permission === 'granted') {
          new Notification(notification.title, {
            body: notification.message,
            icon: '/assets/icon.png',
          });
        }
      });
    }
  }

  markAsRead(id: string): void {
    const notification = this.notifications.find(n => n.id === id);
    if (notification) {
      notification.read = true;
      this.notificationsSubject.next([...this.notifications]);
    }
  }

  markAllAsRead(): void {
    this.notifications.forEach(n => n.read = true);
    this.notificationsSubject.next([...this.notifications]);
  }

  deleteNotification(id: string): void {
    this.notifications = this.notifications.filter(n => n.id !== id);
    this.notificationsSubject.next([...this.notifications]);
    this.logger.debug('NotificationService: Notification deleted. Remaining:', this.notifications.length);
  }

  deleteAllNotifications(): void {
    this.notifications = [];
    this.notificationsSubject.next([]);
    this.logger.debug('NotificationService: All notifications deleted');
  }

  clearAll(): void {
    this.notifications = [];
    this.notificationsSubject.next([]);
  }

  getUnreadCount(): number {
    return this.notifications.filter(n => !n.read).length;
  }

  getNotifications(): Notification[] {
    return [...this.notifications];
  }

  // Public method to manually check for unread messages and create notifications
  async checkForUnreadMessages(): Promise<void> {
    try {
      this.logger.debug('NotificationService: Manually checking for unread messages');
      if (!this.authService.isAuthenticated()) return;
      await this.chatService.getConversations();
    } catch (error) {
      this.logger.error('NotificationService: Error checking for unread messages:', error);
    }
  }

  // Set the active conversation (called when user opens a chat)
  setActiveConversation(userId: string | null): void {
    this.activeConversationUserId = userId ? userId.toString().trim() : null;
    this.logger.debug('NotificationService: Active conversation set to:', this.activeConversationUserId);
    // Re-emit notifications so components can re-filter based on active conversation
    this.notificationsSubject.next([...this.notifications]);
  }

  // Clear the active conversation (called when user closes a chat)
  clearActiveConversation(): void {
    this.activeConversationUserId = null;
    this.logger.debug('NotificationService: Active conversation cleared');
    // Re-emit notifications so components can re-filter based on active conversation
    this.notificationsSubject.next([...this.notifications]);
  }

  // Get the active conversation user ID (for filtering notifications)
  getActiveConversationUserId(): string | null {
    return this.activeConversationUserId;
  }

  /**
   * Extract user ID from notification actionUrl
   * @param actionUrl - The actionUrl from notification (format: /messages?userId=xxx)
   * @returns The user ID or null if not found
   */
  extractUserIdFromActionUrl(actionUrl?: string): string | null {
    if (!actionUrl) return null;
    
    try {
      const urlParts = actionUrl.split('?');
      if (urlParts.length < 2) return null;
      
      const urlParams = new URLSearchParams(urlParts[1]);
      const userId = urlParams.get('userId');
      return userId ? userId.trim() : null;
    } catch (error) {
      this.logger.error('NotificationService: Error extracting userId from actionUrl:', error);
      return null;
    }
  }

  /**
   * Check if a notification is from the active conversation
   * @param notification - The notification to check
   * @returns true if notification is from active conversation user
   */
  isNotificationFromActiveConversation(notification: Notification): boolean {
    if (!this.activeConversationUserId || notification.type !== 'message') {
      return false;
    }
    
    const notificationUserId = this.extractUserIdFromActionUrl(notification.actionUrl);
    return notificationUserId !== null && notificationUserId === this.activeConversationUserId;
  }

  /**
   * Get filtered notifications excluding those from active conversation
   * Enterprise logic: When user is actively chatting with someone,
   * their message notifications are hidden to reduce redundancy
   * @returns Filtered notifications array
   */
  getFilteredNotifications(): Notification[] {
    return this.notifications.filter(n => {
      // Always show missed-call notifications (high priority)
      if (n.type === 'missed-call') {
        return true;
      }
      
      // For message notifications, exclude if from active conversation
      if (n.type === 'message') {
        return !this.isNotificationFromActiveConversation(n);
      }
      
      // Show other notification types
      return true;
    });
  }
}

