const fs = require('fs').promises;
const path = require('path');
const { models } = require('../db/db');
const { Mailbox, Alias } = models;
const { format, isBefore } = require('date-fns');

async function scanMailboxDirectories(callback) {
  const mailPath = '/var/mail';
  
  // Verify mail directory exists
  try {
    await fs.access(mailPath);
  } catch (err) {
    console.error(`Mail directory ${mailPath} does not exist or is not accessible`);
    return;
  }

  const domains = await fs.readdir(mailPath);
  console.log(`Found ${domains.length} domains in ${mailPath}`);
  
  for (const domain of domains) {
    const domainPath = path.join(mailPath, domain);
    const stat = await fs.stat(domainPath);
    
    if (stat.isDirectory()) {
      const users = await fs.readdir(domainPath);
      console.log(`Processing domain ${domain} with ${users.length} users`);
      
      for (const user of users) {
        const userPath = path.join(domainPath, user);
        const userStat = await fs.stat(userPath);
        
        if (userStat.isDirectory()) {
          await callback({ user, domain, userPath });
        }
      }
    }
  }
}

async function cleanupOrphanedMailboxes() {
  try {
    // Get all valid mailboxes from database - include ignore flag
    const validMailboxes = await Mailbox.query()
      .select('username', 'ignore');  // Add ignore to selection
    
    // Create a lookup map for quick checking
    const validMailboxMap = {};
    validMailboxes.forEach(mailbox => {
      const [user, domain] = mailbox.username.split('@');
      if (!validMailboxMap[domain]) validMailboxMap[domain] = new Map();
      validMailboxMap[domain].set(user, mailbox.ignore);  // Store ignore status
    });
    
    await scanMailboxDirectories(async ({ user, domain, userPath }) => {
      // Check if mailbox exists and is not ignored
      const hasMailbox = validMailboxMap[domain] && validMailboxMap[domain].has(user);
      const isIgnored = hasMailbox && validMailboxMap[domain].get(user) === 1;
      
      // Only remove if mailbox doesn't exist or is not ignored
      if (!hasMailbox || !isIgnored) {
        console.log(`Checking mailbox ${user}@${domain} - exists: ${hasMailbox}, ignored: ${isIgnored}`);
        console.log(`Removing orphaned mailbox: ${user}@${domain}`);
        await fs.rm(userPath, { recursive: true, force: true });
      }
    });
    
    console.log('Mailbox cleanup completed');
  } catch (error) {
    console.error('Error during mailbox cleanup:', error);
  }
}

async function cleanupUnmatchedAndExpired() {
  try {
    let expired = 0;
    let removed = 0;
    const mailboxes = await Mailbox.query()
      .select('username', 'domain', 'expires', 'ignore')
      .where('ignore', 0);
    
    const aliases = await Alias.query()
      .select('address', 'domain', 'ignore')
      .where('ignore', 0);
    
    // Create lookup maps for efficient matching
    const mailboxMap = new Map(
      mailboxes.map(m => [m.username, m])
    );
    
    const aliasMap = new Map(
      aliases.map(a => [a.address, a])
    );

    // Check mailboxes without matching aliases
    for (const [mailboxAddress, mailbox] of mailboxMap) {
      if (!aliasMap.has(mailboxAddress)) {
        console.log(`Removing mailbox without alias: ${mailboxAddress}`);
        await Mailbox.query()
          .where('username', mailbox.username)
          .patch({ active: 0 });
        removed++;
      }
    }

    // Check aliases without matching mailboxes
    for (const [aliasAddress, alias] of aliasMap) {
      if (!mailboxMap.has(aliasAddress)) {
        console.log(`Removing alias without mailbox: ${aliasAddress}`);
        await Alias.query()
          .where('address', alias.address)
          .patch({ active: 0 });
        removed++;
      }
    }

    // Check expiration for matched pairs
    const currentTime = format(new Date(), 'yyyy-MM-dd HH:mm:ss');
    for (const [mailboxAddress, mailbox] of mailboxMap) {
      console.log(`Checking ${mailboxAddress}: expires ${mailbox.expires} vs current ${currentTime}`);
      
      if (aliasMap.has(mailboxAddress) && isBefore(new Date(mailbox.expires), new Date())) {
        console.log(`Removing expired mailbox and alias: ${mailboxAddress}`);

        await Mailbox.query()
          .where('username', mailbox.username)
          .where('ignore', 0)
          .delete();
        
        await Alias.query()
          .where('address', mailboxAddress)
          .where('ignore', 0)
          .delete();

        expired++;
      }
    }

    await scanMailboxDirectories(async ({ user, domain, userPath }) => {
      const mailboxAddress = `${user}@${domain}`;
      const mailbox = mailboxMap.get(mailboxAddress);
      const alias = aliasMap.get(mailboxAddress);
      
      // Check if this mailbox exists in database with ignore=1
      const ignoredMailbox = await Mailbox.query()
        .where('username', mailboxAddress)
        .where('ignore', 1)
        .first();
      
      // Only remove if mailbox doesn't exist OR (exists, not ignored, and expired)
      if (!ignoredMailbox && (!mailbox || !alias || (mailbox && mailbox.expires < currentTime))) {
        console.log(`Removing directory for invalid/expired mailbox: ${mailboxAddress}`);
        await fs.rm(userPath, { recursive: true, force: true });
      }
    });

    console.log('Cleanup of unmatched and expired items completed');
  } catch (error) {
    console.error('Error during cleanup:', error);
  }
}

async function cleanupInactiveMailboxes() {
    try {
        const mailboxes = await Mailbox.query()
            .select('username', 'domain', 'active')
            .where('active', 0)
            .where('ignore', 0);

        const aliases = await Alias.query() 
            .select('address', 'domain', 'active')
            .where('active', 0)
            .where('ignore', 0);

        for (const mailbox of mailboxes) {
            await Mailbox.query()
                .where('username', mailbox.username)
                .where('ignore', 0)
                .delete();
        }

        for (const alias of aliases) {
            await Alias.query()
                .where('address', alias.address)
                .where('ignore', 0)
                .delete();
        }

        console.log('Cleanup of inactive mailboxes completed');
    } catch (error) {
        console.error('Error during cleanup:', error);
    }
}


module.exports = {
    cleanupOrphanedMailboxes,
    cleanupUnmatchedAndExpired,
    cleanupInactiveMailboxes
};