const path = require('path');
const fs = require('fs').promises;
const crypto = require('crypto');
const { models } = require(path.resolve(process.env.ROOT_PATH, './db/db.js'));
const BaseController = require(path.resolve(process.env.ROOT_PATH, './controllers/BaseController.js'));
const { format, addDays } = require('date-fns');
const crypt = require('unix-crypt-td-js');
const Docker = require('dockerode');
const docker = new Docker();
require('dotenv').config();

class MailboxController extends BaseController {
  constructor() {
    super();
    
    // Public endpoints
    this.protected('createTemporaryMailbox', 'getMailboxes', 'getMailbox', 'deleteMailbox');
  }

  /**
   * Create a temporary mailbox with random username
   * 
   * @swagger
   * /mailboxes/create:
   *   post:
   *     summary: Create a temporary mailbox
   *     description: Creates a temporary mailbox with a random username on a random active domain
   *     tags: [Mailboxes]
   *     security:
   *       - bearerAuth: []
   *     responses:
   *       201:
   *         description: Mailbox created successfully
   *         content:
   *           application/json:
   *             schema:
   *               type: object
   *               properties:
   *                 id:
   *                   type: integer
   *                   description: Mailbox ID
   *                 username:
   *                   type: string
   *                   description: Full email address
   *                 password:
   *                   type: string
   *                   description: Plain text password for the mailbox
   *                 expires:
   *                   type: string
   *                   format: date-time
   *                   description: Expiration date (14 days from creation)
   *       400:
   *         description: No active domains found
   *       500:
   *         description: Server error
   */
  async createTemporaryMailbox(req, res) {
    try {      
      // Validate domain exists
      const randomDomain = await models.Domain.query().where('active', 1).orderByRaw('RAND()').limit(1).first();
      if (!randomDomain) {
        return res.status(400).json({ error: 'No active domains found' });
      }
      
      // Generate random username (local part)
      const localPart = this._generateRandomUsername();
      const username = `${localPart}@${randomDomain.domain}`;
      const password = this._generateRandomPassword();
      
      // Create mailbox in database
      const mailbox = await models.Mailbox.query().insert({
        username,
        password: password.hashedPassword,
        name: `Temporary Mailbox ${localPart}`,
        domain: randomDomain.domain,
        local_part: localPart,
        quota: 102400000, // 100MB quota
        maildir: `/var/mail/${randomDomain.domain}/${localPart}`,
        active: 1,
        created: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
        modified: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
        expires: format(addDays(new Date(), 14), 'yyyy-MM-dd HH:mm:ss')
      });

      await models.Alias.query().insert({
        address: username,
        goto: username,
        domain: randomDomain.domain,
        created: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
        modified: format(new Date(), 'yyyy-MM-dd HH:mm:ss'),
        active: 1
      });

      const mailData = {
        id: mailbox.id,
        username: mailbox.username,
        password: password.password,
        expires: mailbox.expires
      }
      
      // Instead of manually creating directory, use docker-mailserver setup command
      try {
        const container = docker.getContainer(process.env.MAILSERVER_CONTAINER);
        
        // Add email
        await container.exec({
          Cmd: ['setup', 'email', 'add', username, password.password],
          AttachStdout: true,
          AttachStderr: true
        }).then(exec => exec.start());
        
        // Add alias
        await container.exec({
          Cmd: ['setup', 'alias', 'add', username, username],
          AttachStdout: true,
          AttachStderr: true
        }).then(exec => exec.start());

        console.log(`Created mailbox for ${username} using setup script`);
      } catch (err) {
        console.error(`Error creating mailbox via setup script: ${err.message}`);
        // Clean up the database entry since mailbox creation failed
        await models.Mailbox.query().deleteById(mailbox.id);
        await models.Alias.query().where('address', username).delete();
        return res.status(500).json({ error: 'Failed to create mailbox' });
      }
      
      return res.status(201).json(mailData);
    } catch (error) {
      console.error('Error creating temporary mailbox:', error);
      return res.status(500).json({ error: 'Failed to create mailbox' });
    }
  }

  /**
   * Get all mailboxes for the authenticated user
   * 
   * @swagger
   * /mailboxes:
   *   get:
   *     summary: Get all mailboxes
   *     description: Retrieves all mailboxes for the authenticated user
   *     tags: [Mailboxes]
   *     security:
   *       - bearerAuth: []
   *     responses:
   *       200:
   *         description: List of mailboxes
   *         content:
   *           application/json:
   *             schema:
   *               type: array
   *               items:
   *                 type: object
   *                 properties:
   *                   id:
   *                     type: integer
   *                   username:
   *                     type: string
   *                   domain:
   *                     type: string
   *                   local_part:
   *                     type: string
   *                   created:
   *                     type: string
   *                     format: date-time
   *                   expires:
   *                     type: string
   *                     format: date-time
   *       500:
   *         description: Server error
   */
  async getMailboxes(req, res) {
    try {
      const mailboxes = await models.Mailbox.query()
        .select(['id', 'username', 'domain', 'local_part', 'created', 'expires']);
      
      return res.json(mailboxes);
    } catch (error) {
      console.error('Error fetching mailboxes:', error);
      return res.status(500).json({ error: 'Failed to fetch mailboxes' });
    }
  }

  /**
   * Get a specific mailbox by ID
   * 
   * @swagger
   * /mailboxes/{id}:
   *   get:
   *     summary: Get mailbox by ID
   *     description: Retrieves a specific mailbox by its ID
   *     tags: [Mailboxes]
   *     security:
   *       - bearerAuth: []
   *     parameters:
   *       - in: path
   *         name: id
   *         required: true
   *         schema:
   *           type: integer
   *         description: Mailbox ID
   *     responses:
   *       200:
   *         description: Mailbox details
   *         content:
   *           application/json:
   *             schema:
   *               type: object
   *               properties:
   *                 id:
   *                   type: integer
   *                 username:
   *                   type: string
   *                 domain:
   *                   type: string
   *                 local_part:
   *                   type: string
   *                 created:
   *                   type: string
   *                   format: date-time
   *                 expires:
   *                   type: string
   *                   format: date-time
   *       404:
   *         description: Mailbox not found
   *       500:
   *         description: Server error
   */
  async getMailbox(req, res) {
    try {
      const { id } = req.params;
      const mailbox = await models.Mailbox.query()
        .findById(id)
        .select(['id', 'username', 'domain', 'local_part', 'created', 'expires']);
      
      if (!mailbox) {
        return res.status(404).json({ error: 'Mailbox not found' });
      }
      
      return res.json(mailbox);
    } catch (error) {
      console.error('Error fetching mailbox:', error);
      return res.status(500).json({ error: 'Failed to fetch mailbox' });
    }
  }

  /**
   * Delete a mailbox
   * 
   * @swagger
   * /mailboxes/{id}:
   *   delete:
   *     summary: Delete mailbox
   *     description: Deletes a mailbox by ID, including physical directory
   *     tags: [Mailboxes]
   *     security:
   *       - bearerAuth: []
   *     parameters:
   *       - in: path
   *         name: id
   *         required: true
   *         schema:
   *           type: integer
   *         description: Mailbox ID
   *     responses:
   *       200:
   *         description: Mailbox deleted successfully
   *         content:
   *           application/json:
   *             schema:
   *               type: object
   *               properties:
   *                 message:
   *                   type: string
   *                   example: Mailbox deleted successfully
   *       404:
   *         description: Mailbox not found
   *       500:
   *         description: Server error
   */
  async deleteMailbox(req, res) {
    try {
      const { id } = req.params;
      const mailbox = await models.Mailbox.query().where('id', id).first();
      
      if (!mailbox) {
        return res.status(404).json({ error: 'Mailbox not found' });
      }
      
      try {
        const container = docker.getContainer('mailserver');
        
        // Delete email
        await container.exec({
          Cmd: ['setup', 'email', 'del', '-y', mailbox.username],
          AttachStdout: true,
          AttachStderr: true
        }).then(exec => exec.start());
        
        // Delete alias
        await container.exec({
          Cmd: ['setup', 'alias', 'del', '-y', mailbox.username],
          AttachStdout: true,
          AttachStderr: true
        }).then(exec => exec.start());

        console.log(`Deleted mailbox for ${mailbox.username} using setup script`);
      } catch (err) {
        console.error(`Error deleting mailbox via setup script: ${err.message}`);
        // Continue anyway to delete from database as the mailbox might already be gone
      }
      
      // Delete from database
      await models.Mailbox.query().where('id', id).delete();
      await models.Alias.query().where('address', mailbox.username).delete();
      
      return res.json({ message: 'Mailbox deleted successfully' });
    } catch (error) {
      console.error('Error deleting mailbox:', error);
      return res.status(500).json({ error: 'Failed to delete mailbox' });
    }
  }

  /**
   * Generate a random username
   * @private
   */
  _generateRandomUsername() {
    // Generate a random string of 8 characters
    return crypto.randomBytes(4).toString('hex');
  }

  /**
   * Generate a random password
   * @private
   */
  _generateRandomPassword() {
    const password = crypto.randomBytes(6).toString('hex');
    
    // Generate a salt (8 characters)
    const salt = crypto.randomBytes(4).toString('hex').substring(0, 8);
    
    // Create MD5 crypt hash in the format $1$salt$hash using the proper algorithm
    const hashedPassword = crypt(password, `$1$${salt}`);

    return {
      password: password,
      hashedPassword: hashedPassword
    }
  }
}

module.exports = new MailboxController(); 