// sender.js — trimite prin localhost (SMTP 25) sau /usr/sbin/sendmail cu loguri clare
// Usage:
//   npm install nodemailer
//   node sender.js 1
//
// Fi?iere a?teptate în acela?i folder:
//   emails.txt     (un email pe linie)
//   subjects.txt   (un subiect pe linie; se alege random)
//   names.txt      (un nume pe linie; se alege random)
//   links.txt      (op?ional; un link pe linie; se alege random)
//   messages/*.txt  SAU  message.txt (po?i separa mai multe template-uri în acela?i fi?ier cu linii "---")
//
// Output:
//   sent.txt, failed.txt + loguri în consola
//
// Env op?ionale:
//   LOCAL_FROM="root@domeniu.tld"   -> expeditorul afi?at (From:). Implicit: root@<domain-ul destinatarului>
//   RATE_MS=800                     -> întârziere între emailuri/thread (ms). Implicit: 800
//   REPLY_TO="noreply@domeniu.tld"  -> seteaza Reply-To

const fs = require('fs');
const path = require('path');
const nodemailer = require('nodemailer');

let numberOfThreads = parseInt(process.argv[2], 10);
if (Number.isNaN(numberOfThreads) || numberOfThreads <= 0) numberOfThreads = 1;

const RATE_MS = parseInt(process.env.RATE_MS || '800', 10);
const REPLY_TO = process.env.REPLY_TO || '';

function readLines(file) {
  return fs.existsSync(file)
    ? fs.readFileSync(file, 'utf8')
        .split('\n')
        .map(s => s.trim())
        .filter(Boolean)
    : [];
}

// date de intrare
const emails   = readLines('emails.txt');
const subjects = readLines('subjects.txt');
const names    = readLines('names.txt');
const links    = readLines('links.txt');

// ?abloane (multiple)
const TEMPLATE_DIR = path.join(__dirname, 'messages');
const SINGLE_FILE  = path.join(__dirname, 'message.txt');
const DELIMITER    = /\r?\n-{3,}\r?\n/;

function loadTemplates() {
  let t = [];
  if (fs.existsSync(TEMPLATE_DIR)) {
    const files = fs.readdirSync(TEMPLATE_DIR).filter(f => f.toLowerCase().endsWith('.txt'));
    for (const f of files) {
      const c = fs.readFileSync(path.join(TEMPLATE_DIR, f), 'utf8').trim();
      if (c) t.push(c);
    }
  }
  if (!t.length && fs.existsSync(SINGLE_FILE)) {
    const all = fs.readFileSync(SINGLE_FILE, 'utf8');
    const parts = all.split(DELIMITER).map(s => s.trim()).filter(Boolean);
    if (parts.length) t = parts;
    else if (all.trim()) t = [all.trim()];
  }
  if (!t.length) throw new Error('Nu s-au gasit template-uri: ./messages/*.txt sau message.txt (separat cu "---").');
  return t;
}
const templates = loadTemplates();

function pickRandom(arr) { return arr[Math.floor(Math.random() * arr.length)]; }

function randNum(n){ n=Math.max(1,Number(n)||1); let s=''; for(let i=0;i<n;i++) s += Math.floor(Math.random()*10); return s; }
function randStr(n){ n=Math.max(1,Number(n)||1); const a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; let s=''; for(let i=0;i<n;i++) s += a[Math.floor(Math.random()*a.length)]; return s; }
const domainOf = (email) => (email.split('@')[1] || '').trim();
const today = () => { const d=new Date(); return `${String(d.getDate()).padStart(2,'0')}.${String(d.getMonth()+1).padStart(2,'0')}.${d.getFullYear()}`; };

function render(tpl, ctx) {
  return tpl
    .replace(/#RANDOMNUM\((\d+)\)#/g, (_m,n)=>randNum(n))
    .replace(/#RANDOM\((\d+)\)#/g,    (_m,n)=>randStr(n))
    .replace(/#EMAIL#/g,  String(ctx.recipient ?? ''))
    .replace(/#DOMAIN#/g, String(ctx.domain ?? ''))
    .replace(/#DATE#/g,   String(ctx.date ?? ''))
    .replace(/#LINK#/g,   String(ctx.link ?? ''));
}

async function createLocalSmtpTransport() {
  // SMTP local (25), fara autentificare
  return nodemailer.createTransport({
    host: '127.0.0.1',
    port: 25,
    secure: false,
    tls: { rejectUnauthorized: false }
  });
}
function createSendmailTransport() {
  // fallback direct /usr/sbin/sendmail
  return nodemailer.createTransport({
    sendmail: true,
    newline: 'unix',
    path: '/usr/sbin/sendmail'
  });
}

function sleep(ms){ return new Promise(r=>setTimeout(r, ms)); }

let sentCount = 0;
let failCount = 0;

async function sendWorker(threadIndex) {
  let transporter;
  let transportLabel = '';

  try {
    transporter = await createLocalSmtpTransport();
    await transporter.verify(); // test handshake
    transportLabel = 'localhost';
    console.log(`[Info] Thread ${threadIndex}: using localhost SMTP :25`);
  } catch (e) {
    transporter = createSendmailTransport();
    transportLabel = 'sendmail';
    console.log(`[Info] Thread ${threadIndex}: localhost SMTP unavailable, using sendmail fallback`);
  }

  for (let j = threadIndex; j < emails.length; j += numberOfThreads) {
    const recipient = emails[j];
    if (!recipient) continue;

    const ctx = {
      recipient,
      domain: domainOf(recipient),
      date: today(),
      link: links.length ? pickRandom(links) : ''
    };

    // subiect, nume, corp
    const subject = subjects.length ? render(pickRandom(subjects), ctx) : ' ';
    const displayName = names.length ? render(pickRandom(names), ctx) : 'Mailer';
    const html = render(pickRandom(templates), ctx);

    // From afi?at: daca nu e setat LOCAL_FROM, folosim un fallback root@<domain destinatar>
    const fromAddr = process.env.LOCAL_FROM || `root@${ctx.domain || 'localhost'}`;

    const mail = {
      from: `${displayName} <${fromAddr}>`,
      to: recipient,
      subject,
      html,
      headers: {
        'List-Unsubscribe': `<mailto:${fromAddr}?subject=unsubscribe>`
      }
    };
    if (REPLY_TO) mail.replyTo = REPLY_TO;

    try {
      await transporter.sendMail(mail);
      fs.appendFileSync('sent.txt', recipient + '\n');
      sentCount++;
      console.log(`[Sent] Email sent to ${recipient} by thread ${threadIndex} using ${transportLabel}`);
    } catch (err) {
      fs.appendFileSync('failed.txt', recipient + '\n');
      failCount++;
      console.error(`[Error] Failed to send to ${recipient} by thread ${threadIndex} using ${transportLabel}: ${err && err.message ? err.message : err}`);

      // fallback: daca a e?uat pe localhost, încearca sendmail o singura data
      if (transportLabel !== 'sendmail') {
        try {
          const fallback = createSendmailTransport();
          await fallback.sendMail(mail);
          fs.appendFileSync('sent.txt', recipient + '\n');
          sentCount++;
          console.log(`[Sent] Email sent to ${recipient} by thread ${threadIndex} using sendmail`);
        } catch (e2) {
          fs.appendFileSync('failed.txt', recipient + '\n');
          failCount++;
          console.error(`[Error] Fallback sendmail failed for ${recipient}: ${e2 && e2.message ? e2.message : e2}`);
        }
      }
    }

    await sleep(RATE_MS);
  }
}

(async () => {
  if (!emails.length) {
    console.log('[Info] emails.txt este gol sau lipse?te.');
    console.log('Done. Sent: 0 | Failed: 0');
    return;
  }
  const workers = [];
  for (let i = 0; i < numberOfThreads; i++) {
    workers.push(sendWorker(i));
  }
  await Promise.allSettled(workers);
  console.log(`Done. Sent: ${sentCount} | Failed: ${failCount}`);
})();
