const Conversation = require('../models/conversation.model');
const Message = require('../models/message.model');
const Account = require('../models/account.model');
const mongoose = require('mongoose');

class ConversationController {
  constructor() {}

  static async getConversations(req, res) {
    try {
      // Get all conversations with latest message
      const allConversations = await Conversation.aggregate([
        {
          $lookup: {
            from: 'accounts',
            localField: 'user',
            foreignField: '_id',
            as: 'userInfo'
          }
        },
        {
          $lookup: {
            from: 'accounts',
            localField: 'admin',
            foreignField: '_id',
            as: 'adminInfo'
          }
        },
        {
          $lookup: {
            from: 'messages',
            let: { conversationId: '$_id' },
            pipeline: [
              {
                $match: {
                  $expr: { $eq: ['$conversation', '$$conversationId'] }
                }
              },
              { $sort: { createdAt: -1 } },
              { $limit: 1 },
              {
                $lookup: {
                  from: 'accounts',
                  localField: 'sender',
                  foreignField: '_id',
                  as: 'senderInfo'
                }
              },
              {
                $project: {
                  content: 1,
                  createdAt: 1,
                  isRead: 1,
                  type: 1,
                  sender: {
                    $arrayElemAt: ['$senderInfo', 0]
                  }
                }
              }
            ],
            as: 'latestMessage'
          }
        },
        {
          $addFields: {
            user: { $arrayElemAt: ['$userInfo', 0] },
            admin: { $arrayElemAt: ['$adminInfo', 0] },
            latestMessage: { $arrayElemAt: ['$latestMessage', 0] },
            unreadCount: {
              $size: {
                $filter: {
                  input: '$messages',
                  as: 'msg',
                  cond: { $eq: ['$$msg.isRead', false] }
                }
              }
            }
          }
        },
        {
          $project: {
            userInfo: 0,
            adminInfo: 0
          }
        },
        { $sort: { updatedAt: -1 } }
      ]);

      if (!allConversations || allConversations.length === 0) {
        return res.status(200).json({ message: 'No conversations found', data: [] });
      }

      res.status(200).json({
        data: allConversations,
        message: 'Conversations fetched successfully',
      });
    } catch (error) {
      console.error('Error fetching conversations:', error);
      res.status(500).json({ error: 'Failed to fetch conversations' });
    }
  }

  static async getConversationById(req, res) {
    const { conversationId } = req.params;
    
    try {
      if (!mongoose.Types.ObjectId.isValid(conversationId)) {
        return res.status(400).json({ message: 'Invalid conversation ID format' });
      }

      const [conversation, messages] = await Promise.all([
        Conversation.findById(conversationId)
          .populate('user', 'id firstName lastName email avatar')
          .populate('admin', 'id firstName lastName email avatar')
          .lean(),
        Message.find({ conversation: conversationId })
          .populate('sender', 'id firstName lastName email avatar role')
          .sort({ createdAt: 1 })
          .lean()
      ]);

      if (!conversation) {
        return res.status(404).json({ message: 'Conversation not found' });
      }

      res.status(200).json({
        message: 'Conversation fetched successfully',
        conversation,
        messages: messages || [],
      });
    } catch (error) {
      console.error('Error fetching conversation:', error);
      res.status(500).json({ error: 'Failed to fetch conversation' });
    }
  }

  static async getMyConversation(req, res) {
    try {
      const userId = req.user.id;

      if (!mongoose.Types.ObjectId.isValid(userId)) {
        return res.status(400).json({ message: 'Invalid user ID format' });
      }

      const conversation = await Conversation.findOne({ user: userId })
        .populate('user', 'id firstName lastName email avatar')
        .populate('admin', 'id firstName lastName email avatar')
        .lean();

      if (!conversation) {
        return res.status(404).json({ message: 'No conversation found' });
      }

      // Get messages for this conversation
      const messages = await Message.find({ conversation: conversation._id })
        .populate('sender', 'id firstName lastName email avatar role')
        .sort({ createdAt: 1 })
        .lean();

      // Mark unread messages as read if they're from admin to user
      if (messages && messages.length > 0) {
        const unreadMessages = messages.filter(
          msg => !msg.isRead && msg.sender && msg.sender.role === 'admin'
        );
        
        if (unreadMessages.length > 0) {
          await Message.updateMany(
            {
              _id: { $in: unreadMessages.map(msg => msg._id) }
            },
            { 
              isRead: true,
              readAt: new Date()
            }
          );
        }
      }

      return res.status(200).json({
        message: 'Conversation fetched successfully',
        conversation,
        messages: messages || [],
      });
    } catch (error) {
      console.error(`Error fetching my conversations:`, error);
      return res
        .status(500)
        .json({ error: 'Failed to fetch my conversations' });
    }
  }

  /**
   * Create a new conversation (for users starting a chat)
   */
  static async createConversation(req, res) {
    try {
      const { subject } = req.body;
      const userId = req.user.id;

      if (!subject) {
        return res.status(400).json({ error: 'Subject is required' });
      }

      // Check if user already has an active conversation
      const existingConversation = await Conversation.findOne({ 
        user: userId,
        status: { $ne: 'closed' }
      });

      if (existingConversation) {
        return res.status(400).json({ 
          error: 'You already have an active conversation',
          conversationId: existingConversation._id
        });
      }

      // Generate a unique room ID
      const roomId = `room_${Date.now()}_${userId}`;

      const conversation = await Conversation.create({
        user: userId,
        subject,
        roomId,
        status: 'pending',
        lastMessageAt: new Date()
      });

      return res.status(201).json({
        message: 'Conversation created successfully',
        conversation
      });
    } catch (error) {
      console.error('Error creating conversation:', error);
      return res.status(500).json({ error: 'Failed to create conversation' });
    }
  }

  /**
   * Send a message in a conversation
   */
  static async sendMessage(req, res) {
    try {
      const { conversationId } = req.params;
      const { content, type = 'text', attachments } = req.body;
      const senderId = req.user.id;

      if (!mongoose.Types.ObjectId.isValid(conversationId)) {
        return res.status(400).json({ error: 'Invalid conversation ID format' });
      }

      if (!content && (!attachments || attachments.length === 0)) {
        return res.status(400).json({ error: 'Message content or attachments are required' });
      }

      const conversation = await Conversation.findById(conversationId);
      if (!conversation) {
        return res.status(404).json({ error: 'Conversation not found' });
      }

      // Create the message
      const message = await Message.create({
        conversation: conversationId,
        sender: senderId,
        content: content || '',
        type,
        attachments: attachments || [],
        isRead: false
      });

      // Update conversation's lastMessageAt
      conversation.lastMessageAt = new Date();
      await conversation.save();

      // Populate sender info for response
      await message.populate('sender', 'id firstName lastName email avatar role');

      return res.status(201).json({
        message: 'Message sent successfully',
        data: message
      });
    } catch (error) {
      console.error('Error sending message:', error);
      return res.status(500).json({ error: 'Failed to send message' });
    }
  }

  /**
   * Assign admin to conversation
   */
  static async assignAdmin(req, res) {
    try {
      const { conversationId } = req.params;
      const adminId = req.user.id; // Current admin assigning themselves

      if (!mongoose.Types.ObjectId.isValid(conversationId)) {
        return res.status(400).json({ error: 'Invalid conversation ID format' });
      }

      const conversation = await Conversation.findById(conversationId);
      if (!conversation) {
        return res.status(404).json({ error: 'Conversation not found' });
      }

      if (conversation.admin) {
        return res.status(400).json({ error: 'Conversation already has an assigned admin' });
      }

      conversation.admin = adminId;
      conversation.status = 'active';
      await conversation.save();

      // Send system message about admin assignment
      await Message.create({
        conversation: conversationId,
        sender: adminId,
        content: `Admin ${req.user.firstName} has joined the conversation`,
        type: 'text',
        isRead: false
      });

      return res.status(200).json({
        message: 'Admin assigned successfully',
        conversation
      });
    } catch (error) {
      console.error('Error assigning admin:', error);
      return res.status(500).json({ error: 'Failed to assign admin' });
    }
  }

  /**
   * Mark messages as read
   */
  static async markAsRead(req, res) {
    try {
      const { conversationId } = req.params;
      const userId = req.user.id;

      if (!mongoose.Types.ObjectId.isValid(conversationId)) {
        return res.status(400).json({ error: 'Invalid conversation ID format' });
      }

      // Mark all unread messages in this conversation as read
      const result = await Message.updateMany(
        {
          conversation: conversationId,
          sender: { $ne: userId }, // Don't mark own messages
          isRead: false
        },
        {
          isRead: true,
          readAt: new Date()
        }
      );

      return res.status(200).json({
        message: 'Messages marked as read',
        updatedCount: result.modifiedCount
      });
    } catch (error) {
      console.error('Error marking messages as read:', error);
      return res.status(500).json({ error: 'Failed to mark messages as read' });
    }
  }

  /**
   * Close a conversation
   */
  static async closeConversation(req, res) {
    try {
      const { conversationId } = req.params;

      if (!mongoose.Types.ObjectId.isValid(conversationId)) {
        return res.status(400).json({ error: 'Invalid conversation ID format' });
      }

      const conversation = await Conversation.findById(conversationId);
      if (!conversation) {
        return res.status(404).json({ error: 'Conversation not found' });
      }

      conversation.status = 'closed';
      await conversation.save();

      // Send system message about conversation closing
      await Message.create({
        conversation: conversationId,
        sender: conversation.admin || conversation.user,
        content: 'This conversation has been closed',
        type: 'text',
        isRead: false
      });

      return res.status(200).json({
        message: 'Conversation closed successfully',
        conversation
      });
    } catch (error) {
      console.error('Error closing conversation:', error);
      return res.status(500).json({ error: 'Failed to close conversation' });
    }
  }

  /**
   * Get unread message count for admin
   */
  static async getUnreadCount(req, res) {
    try {
      const userId = req.user.id;

      // For admin: get count of unread messages from users
      // For user: get count of unread messages from admin
      let matchCondition = {};
      
      if (req.user.role === 'admin') {
        // Admin sees messages from users that are unread
        matchCondition = {
          isRead: false,
          'senderInfo.role': 'user'
        };
      } else {
        // User sees messages from admin that are unread
        matchCondition = {
          isRead: false,
          'senderInfo.role': 'admin'
        };
      }

      const unreadCount = await Message.aggregate([
        {
          $lookup: {
            from: 'accounts',
            localField: 'sender',
            foreignField: '_id',
            as: 'senderInfo'
          }
        },
        {
          $match: {
            ...matchCondition,
            'senderInfo.0': { $exists: true }
          }
        },
        {
          $count: 'count'
        }
      ]);

      return res.status(200).json({
        unreadCount: unreadCount[0]?.count || 0
      });
    } catch (error) {
      console.error('Error getting unread count:', error);
      return res.status(500).json({ error: 'Failed to get unread count' });
    }
  }

  /**
   * Get all conversations for admin dashboard
   */
  static async getAdminConversations(req, res) {
    try {
      const page = parseInt(req.query.page) || 1;
      const limit = parseInt(req.query.limit) || 20;
      const skip = (page - 1) * limit;
      const status = req.query.status; // Optional filter by status

      const match = {};
      if (status) {
        match.status = status;
      }

      const [conversations, totalCount] = await Promise.all([
        Conversation.aggregate([
          { $match: match },
          {
            $lookup: {
              from: 'accounts',
              localField: 'user',
              foreignField: '_id',
              as: 'userInfo'
            }
          },
          {
            $lookup: {
              from: 'accounts',
              localField: 'admin',
              foreignField: '_id',
              as: 'adminInfo'
            }
          },
          {
            $lookup: {
              from: 'messages',
              let: { conversationId: '$_id' },
              pipeline: [
                {
                  $match: {
                    $expr: { $eq: ['$conversation', '$$conversationId'] }
                  }
                },
                { $sort: { createdAt: -1 } },
                { $limit: 1 },
                {
                  $lookup: {
                    from: 'accounts',
                    localField: 'sender',
                    foreignField: '_id',
                    as: 'senderInfo'
                  }
                }
              ],
              as: 'lastMessage'
            }
          },
          {
            $lookup: {
              from: 'messages',
              let: { conversationId: '$_id' },
              pipeline: [
                {
                  $match: {
                    $expr: {
                      $and: [
                        { $eq: ['$conversation', '$$conversationId'] },
                        { $eq: ['$isRead', false] }
                      ]
                    }
                  }
                },
                { $count: 'unreadCount' }
              ],
              as: 'unreadStats'
            }
          },
          {
            $addFields: {
              user: { $arrayElemAt: ['$userInfo', 0] },
              admin: { $arrayElemAt: ['$adminInfo', 0] },
              lastMessage: { $arrayElemAt: ['$lastMessage', 0] },
              unreadCount: {
                $ifNull: [{ $arrayElemAt: ['$unreadStats.unreadCount', 0] }, 0]
              }
            }
          },
          {
            $project: {
              userInfo: 0,
              adminInfo: 0,
              unreadStats: 0
            }
          },
          { $sort: { updatedAt: -1 } },
          { $skip: skip },
          { $limit: limit }
        ]),
        Conversation.countDocuments(match)
      ]);

      const totalPages = Math.ceil(totalCount / limit);

      return res.status(200).json({
        success: true,
        data: {
          conversations,
          pagination: {
            totalItems: totalCount,
            totalPages,
            currentPage: page,
            itemsPerPage: limit,
            hasNextPage: page < totalPages,
            hasPreviousPage: page > 1,
          },
        },
      });
    } catch (error) {
      console.error('Error fetching admin conversations:', error);
      return res.status(500).json({ error: 'Failed to fetch conversations' });
    }
  }
}

module.exports = ConversationController;