const Imap = require('imap');
const nodemailer = require('nodemailer');
const { simpleParser } = require('mailparser');
const { getDb } = require('./db');
const util = require('util');

function getAccount(accountId) {
  const db = getDb();
  return db.prepare('SELECT * FROM email_accounts WHERE id = ?').get(accountId);
}

function getUserAccounts(userId) {
  const db = getDb();
  return db.prepare('SELECT id, email, name, imap_host, smtp_host, active FROM email_accounts WHERE user_id = ?').all(userId);
}

function createImapConnection(account) {
  return new Promise((resolve, reject) => {
    const imap = new Imap({
      user: account.imap_user,
      password: account.imap_pass,
      host: account.imap_host,
      port: account.imap_port || 993,
      tls: account.imap_secure !== 0,
      tlsOptions: { rejectUnauthorized: false },
      connTimeout: 10000,
      authTimeout: 5000
    });
    imap.once('ready', () => resolve(imap));
    imap.once('error', (err) => reject(err));
    imap.connect();
  });
}

function openMailbox(imap, folder) {
  return new Promise((resolve, reject) => {
    imap.openBox(folder || 'INBOX', true, (err, box) => {
      if (err) return reject(err);
      resolve(box);
    });
  });
}

function searchMessages(imap, criteria) {
  return new Promise((resolve, reject) => {
    imap.search(criteria, (err, uids) => {
      if (err) return reject(err);
      resolve(uids || []);
    });
  });
}

function fetchMessages(imap, uids) {
  return new Promise((resolve, reject) => {
    if (!uids || uids.length === 0) return resolve([]);
    const f = imap.fetch(uids, { bodies: '', struct: true });
    const messages = [];
    f.on('message', (msg, seqno) => {
      const message = { uid: null, seqno, body: '' };
      msg.on('body', (stream, info) => {
        let buffer = '';
        stream.on('data', chunk => buffer += chunk.toString('utf8'));
        stream.on('end', () => { message.body = buffer; });
      });
      msg.on('attributes', attrs => {
        message.uid = attrs.uid;
        message.flags = attrs.flags || [];
        message.date = attrs.date;
        message.size = attrs.size;
      });
      msg.on('end', () => { if (message.uid) messages.push(message); });
    });
    f.once('error', reject);
    f.once('end', () => resolve(messages));
  });
}

async function fetchAndStoreMessages(accountId, folder, limit) {
  const account = getAccount(accountId);
  if (!account) throw new Error('Account not found');
  const db = getDb();
  const imap = await createImapConnection(account);
  const box = await openMailbox(imap, folder || 'INBOX');
  const total = Math.min(box.messages.total, limit || 50);
  const start = Math.max(1, box.messages.total - total + 1);
  const uids = [];
  for (let i = start; i <= box.messages.total; i++) uids.push(i);
  if (uids.length === 0) { imap.end(); return []; }
  const messages = await fetchMessages(imap, uids);
  imap.end();
  const results = [];
  for (const msg of messages) {
    try {
      const parsed = await simpleParser(msg.body);
      const seen = msg.flags.includes('\\Seen');
      const flagged = msg.flags.includes('\\Flagged');
      const existing = db.prepare('SELECT id FROM email_messages WHERE account_id = ? AND uid = ?').get(accountId, msg.uid);
      if (!existing) {
        db.prepare(`INSERT INTO email_messages (account_id, uid, folder, subject, from_name, from_addr, to_addr, date, body_text, body_html, attachments, seen, flagged, size)
          VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(
          accountId, msg.uid, folder || 'INBOX',
          (parsed.subject || '').substring(0, 500),
          (parsed.from && parsed.from.text || '').substring(0, 200),
          (parsed.from && parsed.from.value && parsed.from.value[0] && parsed.from.value[0].address || ''),
          (parsed.to && parsed.to.text || '').substring(0, 500),
          (parsed.date && parsed.date.toISOString() || msg.date || ''),
          (parsed.text || '').substring(0, 100000),
          (parsed.html || '').substring(0, 100000),
          (parsed.attachments && parsed.attachments.length) || 0,
          seen ? 1 : 0, flagged ? 1 : 0, msg.size || 0
        );
      } else {
        db.prepare('UPDATE email_messages SET seen=?, flagged=? WHERE id=?').run(seen ? 1 : 0, flagged ? 1 : 0, existing.id);
      }
      results.push({
        uid: msg.uid, subject: parsed.subject || '', from: parsed.from ? parsed.from.text : '',
        date: parsed.date || msg.date, seen, flagged, size: msg.size
      });
    } catch (e) {
      console.error('Error parsing message:', e.message);
    }
  }
  return results;
}

async function sendEmail(accountId, to, subject, text, html, cc, bcc) {
  const account = getAccount(accountId);
  if (!account) throw new Error('Account not found');
  const transporter = nodemailer.createTransport({
    host: account.smtp_host,
    port: account.smtp_port || 587,
    secure: account.smtp_secure === 1,
    auth: { user: account.smtp_user || account.imap_user, pass: account.smtp_pass || account.imap_pass }
  });
  const info = await transporter.sendMail({
    from: `"${account.name || account.email}" <${account.email}>`,
    to, subject, text, html, cc, bcc
  });
  return info;
}

async function getMailboxes(accountId) {
  const account = getAccount(accountId);
  if (!account) return [];
  const imap = await createImapConnection(account);
  const list = await new Promise((resolve, reject) => {
    imap.getBoxes((err, boxes) => {
      if (err) return reject(err);
      resolve(boxes);
    });
  });
  imap.end();
  function flatten(boxes, path) {
    const result = [];
    for (const [name, box] of Object.entries(boxes)) {
      const fullPath = path ? `${path}${box.delimiter}${name}` : name;
      result.push({ name: fullPath, delimiter: box.delimiter });
      if (box.children) result.push(...flatten(box.children, fullPath));
    }
    return result;
  }
  return flatten(list, '');
}

async function getMessageBody(accountId, uid) {
  const account = getAccount(accountId);
  if (!account) return null;
  const imap = await createImapConnection(account);
  await openMailbox(imap, 'INBOX');
  const messages = await fetchMessages(imap, [uid]);
  imap.end();
  if (messages.length === 0) return null;
  const parsed = await simpleParser(messages[0].body);
  return parsed;
}

module.exports = { getAccount, getUserAccounts, createImapConnection, openMailbox, searchMessages, fetchMessages, fetchAndStoreMessages, sendEmail, getMailboxes, getMessageBody };
