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

export interface ChatMessage {
  id: string;
  senderId: number;
  senderUsername: string;
  recipientId: number;
  content: string;
  timestamp: Date;
  read: boolean;
  edited?: boolean;
  editedAt?: Date;
}

export interface ChatConversation {
  id: string;
  participantIds: [number, number];
  lastMessage?: ChatMessage;
  lastMessageAt?: Date;
  unreadCount: number;
}

export interface ChatUser {
  id: number;
  username: string;
  status: 'online' | 'offline' | 'away';
  lastSeenAt: Date;
}

/**
 * Chat Manager - Handles real-time messaging
 */
export class ChatManager {
  private conversations: Map<string, ChatConversation> = new Map();
  private messages: Map<string, ChatMessage[]> = new Map();
  private userStatus: Map<number, ChatUser> = new Map();
  private activeConnections: Map<number, Set<string>> = new Map(); // userId -> set of connection IDs

  /**
   * Generate conversation ID
   */
  private getConversationId(userId1: number, userId2: number): string {
    const sorted = [userId1, userId2].sort();
    return `conv_${sorted[0]}_${sorted[1]}`;
  }

  /**
   * Initialize user status
   */
  async initializeUser(userId: number, username: string): Promise<void> {
    this.userStatus.set(userId, {
      id: userId,
      username,
      status: 'offline',
      lastSeenAt: new Date(),
    });
  }

  /**
   * User comes online
   */
  async userOnline(userId: number, connectionId: string): Promise<void> {
    const user = this.userStatus.get(userId);
    if (user) {
      user.status = 'online';
      user.lastSeenAt = new Date();
    }

    if (!this.activeConnections.has(userId)) {
      this.activeConnections.set(userId, new Set());
    }
    this.activeConnections.get(userId)!.add(connectionId);
  }

  /**
   * User goes offline
   */
  async userOffline(userId: number, connectionId: string): Promise<void> {
    const connections = this.activeConnections.get(userId);
    if (connections) {
      connections.delete(connectionId);
      if (connections.size === 0) {
        const user = this.userStatus.get(userId);
        if (user) {
          user.status = 'offline';
          user.lastSeenAt = new Date();
        }
      }
    }
  }

  /**
   * Send message
   */
  async sendMessage(
    senderId: number,
    senderUsername: string,
    recipientId: number,
    content: string
  ): Promise<ChatMessage> {
    const message: ChatMessage = {
      id: `msg_${Date.now()}_${Math.random().toString(36).substring(7)}`,
      senderId,
      senderUsername,
      recipientId,
      content,
      timestamp: new Date(),
      read: false,
    };

    const conversationId = this.getConversationId(senderId, recipientId);

    // Add message to conversation
    if (!this.messages.has(conversationId)) {
      this.messages.set(conversationId, []);
    }
    this.messages.get(conversationId)!.push(message);

    // Update or create conversation
    let conversation = this.conversations.get(conversationId);
    if (!conversation) {
      conversation = {
        id: conversationId,
        participantIds: [senderId, recipientId],
        unreadCount: 0,
      };
      this.conversations.set(conversationId, conversation);
    }

    conversation.lastMessage = message;
    conversation.lastMessageAt = new Date();

    // Increment unread count
    if (!this.isUserOnline(recipientId)) {
      conversation.unreadCount++;
    }

    // Log message
    await writeAuditLog({
      actorId: senderId,
      actorRole: 'user',
      action: 'message_sent',
      category: 'chat',
      details: {
        recipientId,
        messageLength: content.length,
      },
    });

    return message;
  }

  /**
   * Get conversation messages
   */
  async getMessages(userId1: number, userId2: number, limit: number = 50): Promise<ChatMessage[]> {
    const conversationId = this.getConversationId(userId1, userId2);
    const messages = this.messages.get(conversationId) || [];
    return messages.slice(-limit);
  }

  /**
   * Mark message as read
   */
  async markAsRead(messageId: string, conversationId: string): Promise<void> {
    const messages = this.messages.get(conversationId);
    if (messages) {
      const message = messages.find((m) => m.id === messageId);
      if (message) {
        message.read = true;
      }
    }
  }

  /**
   * Mark all messages as read in conversation
   */
  async markConversationAsRead(userId: number, conversationId: string): Promise<void> {
    const messages = this.messages.get(conversationId);
    if (messages) {
      for (const message of messages) {
        if (message.recipientId === userId && !message.read) {
          message.read = true;
        }
      }
    }

    const conversation = this.conversations.get(conversationId);
    if (conversation) {
      conversation.unreadCount = 0;
    }
  }

  /**
   * Edit message
   */
  async editMessage(messageId: string, conversationId: string, newContent: string): Promise<ChatMessage | null> {
    const messages = this.messages.get(conversationId);
    if (messages) {
      const message = messages.find((m) => m.id === messageId);
      if (message) {
        message.content = newContent;
        message.edited = true;
        message.editedAt = new Date();
        return message;
      }
    }
    return null;
  }

  /**
   * Delete message
   */
  async deleteMessage(messageId: string, conversationId: string): Promise<boolean> {
    const messages = this.messages.get(conversationId);
    if (messages) {
      const index = messages.findIndex((m) => m.id === messageId);
      if (index > -1) {
        messages.splice(index, 1);
        return true;
      }
    }
    return false;
  }

  /**
   * Get user conversations
   */
  async getUserConversations(userId: number): Promise<ChatConversation[]> {
    const userConversations: ChatConversation[] = [];

    for (const conversation of this.conversations.values()) {
      if (conversation.participantIds.includes(userId)) {
        userConversations.push(conversation);
      }
    }

    // Sort by last message time
    return userConversations.sort((a, b) => {
      const timeA = a.lastMessageAt?.getTime() || 0;
      const timeB = b.lastMessageAt?.getTime() || 0;
      return timeB - timeA;
    });
  }

  /**
   * Get unread count for user
   */
  async getUnreadCount(userId: number): Promise<number> {
    let unreadCount = 0;

    for (const conversation of this.conversations.values()) {
      if (conversation.participantIds.includes(userId)) {
        const messages = this.messages.get(conversation.id) || [];
        for (const message of messages) {
          if (message.recipientId === userId && !message.read) {
            unreadCount++;
          }
        }
      }
    }

    return unreadCount;
  }

  /**
   * Get user status
   */
  async getUserStatus(userId: number): Promise<ChatUser | undefined> {
    return this.userStatus.get(userId);
  }

  /**
   * Get multiple users' status
   */
  async getUsersStatus(userIds: number[]): Promise<ChatUser[]> {
    const statuses: ChatUser[] = [];

    for (const userId of userIds) {
      const status = this.userStatus.get(userId);
      if (status) {
        statuses.push(status);
      }
    }

    return statuses;
  }

  /**
   * Check if user is online
   */
  private isUserOnline(userId: number): boolean {
    const connections = this.activeConnections.get(userId);
    return connections ? connections.size > 0 : false;
  }

  /**
   * Search conversations
   */
  async searchConversations(userId: number, query: string): Promise<ChatConversation[]> {
    const userConversations = await this.getUserConversations(userId);

    return userConversations.filter((conv) => {
      const otherUserId = conv.participantIds.find((id) => id !== userId);
      const otherUser = this.userStatus.get(otherUserId!);
      if (!otherUser) return false;

      return (
        otherUser.username.toLowerCase().includes(query.toLowerCase()) ||
        conv.lastMessage?.content.toLowerCase().includes(query.toLowerCase())
      );
    });
  }

  /**
   * Get chat statistics
   */
  async getChatStats(): Promise<{
    totalConversations: number;
    totalMessages: number;
    activeUsers: number;
    offlineUsers: number;
  }> {
    const activeUsers = Array.from(this.userStatus.values()).filter((u) => u.status === 'online').length;
    const offlineUsers = Array.from(this.userStatus.values()).filter((u) => u.status === 'offline').length;
    const totalMessages = Array.from(this.messages.values()).reduce((sum, msgs) => sum + msgs.length, 0);

    return {
      totalConversations: this.conversations.size,
      totalMessages,
      activeUsers,
      offlineUsers,
    };
  }

  /**
   * Block user
   */
  async blockUser(userId: number, blockedUserId: number): Promise<void> {
    // Implementation would involve storing blocked users
    await writeAuditLog({
      actorId: userId,
      actorRole: 'user',
      action: 'user_blocked',
      category: 'chat',
      details: { blockedUserId },
    });
  }

  /**
   * Unblock user
   */
  async unblockUser(userId: number, blockedUserId: number): Promise<void> {
    await writeAuditLog({
      actorId: userId,
      actorRole: 'user',
      action: 'user_unblocked',
      category: 'chat',
      details: { blockedUserId },
    });
  }
}

export const chatManager = new ChatManager();
