const path = require('path');
const fs = require('fs').promises;
const simpleParser = require('mailparser').simpleParser;
const { models } = require(path.resolve(process.env.ROOT_PATH, './db/db.js'));
const BaseController = require(path.resolve(process.env.ROOT_PATH, './controllers/BaseController.js'));

class MessageController extends BaseController {
  constructor() {
    super();
    
    // Protected endpoints
    this.protected('getMessages', 'getMessage', 'deleteMessage');
  }

  /**
   * Get all messages for a specific mailbox
   * 
   * @swagger
   * /mailboxes/{mailboxId}/messages:
   *   get:
   *     summary: Get all messages
   *     description: Retrieves all messages for a specific mailbox
   *     tags: [Messages]
   *     security:
   *       - bearerAuth: []
   *     parameters:
   *       - in: path
   *         name: mailboxId
   *         required: true
   *         schema:
   *           type: integer
   *         description: Mailbox ID
   *     responses:
   *       200:
   *         description: List of messages
   *         content:
   *           application/json:
   *             schema:
   *               type: array
   *               items:
   *                 type: object
   *                 properties:
   *                   id:
   *                     type: string
   *                     description: Message ID
   *                   subject:
   *                     type: string
   *                     description: Email subject
   *                   from:
   *                     type: string
   *                     description: Sender information
   *                   date:
   *                     type: string
   *                     format: date-time
   *                     description: Message date
   *                   size:
   *                     type: integer
   *                     description: Message size in bytes
   *                   hasAttachments:
   *                     type: boolean
   *                     description: Whether the message has attachments
   *       404:
   *         description: Mailbox not found
   *       500:
   *         description: Server error
   */
  async getMessages(req, res) {
    try {
      const { mailboxId } = req.params;
      
      // Verify mailbox exists
      const mailbox = await models.Mailbox.query().where('id', mailboxId).first();
      if (!mailbox) {
        return res.status(404).json({ error: 'Mailbox not found' });
      }
      
      // Get the physical path to the mailbox
      const mailboxPath = path.join('/var/mail', mailbox.domain, mailbox.local_part, 'new');
      
      try {
        // Check if directory exists
        await fs.access(mailboxPath);
        
        // Read all files in the directory
        const files = await fs.readdir(mailboxPath);
        
        // Process each file to extract basic email info
        const messages = await Promise.all(
          files.map(async (file) => {
            const filePath = path.join(mailboxPath, file);
            const stats = await fs.stat(filePath);
            
            try {
              // Read file content
              const content = await fs.readFile(filePath, { encoding: 'utf-8' });
              
              // Parse email content
              const parsed = await simpleParser(content);
              
              return {
                id: file,
                subject: parsed.subject || '(No Subject)',
                from: parsed.from?.text || '(Unknown Sender)',
                date: parsed.date || stats.mtime,
                size: stats.size,
                hasAttachments: parsed.attachments?.length > 0 || false
              };
            } catch (err) {
              console.error(`Error parsing email ${file}:`, err);
              return {
                id: file,
                subject: '(Error: Could not parse email)',
                from: '(Unknown)',
                date: stats.mtime,
                size: stats.size,
                hasAttachments: false,
                error: true
              };
            }
          })
        );
        
        // Sort messages by date (newest first)
        messages.sort((a, b) => new Date(b.date) - new Date(a.date));
        
        return res.json(messages);
      } catch (err) {
        // If directory doesn't exist or is empty
        if (err.code === 'ENOENT') {
          return res.json([]);
        }
        throw err;
      }
    } catch (error) {
      console.error('Error fetching messages:', error);
      return res.status(500).json({ error: 'Failed to fetch messages' });
    }
  }

  /**
   * Get a specific message from a mailbox
   * 
   * @swagger
   * /mailboxes/{mailboxId}/messages/{messageId}:
   *   get:
   *     summary: Get message details
   *     description: Retrieves a specific message from a mailbox
   *     tags: [Messages]
   *     security:
   *       - bearerAuth: []
   *     parameters:
   *       - in: path
   *         name: mailboxId
   *         required: true
   *         schema:
   *           type: integer
   *         description: Mailbox ID
   *       - in: path
   *         name: messageId
   *         required: true
   *         schema:
   *           type: string
   *         description: Message ID
   *     responses:
   *       200:
   *         description: Message details
   *         content:
   *           application/json:
   *             schema:
   *               type: object
   *               properties:
   *                 id:
   *                   type: string
   *                   description: Message ID
   *                 subject:
   *                   type: string
   *                   description: Email subject
   *                 from:
   *                   type: string
   *                   description: Sender information
   *                 to:
   *                   type: string
   *                   description: Recipient information
   *                 cc:
   *                   type: string
   *                   description: CC recipients
   *                 date:
   *                   type: string
   *                   format: date-time
   *                   description: Message date
   *                 textBody:
   *                   type: string
   *                   description: Plain text content
   *                 htmlBody:
   *                   type: string
   *                   description: HTML content
   *                 attachments:
   *                   type: array
   *                   description: List of attachments
   *                   items:
   *                     type: object
   *                     properties:
   *                       filename:
   *                         type: string
   *                       contentType:
   *                         type: string
   *                       size:
   *                         type: integer
   *                       contentId:
   *                         type: string
   *       404:
   *         description: Mailbox or message not found
   *       500:
   *         description: Server error
   */
  async getMessage(req, res) {
    try {
      const { mailboxId, messageId } = req.params;
      
      // Verify mailbox exists
      const mailbox = await models.Mailbox.query().where('id', mailboxId).first();
      if (!mailbox) {
        return res.status(404).json({ error: 'Mailbox not found' });
      }
      
      // Check both new and cur directories for the message
      const directories = ['new', 'cur'];
      let messageContent = null;
      let messageFound = false;
      
      for (const dir of directories) {
        const messagePath = path.join('/var/mail', mailbox.domain, mailbox.local_part, dir, messageId);
        
        try {
          // Check if file exists
          await fs.access(messagePath);
          
          // Read file content
          const content = await fs.readFile(messagePath, { encoding: 'utf-8' });
          
          // Parse email content
          messageContent = await simpleParser(content);
          messageFound = true;
          
          // Move message from 'new' to 'cur' if it's in 'new'
          if (dir === 'new') {
            const curDir = path.join('/var/mail', mailbox.domain, mailbox.local_part, 'cur');
            
            // Ensure cur directory exists
            await fs.mkdir(curDir, { recursive: true });
            
            // Move the file
            const curPath = path.join(curDir, `${messageId}:2,S`); // Mark as seen
            await fs.rename(messagePath, curPath);
          }
          
          break;
        } catch (err) {
          // Continue to next directory if file not found
          if (err.code === 'ENOENT') {
            continue;
          }
          throw err;
        }
      }
      
      if (!messageFound) {
        return res.status(404).json({ error: 'Message not found' });
      }
      
      // Format the response
      const response = {
        id: messageId,
        subject: messageContent.subject || '(No Subject)',
        from: messageContent.from?.text || '(Unknown Sender)',
        to: messageContent.to?.text || '',
        cc: messageContent.cc?.text || '',
        date: messageContent.date,
        textBody: messageContent.text,
        htmlBody: messageContent.html,
        attachments: messageContent.attachments?.map(attachment => ({
          filename: attachment.filename,
          contentType: attachment.contentType,
          size: attachment.size,
          contentId: attachment.contentId
        })) || []
      };
      
      return res.json(response);
    } catch (error) {
      console.error('Error fetching message:', error);
      return res.status(500).json({ error: 'Failed to fetch message' });
    }
  }

  /**
   * Delete a specific message from a mailbox
   * 
   * @swagger
   * /mailboxes/{mailboxId}/messages/{messageId}:
   *   delete:
   *     summary: Delete message
   *     description: Deletes a specific message from a mailbox
   *     tags: [Messages]
   *     security:
   *       - bearerAuth: []
   *     parameters:
   *       - in: path
   *         name: mailboxId
   *         required: true
   *         schema:
   *           type: integer
   *         description: Mailbox ID
   *       - in: path
   *         name: messageId
   *         required: true
   *         schema:
   *           type: string
   *         description: Message ID
   *     responses:
   *       200:
   *         description: Message deleted successfully
   *         content:
   *           application/json:
   *             schema:
   *               type: object
   *               properties:
   *                 message:
   *                   type: string
   *                   example: Message deleted successfully
   *       404:
   *         description: Mailbox or message not found
   *       500:
   *         description: Server error
   */
  async deleteMessage(req, res) {
    try {
      const { mailboxId, messageId } = req.params;
      
      // Verify mailbox exists
      const mailbox = await models.Mailbox.query().findById(mailboxId);
      if (!mailbox) {
        return res.status(404).json({ error: 'Mailbox not found' });
      }
      
      // Check both new and cur directories for the message
      const directories = ['new', 'cur'];
      let messageFound = false;
      
      for (const dir of directories) {
        // In 'cur' directory, the message might have flags appended
        const dirPath = path.join('/var/mail', mailbox.domain, mailbox.local_part, dir);
        
        try {
          // Check if directory exists
          await fs.access(dirPath);
          
          // Read all files in the directory
          const files = await fs.readdir(dirPath);
          
          // Find files that start with the messageId
          for (const file of files) {
            if (file === messageId || file.startsWith(`${messageId}:`)) {
              const filePath = path.join(dirPath, file);
              await fs.unlink(filePath);
              messageFound = true;
              break;
            }
          }
          
          if (messageFound) break;
        } catch (err) {
          // Continue to next directory if directory not found
          if (err.code === 'ENOENT') {
            continue;
          }
          throw err;
        }
      }
      
      if (!messageFound) {
        return res.status(404).json({ error: 'Message not found' });
      }
      
      return res.json({ message: 'Message deleted successfully' });
    } catch (error) {
      console.error('Error deleting message:', error);
      return res.status(500).json({ error: 'Failed to delete message' });
    }
  }

  /**
   * Get attachment from a message
   * 
   * @swagger
   * /mailboxes/{mailboxId}/messages/{messageId}/attachments/{attachmentId}:
   *   get:
   *     summary: Get message attachment
   *     description: Retrieves a specific attachment from a message
   *     tags: [Messages]
   *     security:
   *       - bearerAuth: []
   *     parameters:
   *       - in: path
   *         name: mailboxId
   *         required: true
   *         schema:
   *           type: integer
   *         description: Mailbox ID
   *       - in: path
   *         name: messageId
   *         required: true
   *         schema:
   *           type: string
   *         description: Message ID
   *       - in: path
   *         name: attachmentId
   *         required: true
   *         schema:
   *           type: string
   *         description: Attachment ID or filename
   *     responses:
   *       200:
   *         description: Attachment file
   *         content:
   *           application/octet-stream:
   *             schema:
   *               type: string
   *               format: binary
   *       404:
   *         description: Mailbox, message, or attachment not found
   *       500:
   *         description: Server error
   */
  async getAttachment(req, res) {
    try {
      const { mailboxId, messageId, attachmentId } = req.params;
      
      // Verify mailbox exists
      const mailbox = await models.Mailbox.query().findById(mailboxId);
      if (!mailbox) {
        return res.status(404).json({ error: 'Mailbox not found' });
      }
      
      // Check both new and cur directories for the message
      const directories = ['new', 'cur'];
      let messageContent = null;
      let messageFound = false;
      
      for (const dir of directories) {
        // In 'cur' directory, the message might have flags appended
        const dirPath = path.join('/var/mail', mailbox.domain, mailbox.local_part, dir);
        
        try {
          // Check if directory exists
          await fs.access(dirPath);
          
          // Read all files in the directory
          const files = await fs.readdir(dirPath);
          
          // Find files that start with the messageId
          for (const file of files) {
            if (file === messageId || file.startsWith(`${messageId}:`)) {
              const filePath = path.join(dirPath, file);
              
              // Read file content
              const content = await fs.readFile(filePath, { encoding: 'utf-8' });
              
              // Parse email content
              messageContent = await simpleParser(content);
              messageFound = true;
              break;
            }
          }
          
          if (messageFound) break;
        } catch (err) {
          // Continue to next directory if directory not found
          if (err.code === 'ENOENT') {
            continue;
          }
          throw err;
        }
      }
      
      if (!messageFound) {
        return res.status(404).json({ error: 'Message not found' });
      }
      
      // Find the attachment
      const attachment = messageContent.attachments?.find(
        att => att.contentId === attachmentId || att.filename === attachmentId
      );
      
      if (!attachment) {
        return res.status(404).json({ error: 'Attachment not found' });
      }
      
      // Set appropriate headers
      res.setHeader('Content-Type', attachment.contentType);
      res.setHeader('Content-Disposition', `attachment; filename="${attachment.filename}"`);
      
      // Send the attachment content
      return res.send(attachment.content);
    } catch (error) {
      console.error('Error fetching attachment:', error);
      return res.status(500).json({ error: 'Failed to fetch attachment' });
    }
  }
}

module.exports = new MessageController(); 