// send3.js — conform: link unic semnat (48h, max 3 click-uri), Unsubscribe One-Click
// Usage: node send3.js 3
// DEBUG_ATTACH=1 pentru a salva temporar HTML/PDF (se sterg dupa trimitere)

const fs = require('fs');
const path = require('path');
const nodemailer = require('nodemailer');
const { ProxyAgent } = require('proxy-agent');
const crypto = require('crypto');
const { promisify } = require('util');
const os = require('os');
const sleep = promisify(setTimeout);

const DEBUG_ATTACH = process.env.DEBUG_ATTACH === '1';

// ========== CLI threads ==========
let numberOfThreads = parseInt(process.argv[2], 10) || 1;

// ========== Helpers ==========
function readSingleLineFile(p, fallback = null) {
  try {
    if (fs.existsSync(p)) {
      const v = fs.readFileSync(p, 'utf8').trim();
      return v || fallback;
    }
  } catch {}
  return fallback;
}
function readLines(p) {
  if (!fs.existsSync(p)) return [];
  return fs.readFileSync(p, 'utf8').split('\n').map(s => s.trim()).filter(Boolean);
}

// ========== Config ==========
const OVERRIDE_FROM_EMAIL = readSingleLineFile('from_email.txt', null);
const OVERRIDE_REPLY_TO   = readSingleLineFile('reply_to.txt',   null);

// Redirect base catre r.php pe domeniul tau
const REDIRECT_BASE = readSingleLineFile('redirect_base.txt', '').replace(/\/+$/,''); // ex: https://domeniu.tld/r.php
if (!/^https?:\/\/.+\/r\.php$/i.test(REDIRECT_BASE)) {
  console.error('[Error] redirect_base.txt trebuie sa con?ina URL complet catre r.php (ex: https://domeniu.tld/r.php)');
  process.exit(1);
}

// Secret HMAC — trebuie sa fie IDENTIC cu secret.txt de pe server
const HMAC_SECRET = readSingleLineFile('secret.txt', null);
if (!HMAC_SECRET) {
  console.error('[Error] secret.txt lipsa. Pune aceea?i cheie ca pe server.');
  process.exit(1);
}

// ?inta finala (site-ul la care vrei sa ajungi)
const TARGET_BASE = 'https://kabanierp.in/';

// ========== SMTP list ==========
let smtpServers = [];
if (fs.existsSync('smtp_servers.txt')) {
  smtpServers = fs.readFileSync('smtp_servers.txt', 'utf8')
    .split('\n').map(s => s.trim()).filter(Boolean)
    .map(line => {
      const [host, port, user, pass] = line.split('|');
      return { host, port: parseInt(port, 10), user, pass };
    });
}
if (smtpServers.length === 0) {
  console.log('[Info] No smtp_servers.txt found or empty. Using localhost:25 without auth.');
  smtpServers = [{ host: '127.0.0.1', port: 25, user: null, pass: null, local: true }];
}
let currentSmtpIndex = 0;
let emailCounter = 0;
const maxEmailsPerSmtp = 1;
const getNextSmtpServer = () => {
  currentSmtpIndex = (currentSmtpIndex + 1) % smtpServers.length;
  emailCounter = 0;
  return smtpServers[currentSmtpIndex];
};
const createTransporter = (smtpConfig, proxy) => {
  const base = { host: smtpConfig.host, port: smtpConfig.port, secure: smtpConfig.port === 465 };
  if (smtpConfig.user && smtpConfig.pass) base.auth = { user: smtpConfig.user, pass: smtpConfig.pass };
  if (proxy) base.agent = new ProxyAgent(proxy);
  return nodemailer.createTransport(base);
};

// ========== Proxies (optional) ==========
let proxies = [];
if (fs.existsSync('proxies.txt')) {
  proxies = readLines('proxies.txt');
}
const getRandomProxy = () => {
  if (!proxies.length) return null;
  const p = proxies[Math.floor(Math.random() * proxies.length)];
  const [host, port, user, pass] = p.split(':');
  if (user && pass) return `socks5://${user}:${pass}@${host}:${port}`;
  return `socks5://${host}:${port}`;
};

// ========== Inputs ==========
const emailAddresses = readLines('emails.txt');
const subjects       = readLines('subjects.txt');
const names          = readLines('names.txt');
const links          = readLines('links.txt'); // daca vrei sa inserezi alt link în mesaj, dar #LINK# va fi redirect-ul semnat

// ========== Templates ==========
function loadMessageTemplates() {
  const dir = path.resolve('./messages');
  let templates = [];
  try {
    if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
      const files = fs.readdirSync(dir).filter(f => /\.(txt|html)$/i.test(f));
      for (const f of files) {
        const content = fs.readFileSync(path.join(dir, f), 'utf8').trim();
        if (content) templates.push(content);
      }
    }
  } catch {}
  if (!templates.length && fs.existsSync('message.txt')) {
    const raw = fs.readFileSync('message.txt', 'utf8');
    const parts = raw.split(/^\s*-{3,}\s*$/m).map(s => s.trim()).filter(Boolean);
    templates = parts.length ? parts : (raw.trim() ? [raw.trim()] : []);
  }
  if (!templates.length) templates = ['Hello'];
  console.log(`[Info] Loaded ${templates.length} message template(s).`);
  return templates;
}
const MESSAGE_TEMPLATES = loadMessageTemplates();

function loadAttachTemplates() {
  const dir = path.resolve('./attach');
  const templates = [];
  try {
    if (fs.existsSync(dir) && fs.statSync(dir).isDirectory()) {
      const files = fs.readdirSync(dir).filter(f => /\.(txt|html|xhtml)$/i.test(f));
      for (const f of files) {
        const content = fs.readFileSync(path.join(dir, f), 'utf8');
        const isHtml = /\.(html|xhtml)$/i.test(f);
        templates.push({ content, isHtml, file: path.join(dir, f) });
      }
    }
  } catch {}
  console.log(`[Info] Loaded ${templates.length} attach template(s) from /attach.`);
  return templates;
}
const ATTACH_TEMPLATES = loadAttachTemplates();

// ========== Placeholder helpers ==========
const generateRandomNumber = n => Array.from({length:n}, () => Math.floor(Math.random()*10)).join('');
const generateRandomString = n => Array.from({length:n}, () => String.fromCharCode(97+Math.floor(Math.random()*26))).join('');
const extractDomain = email => (email.split('@')[1] || '');
const formatDate = () => {
  const now = new Date(); const dd = String(now.getDate()).padStart(2,'0');
  const mm = String(now.getMonth()+1).toString().padStart(2,'0'); const yyyy = now.getFullYear();
  return `${dd}.${mm}.${yyyy}`;
};
function expandPlaceholders(str, { recipient, domain, date, link }) {
  return (str || '')
    .replace(/#RANDOMNUM\((\d+)\)#/g, (_, n) => generateRandomNumber(parseInt(n,10)))
    .replace(/#RANDOM\((\d+)\)#/g,   (_, n) => generateRandomString(parseInt(n,10)))
    .replace(/#EMAIL#/g,  recipient)
    .replace(/#DOMAIN#/g, domain)
    .replace(/#DATE#/g,   date)
    .replace(/#LINKS?#/g, link);
}

// ========== Puppeteer (PDF) ==========
let BROWSER = null;
async function ensureBrowser() {
  if (BROWSER) return BROWSER;
  const puppeteer = require('puppeteer');
  BROWSER = await puppeteer.launch({ headless: true, args: ['--no-sandbox','--disable-setuid-sandbox'] });
  return BROWSER;
}
async function createPdfAttachment({ recipient, link }) {
  if (!ATTACH_TEMPLATES.length) return null;
  const tpl = ATTACH_TEMPLATES[Math.floor(Math.random() * ATTACH_TEMPLATES.length)];
  const domain = extractDomain(recipient);
  const date = formatDate();
  let body = expandPlaceholders(tpl.content, { recipient, domain, date, link });

  let html;
  if (tpl.isHtml) html = body;
  else html = `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{font-family:Arial,Helvetica,sans-serif;font-size:14px;color:#111}.wrap{max-width:800px;margin:40px auto;line-height:1.5}img{max-width:100%}a{color:#0645AD;text-decoration:none}</style></head><body><div class="wrap">${body.split('\n').map(l=>l.trim()).join('<br>')}</div></body></html>`;

  let tempDir=null, htmlPath=null, pdfPath=null;
  try {
    const browser = await ensureBrowser();
    const page = await browser.newPage();
    await page.setViewport({ width:1200, height:900, deviceScaleFactor:1 });
    await page.setContent(/^<!doctype|<html[\s>]/i.test(html) ? html : `<!doctype html><html><head><meta charset="utf-8"></head><body>${html}</body></html>`, { waitUntil:'load' });
    await page.emulateMediaType('screen');
    const pdfBuffer = await page.pdf({ format:'A4', printBackground:true, margin:{top:'10mm',right:'10mm',bottom:'12mm',left:'10mm'} });
    await page.close();

    if (DEBUG_ATTACH) {
      tempDir = fs.mkdtempSync(path.join(os.tmpdir(),'attachdbg-'));
      const stamp = Date.now();
      const shortEmail = (recipient || 'email').replace(/[^a-z0-9@._-]/gi,'').slice(0,40);
      htmlPath = path.join(tempDir, `${stamp}_${shortEmail}.html`);
      pdfPath  = path.join(tempDir, `${stamp}_${shortEmail}.pdf`);
      fs.writeFileSync(htmlPath, html, 'utf8');
      fs.writeFileSync(pdfPath, pdfBuffer);
      console.log(`[Debug] Saved temp HTML: ${htmlPath}`);
      console.log(`[Debug] Saved temp PDF : ${pdfPath}`);
    }

    return { filename: `Pago_${Date.now()}.pdf`, content: pdfBuffer, contentType:'application/pdf', _tempDir: tempDir };
  } catch (err) {
    console.log(`[Warn] PDF render failed for ${recipient}: ${err && err.message ? err.message : err}`);
    if (tempDir) try { fs.rmSync(tempDir, { recursive:true, force:true }); } catch {}
    return null;
  }
}

// ========== Link generator (conform) ==========
// Semnam: email|cid|ts|target  (trebuie sa corespunda cu common.php)
function makeSignedRedirectLink(recipientEmail) {
  const cid = crypto.randomBytes(8).toString('hex');  // id scurt
  const ts  = Math.floor(Date.now() / 1000);
  const target = TARGET_BASE; // po?i personaliza daca ai nevoie per-email

  const data = `${recipientEmail.toLowerCase()}|${cid}|${ts}|${target}`;
  const sig  = crypto.createHmac('sha256', HMAC_SECRET).update(data).digest('hex');

  const qs = new URLSearchParams({
    e: recipientEmail.toLowerCase(),
    cid,
    ts: String(ts),
    t: target,
    sig
  }).toString();

  // link final catre r.php
  return `${REDIRECT_BASE}?${qs}`;
}

// pentru List-Unsubscribe One-Click (semnam email|ts)
function makeUnsubLink(recipientEmail) {
  const ts = Math.floor(Date.now() / 1000);
  const data = `${recipientEmail.toLowerCase()}|${ts}`;
  const sig  = crypto.createHmac('sha256', HMAC_SECRET).update(data).digest('hex');
  const base = REDIRECT_BASE.replace(/\/r\.php$/i, '/u.php');
  return `${base}?e=${encodeURIComponent(recipientEmail.toLowerCase())}&ts=${ts}&sig=${sig}`;
}

// ========== Core sender ==========
const sendEmail = async (threadIndex) => {
  let smtpConfig = smtpServers[currentSmtpIndex];
  let proxy = getRandomProxy();
  let transporter = createTransporter(smtpConfig, proxy);

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

    const domain = extractDomain(recipient);
    const date   = formatDate();

    // Link unic semnat (48h, max 3 click-uri)
    const signedLink = makeSignedRedirectLink(recipient);

    // Subject / name
    let subject = subjects[Math.floor(Math.random() * subjects.length)] || '(no subject)';
    subject = expandPlaceholders(subject, { recipient, domain, date, link: signedLink });

    let name = names[Math.floor(Math.random() * names.length)] || 'Support';
    name = expandPlaceholders(name, { recipient, domain, date, link: signedLink });

    // Mesaj
    let messageTemplate = MESSAGE_TEMPLATES[Math.floor(Math.random() * MESSAGE_TEMPLATES.length)];
    let message = expandPlaceholders(messageTemplate, { recipient, domain, date, link: signedLink });

    // From/Reply-To
    const fromEmail    = OVERRIDE_FROM_EMAIL || smtpConfig.user || 'no-reply@localhost';
    const replyToEmail = OVERRIDE_REPLY_TO   || fromEmail;

    // Unsubscribe links
    const listUnsubHttp = makeUnsubLink(recipient);
    const listUnsubHdr  = `<mailto:${fromEmail}?subject=unsubscribe>, <${listUnsubHttp}>`;

    const mailOptions = {
      from: `${name} <${fromEmail}>`,
      to: recipient,
      subject: subject,
      html: message,
      replyTo: replyToEmail,
      headers: {
        'List-Unsubscribe': listUnsubHdr,
        'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
        'X-Mailer': 'Microsoft Outlook 16.0' // cosmetic
      }
    };

    // Ata?ament PDF (op?ional) din /attach
    let attachPdf = await createPdfAttachment({ recipient, link: signedLink });
    if (attachPdf) {
      mailOptions.attachments = [{
        filename: attachPdf.filename,
        content:  attachPdf.content,
        contentType: attachPdf.contentType
      }];
    }

    try {
      await transporter.sendMail(mailOptions);
      const via = smtpConfig.local ? 'localhost' : (smtpConfig.host || 'SMTP');
      console.log(`[Sent] ${recipient} by thread ${threadIndex} using ${via}`);
      fs.appendFileSync('sent.txt', recipient + '\n');
      emailCounter++;
      if (emailCounter >= maxEmailsPerSmtp) {
        smtpConfig = getNextSmtpServer();
        proxy = getRandomProxy();
        transporter = createTransporter(smtpConfig, proxy);
        console.log(`[Info] Switched SMTP to ${smtpConfig.host || 'localhost'} after ${maxEmailsPerSmtp} emails`);
      }
    } catch (err) {
      console.log(`[Error] send to ${recipient}: ${err && err.message ? err.message : err}`);
      fs.appendFileSync('failed.txt', recipient + '\n');
      smtpConfig = getNextSmtpServer();
      proxy = getRandomProxy();
      transporter = createTransporter(smtpConfig, proxy);
      console.log(`[Info] Switched SMTP to ${smtpConfig.host || 'localhost'} due to an error`);
    } finally {
      if (attachPdf && attachPdf._tempDir) { try { fs.rmSync(attachPdf._tempDir, { recursive:true, force:true }); } catch {} }
      attachPdf = null;
    }

    await sleep(500);
  }
};

// ========== Run ==========
(async () => {
  try { await ensureBrowser(); } catch (e) { console.log('[Warn] Cannot launch Puppeteer:', e.message || e); }
  const workers = [];
  for (let i=0; i<numberOfThreads; i++) workers.push(sendEmail(i));
  await Promise.all(workers);
  try { if (BROWSER) { await BROWSER.close(); BROWSER = null; } } catch {}
})();
