<?php
// ─── CONFIG ───────────────────────────────────────────────────────────────────
define('UPLOAD_DIR',    __DIR__ . '/uploads/');
define('ATTACH_DIR',    __DIR__ . '/attachments/');
define('BATCH_SIZE',    20);   // ← changed from 50 to 20
define('STATE_FILE',    __DIR__ . '/data/state.json');
define('CAMPAIGN_FILE', __DIR__ . '/data/campaign.json');

foreach ([UPLOAD_DIR, ATTACH_DIR, __DIR__ . '/data'] as $d) {
    if (!is_dir($d)) mkdir($d, 0755, true);
}

// ─── DATABASE CONFIG ──────────────────────────────────────────────────────────
define('DB_HOST', 'localhost');
define('DB_USER', 'ubsforums_campaign');
define('DB_PASS', 'camp@202526#');
define('DB_NAME', 'ubsforums_campaign');

function getDB(): ?mysqli {
    static $conn = null;
    if ($conn instanceof mysqli && $conn->ping()) return $conn;
    $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    if ($conn->connect_error) {
        error_log('BulkMailer DB error: ' . $conn->connect_error);
        return null;
    }
    $conn->set_charset('utf8mb4');
    // Auto-create tables on first connect
    $conn->query("CREATE TABLE IF NOT EXISTS bm_campaigns (
        id              INT AUTO_INCREMENT PRIMARY KEY,
        excel_file      VARCHAR(255) NOT NULL DEFAULT '',
        subject         VARCHAR(500) NOT NULL DEFAULT '',
        from_name       VARCHAR(255) NOT NULL DEFAULT '',
        from_email      VARCHAR(255) NOT NULL DEFAULT '',
        total_raw       INT NOT NULL DEFAULT 0,
        total_clean     INT NOT NULL DEFAULT 0,
        junk_removed    INT NOT NULL DEFAULT 0,
        attachments     TEXT,
        sent            INT NOT NULL DEFAULT 0,
        failed          INT NOT NULL DEFAULT 0,
        skipped         INT NOT NULL DEFAULT 0,
        status          VARCHAR(20) NOT NULL DEFAULT 'running',
        started_at      DATETIME,
        completed_at    DATETIME DEFAULT NULL,
        created_at      DATETIME DEFAULT CURRENT_TIMESTAMP
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");

    $conn->query("CREATE TABLE IF NOT EXISTS bm_email_log (
        id              INT AUTO_INCREMENT PRIMARY KEY,
        campaign_id     INT NOT NULL,
        email           VARCHAR(255) NOT NULL,
        first_name      VARCHAR(255) NOT NULL DEFAULT '',
        last_name       VARCHAR(255) NOT NULL DEFAULT '',
        status          VARCHAR(20)  NOT NULL,
        reason          VARCHAR(500) DEFAULT NULL,
        domain_issue    TINYINT(1)   NOT NULL DEFAULT 0,
        batch_no        INT          NOT NULL DEFAULT 0,
        sent_at         DATETIME     DEFAULT CURRENT_TIMESTAMP,
        INDEX idx_camp  (campaign_id),
        INDEX idx_status(status)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
    return $conn;
}

// ─── DOMAIN VALIDATION ────────────────────────────────────────────────────────
function isCleanEmail(string $email): bool {
    $email = trim($email);
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
    $parts  = explode('@', $email, 2);
    $domain = strtolower(trim($parts[1] ?? ''));
    if (preg_match('/^\[?\d{1,3}(\.\d{1,3}){3}\]?$/', $domain)) return false;
    $junkPatterns = [
        '/\.test-google-a\.com$/i', '/\.test$/i', '/\.local$/i',
        '/\.invalid$/i', '/\.example$/i', '/\.localhost$/i',
        '/secureserver\.net$/i', '/host\.secureserver\.net$/i',
    ];
    foreach ($junkPatterns as $pat) {
        if (preg_match($pat, $domain)) return false;
    }
    if (preg_match('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $domain)) return false;
    if (!preg_match('/^[^.]+\.[a-z]{2,}$/i', $domain) &&
        !preg_match('/^.+\.[a-z]{2,}$/i', $domain)) return false;
    return true;
}

function domainIssueHint(string $email): string {
    $parts  = explode('@', $email, 2);
    $domain = strtolower(trim($parts[1] ?? ''));
    if (preg_match('/\.\./', $domain))                              return 'Double dot in domain';
    if (preg_match('/\.$/', $domain))                              return 'Trailing dot in domain';
    if (preg_match('/\.(con|cmo|ocm|nte|og|cm|co\.i)$/', $domain)) return 'Possible TLD typo';
    if (substr_count($domain, '.') >= 4)                           return 'Unusual domain depth';
    if (preg_match('/\d{2,}\.\d{2,}/', $domain))                  return 'Numeric host segments';
    if (preg_match('/test-google/i', $domain))                     return 'Test/sandbox domain';
    if (preg_match('/secureserver/i', $domain))                    return 'Server hostname, not real email';
    return '';
}

// ─── AJAX HANDLERS ────────────────────────────────────────────────────────────
$action = $_GET['action'] ?? $_POST['action'] ?? '';

if ($action) {
    header('Content-Type: application/json');

    switch ($action) {

        // ── Upload Excel
        case 'upload_excel':
            if (!isset($_FILES['excel'])) { echo json_encode(['ok'=>false,'msg'=>'No file']); exit; }
            $f   = $_FILES['excel'];
            $ext = strtolower(pathinfo($f['name'], PATHINFO_EXTENSION));
            if (!in_array($ext, ['xlsx','xls'])) { echo json_encode(['ok'=>false,'msg'=>'Only .xlsx/.xls']); exit; }
            $dest = UPLOAD_DIR . 'recipients.' . $ext;
            move_uploaded_file($f['tmp_name'], $dest);
            require_once __DIR__ . '/lib/SimpleXlsxParser.php';
            $rows = [];
            if ($xlsx = SimpleXlsx::parse($dest)) {
                $data    = $xlsx->rows();
                $headers = array_map('strtolower', array_map('trim', $data[0]));
                for ($i = 1; $i < count($data); $i++) {
                    $row = [];
                    foreach ($headers as $k => $h) $row[$h] = $data[$i][$k] ?? '';
                    // Fix: strip hidden whitespace/newlines from email field
                    if (isset($row['email'])) $row['email'] = trim(preg_replace('/\s+/', '', (string)$row['email']));
                    if (!empty(trim($row['email'] ?? ''))) $rows[] = $row;
                }
            }
            $cleanRows = array_filter($rows, fn($r) => isCleanEmail(trim($r['email'])));
            $junkCount = count($rows) - count($cleanRows);
            $state = [
                'file'           => $f['name'],
                'total'          => count($cleanRows),
                'total_raw'      => count($rows),
                'junk_count'     => $junkCount,
                'rows'           => array_values($cleanRows),
                'sent'           => 0,
                'failed'         => 0,
                'skipped'        => 0,
                'processing'     => false,
                'batch_done'     => 0,
                'started_at'     => null,
                'campaign_db_id' => 0,
                'log'            => []
            ];
            file_put_contents(STATE_FILE, json_encode($state));
            echo json_encode(['ok'=>true,'total'=>count($cleanRows),'total_raw'=>count($rows),'junk_count'=>$junkCount]);
            exit;

        // ── Upload attachment file
        case 'upload_attachment':
            if (!isset($_FILES['attachment'])) { echo json_encode(['ok'=>false,'msg'=>'No file']); exit; }
            $f    = $_FILES['attachment'];
            $name = preg_replace('/[^a-zA-Z0-9._\-]/', '_', basename($f['name']));
            $dest = ATTACH_DIR . $name;
            move_uploaded_file($f['tmp_name'], $dest);
            $size = round(filesize($dest) / 1024, 1);
            echo json_encode(['ok'=>true,'name'=>$name,'size'=>$size.'KB','path'=>$dest]);
            exit;

        // ── Remove attachment
        case 'remove_attachment':
            $name = basename($_POST['name'] ?? '');
            $path = ATTACH_DIR . $name;
            if ($name && file_exists($path)) unlink($path);
            echo json_encode(['ok'=>true]);
            exit;

        // ── List attachments
        case 'list_attachments':
            $files = [];
            foreach (glob(ATTACH_DIR . '*') as $p) {
                $files[] = ['name'=>basename($p),'size'=>round(filesize($p)/1024,1).'KB'];
            }
            echo json_encode(['ok'=>true,'files'=>$files]);
            exit;

        // ── Save campaign settings
        case 'save_campaign':
            $cfg = [
                'body'       => $_POST['body']       ?? '',
                'subject'    => $_POST['subject']    ?? '',
                'from_name'  => $_POST['from_name']  ?? '',
                'from_email' => $_POST['from_email'] ?? '',
                'signature'  => $_POST['signature']  ?? '',
            ];
            file_put_contents(CAMPAIGN_FILE, json_encode($cfg));
            echo json_encode(['ok'=>true]);
            exit;

        // ── Send one batch of 20
        case 'send_batch':
            if (!file_exists(STATE_FILE) || !file_exists(CAMPAIGN_FILE)) {
                echo json_encode(['ok'=>false,'msg'=>'Missing state/config']); exit;
            }
            $state = json_decode(file_get_contents(STATE_FILE), true);
            $cfg   = json_decode(file_get_contents(CAMPAIGN_FILE), true);

            $rows   = $state['rows'];
            $sent   = (int)($state['sent']   ?? 0);
            $failed = (int)($state['failed'] ?? 0);
            $total  = (int)($state['total']  ?? 0);

            // ── Create DB campaign row on very first batch ──
            if (empty($state['campaign_db_id'])) {
                $db = getDB();
                if ($db) {
                    $attachNames = implode(', ', array_map('basename', glob(ATTACH_DIR . '*') ?: []));
                    $stmt = $db->prepare(
                        "INSERT INTO bm_campaigns
                         (excel_file, subject, from_name, from_email,
                          total_raw, total_clean, junk_removed, attachments,
                          status, started_at)
                         VALUES (?,?,?,?,?,?,?,?,'running',NOW())"
                    );
                    if ($stmt) {
                        $junk = (int)($state['junk_count'] ?? 0);
                        $stmt->bind_param('ssssiiss',
                            $state['file'], $cfg['subject'], $cfg['from_name'], $cfg['from_email'],
                            $state['total_raw'], $total, $junk, $attachNames
                        );
                        $stmt->execute();
                        $state['campaign_db_id'] = (int)$db->insert_id;
                        $stmt->close();
                    }
                }
            }
            $campaignDbId = (int)($state['campaign_db_id'] ?? 0);

            if ($sent + $failed >= $total) {
                $state['processing'] = false;
                // Mark complete in DB
                $db = getDB();
                if ($db && $campaignDbId) {
                    $db->query("UPDATE bm_campaigns SET
                        status='completed', completed_at=NOW(),
                        sent={$state['sent']}, failed={$state['failed']}, skipped={$state['skipped']}
                        WHERE id=$campaignDbId");
                }
                file_put_contents(STATE_FILE, json_encode($state));
                echo json_encode(['ok'=>true,'done'=>true,'sent'=>$sent,'failed'=>$failed,'total'=>$total]);
                exit;
            }

            if (!$state['started_at']) $state['started_at'] = date('Y-m-d H:i:s');
            $state['processing'] = true;

            $attach_files = glob(ATTACH_DIR . '*') ?: [];
            $batchNo      = (int)($state['batch_done'] ?? 0) + 1;
            $batch        = array_slice($rows, $sent + $failed, BATCH_SIZE);
            $batch_sent   = 0;
            $batch_fail   = 0;
            $log_entries  = [];

            foreach ($batch as $row) {
                $to    = trim(preg_replace('/\s+/', '', (string)($row['email'] ?? '')));
                $fname = trim($row['first_name'] ?? '');
                $lname = trim($row['last_name']  ?? '');

                if (!isCleanEmail($to)) {
                    $hint = domainIssueHint($to) ?: 'Junk/test domain';
                    $batch_fail++;
                    $log_entries[] = [
                        'email' => $to, 'name' => "$fname $lname",
                        'status' => 'skipped', 'reason' => $hint,
                        'time'  => date('H:i:s'), 'domain_issue' => true,
                    ];
                    continue;
                }

                if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
                    $batch_fail++;
                    $log_entries[] = [
                        'email' => $to, 'name' => "$fname $lname",
                        'status' => 'failed', 'reason' => 'Invalid email format',
                        'time'  => date('H:i:s'),
                    ];
                    continue;
                }

                $body_html = str_replace(
                    ['{{first_name}}','{{last_name}}','{{email}}'],
                    [htmlspecialchars($fname), htmlspecialchars($lname), htmlspecialchars($to)],
                    $cfg['body']
                );
                $body_html = str_replace('<p>', '<p style="margin:0;">', $body_html);
                $sig_html  = $cfg['signature'];

                $result = sendMailWithAttachments(
                    $to, "$fname $lname",
                    $cfg['from_email'], $cfg['from_name'],
                    $cfg['subject'],
                    $body_html, $sig_html,
                    $attach_files
                );

                if ($result) {
                    $batch_sent++;
                    $log_entries[] = ['email'=>$to,'name'=>"$fname $lname",'status'=>'sent','time'=>date('H:i:s')];
                } else {
                    $batch_fail++;
                    $log_entries[] = ['email'=>$to,'name'=>"$fname $lname",'status'=>'failed','reason'=>'mail() failed','time'=>date('H:i:s')];
                }
            }

            $batchSkipped        = count(array_filter($log_entries, fn($e) => $e['status'] === 'skipped'));
            $state['sent']      += $batch_sent;
            $state['failed']    += $batch_fail;
            $state['skipped']    = (int)($state['skipped'] ?? 0) + $batchSkipped;
            $state['batch_done']+= 1;
            $state['log']        = array_merge($state['log'], $log_entries);

            $isDone = ($state['sent'] + $state['failed'] >= $total);
            if ($isDone) $state['processing'] = false;

            // ── Save log rows to DB ──
            $db = getDB();
            if ($db && $campaignDbId && !empty($log_entries)) {
                $stmt = $db->prepare(
                    "INSERT INTO bm_email_log
                     (campaign_id, email, first_name, last_name, status, reason, domain_issue, batch_no)
                     VALUES (?,?,?,?,?,?,?,?)"
                );
                if ($stmt) {
                    foreach ($log_entries as $e) {
                        $parts    = explode(' ', $e['name'] ?? '', 2);
                        $fn       = $parts[0] ?? '';
                        $ln       = $parts[1] ?? '';
                        $st       = $e['status'];
                        $rs       = $e['reason'] ?? null;
                        $di       = (int)($e['domain_issue'] ?? 0);
                        $stmt->bind_param('issssiii',
                            $campaignDbId, $e['email'], $fn, $ln, $st, $rs, $di, $batchNo
                        );
                        $stmt->execute();
                    }
                    $stmt->close();
                }
                // Update running totals on campaign row
                $db->query("UPDATE bm_campaigns SET
                    sent={$state['sent']}, failed={$state['failed']}, skipped={$state['skipped']},
                    status='" . ($isDone ? 'completed' : 'running') . "'" .
                    ($isDone ? ", completed_at=NOW()" : "") .
                    " WHERE id=$campaignDbId");
            }

            file_put_contents(STATE_FILE, json_encode($state));

            echo json_encode([
                'ok'          => true,
                'done'        => $isDone,
                'sent'        => $state['sent'],
                'failed'      => $state['failed'],
                'skipped'     => $state['skipped'],
                'total'       => $total,
                'batch_sent'  => $batch_sent,
                'batch_fail'  => $batch_fail,
                'batch_done'  => $state['batch_done'],
                'log_entries' => $log_entries,
            ]);
            exit;

        case 'get_state':
            if (!file_exists(STATE_FILE)) { echo json_encode(['ok'=>false]); exit; }
            $s = json_decode(file_get_contents(STATE_FILE), true);
            unset($s['rows']);
            echo json_encode(['ok'=>true,'state'=>$s]);
            exit;

        case 'reset':
            if (file_exists(STATE_FILE)) {
                $state = json_decode(file_get_contents(STATE_FILE), true);
                $id = (int)($state['campaign_db_id'] ?? 0);
                if ($id) {
                    $db = getDB();
                    if ($db) $db->query("UPDATE bm_campaigns SET status='stopped', completed_at=NOW() WHERE id=$id AND status='running'");
                }
                unlink(STATE_FILE);
            }
            foreach (glob(ATTACH_DIR . '*') as $p) unlink($p);
            echo json_encode(['ok'=>true]);
            exit;

        // ── DB test
        case 'test_db':
            $db = getDB();
            if (!$db) { echo json_encode(['ok'=>false,'msg'=>'Cannot connect to database. Check DB credentials.']); exit; }
            $r = $db->query("SELECT COUNT(*) AS c FROM bm_campaigns");
            $c = $r ? $r->fetch_assoc()['c'] : 0;
            echo json_encode(['ok'=>true,'msg'=>"DB connected ✓  ($c campaign(s) saved so far)"]);
            exit;
    }
    exit;
}

// ─── SEND WITH MIME MULTIPART + ATTACHMENTS ───────────────────────────────────
function sendMailWithAttachments($to, $to_name, $from_email, $from_name, $subject, $body_html, $signature_html, $attachments = []) {

    $boundary = '=_Part_' . md5(uniqid());

    $full_html = <<<HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body, p, div { margin: 0; padding: 0; }
p { margin: 0 0 10px 0; }
br { line-height: 1.4; }
body { font-family: Arial, sans-serif; line-height: 1.4; color: #1a1a1a; }
</style>
</head>
<body>
{$body_html}
<br><br>
{$signature_html}
</body>
</html>
HTML;

    $subject  = "=?UTF-8?B?" . base64_encode($subject) . "?=";
    $headers  = "From: =?UTF-8?B?" . base64_encode($from_name) . "?= <$from_email>\r\n";
    $headers .= "Reply-To: $from_email\r\n";
    $headers .= "MIME-Version: 1.0\r\n";
    $headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\r\n";

    $body  = "--$boundary\r\n";
    $body .= "Content-Type: text/html; charset=UTF-8\r\n";
    $body .= "Content-Transfer-Encoding: base64\r\n\r\n";
    $body .= chunk_split(base64_encode($full_html), 76, "\r\n");
    $body .= "\r\n";

    foreach ($attachments as $path) {
        if (!file_exists($path)) continue;
        $fname = basename($path);
        $data  = chunk_split(base64_encode(file_get_contents($path)), 76, "\r\n");
        $body .= "--$boundary\r\n";
        $body .= "Content-Type: application/octet-stream; name=\"$fname\"\r\n";
        $body .= "Content-Transfer-Encoding: base64\r\n";
        $body .= "Content-Disposition: attachment; filename=\"$fname\"\r\n\r\n";
        $body .= $data . "\r\n";
    }
    $body .= "--$boundary--\r\n";

    return mail($to, $subject, $body, $headers);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>BulkMailer 2.0</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<script src="https://cdn.ckeditor.com/ckeditor5/41.4.2/classic/ckeditor.js"></script>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
  --bg:#f5f7fb;--surf:#ffffff;--surf2:#f0f3f9;--bd:#e3e8f0;--bd2:#c8d0e0;
  --txt:#0d1117;--txt2:#3d4a5c;--txt3:#8794a8;
  --accent:#2563eb;--accent-h:#1d4ed8;--accent-lt:#eff6ff;--accent-bd:#bfdbfe;
  --ok:#059669;--ok-lt:#ecfdf5;--ok-bd:#a7f3d0;
  --err:#dc2626;--err-lt:#fef2f2;--err-bd:#fca5a5;
  --warn:#d97706;--warn-lt:#fffbeb;--warn-bd:#fcd34d;
  --skip:#7c3aed;--skip-lt:#f5f3ff;--skip-bd:#ddd6fe;
  --shadow:0 1px 3px rgba(15,20,35,.06),0 2px 8px rgba(15,20,35,.04);
  --shadow-md:0 4px 16px rgba(15,20,35,.08);
  --r:10px;--r-lg:14px;--font:'Inter',system-ui,sans-serif;--mono:'JetBrains Mono',monospace;
}
html{font-size:14px;scroll-behavior:smooth}
body{font-family:var(--font);background:var(--bg);color:var(--txt);min-height:100vh}
.app{display:flex;min-height:100vh}
.sb{width:220px;min-height:100vh;background:var(--surf);border-right:1px solid var(--bd);display:flex;flex-direction:column;position:fixed;top:0;left:0;z-index:50}
.sb-logo{padding:18px 16px 14px;border-bottom:1px solid var(--bd)}
.logo-r{display:flex;align-items:center;gap:10px}
.logo-ico{width:36px;height:36px;border-radius:10px;background:linear-gradient(135deg,#2563eb,#7c3aed);display:flex;align-items:center;justify-content:center;font-size:18px;color:#fff;flex-shrink:0}
.logo-name{font-size:15px;font-weight:600;color:var(--txt);letter-spacing:-.3px}
.logo-ver{font-size:11px;color:var(--txt3)}
.sb-nav{padding:10px 10px;flex:1}
.nav-sec{font-size:10px;font-weight:600;color:var(--txt3);letter-spacing:.8px;text-transform:uppercase;padding:8px 8px 3px}
.nav-i{display:flex;align-items:center;gap:9px;padding:8px 10px;border-radius:var(--r);cursor:pointer;color:var(--txt2);font-size:13px;font-weight:500;transition:all .12s;margin-bottom:1px}
.nav-i:hover{background:var(--surf2);color:var(--txt)}
.nav-i.on{background:var(--accent-lt);color:var(--accent)}
.nav-i .ico{font-size:15px;width:18px;text-align:center}
.main{margin-left:220px;flex:1;padding:28px 32px}
.topbar{display:flex;align-items:flex-start;justify-content:space-between;margin-bottom:22px}
.pg-title{font-size:20px;font-weight:600;letter-spacing:-.4px}
.pg-sub{font-size:12.5px;color:var(--txt3);margin-top:2px}
.st-badge{display:flex;align-items:center;gap:6px;padding:5px 12px;border-radius:20px;font-size:12px;font-weight:500;border:1px solid var(--bd);background:var(--surf2);color:var(--txt3)}
.st-badge.sending{background:var(--warn-lt);color:var(--warn);border-color:var(--warn-bd)}
.st-badge.done{background:var(--ok-lt);color:var(--ok);border-color:var(--ok-bd)}
.dot{width:7px;height:7px;border-radius:50%;background:currentColor;animation:blink 1.2s infinite}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.2}}
.stats{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin-bottom:16px}
.sc{background:var(--surf);border:1px solid var(--bd);border-radius:var(--r);padding:14px 18px;box-shadow:var(--shadow)}
.sc-lbl{font-size:11px;font-weight:500;color:var(--txt3);text-transform:uppercase;letter-spacing:.5px;margin-bottom:5px}
.sc-val{font-size:26px;font-weight:600;font-family:var(--mono);letter-spacing:-1px}
.sc-val.tot{color:var(--txt)}.sc-val.ok{color:var(--ok)}.sc-val.err{color:var(--err)}.sc-val.bl{color:var(--accent)}.sc-val.skip{color:var(--skip)}
.sc-sub{font-size:11px;color:var(--txt3);margin-top:3px}
.prog-card{background:var(--surf);border:1px solid var(--bd);border-radius:var(--r);padding:14px 20px;margin-bottom:16px;box-shadow:var(--shadow)}
.prog-hd{display:flex;justify-content:space-between;font-size:12px;color:var(--txt3);margin-bottom:8px}
.prog-bar{height:9px;background:var(--surf2);border-radius:20px;overflow:hidden;border:1px solid var(--bd)}
.prog-fill{height:100%;background:linear-gradient(90deg,var(--accent),#7c3aed);border-radius:20px;transition:width .5s ease;width:0%}
.card{background:var(--surf);border:1px solid var(--bd);border-radius:var(--r-lg);box-shadow:var(--shadow);margin-bottom:18px;overflow:hidden}
.card-hd{padding:14px 20px;border-bottom:1px solid var(--bd);display:flex;align-items:center;justify-content:space-between}
.card-title{font-size:13.5px;font-weight:600;display:flex;align-items:center;gap:8px}
.card-body{padding:18px 20px}
.uzone{border:2px dashed var(--bd2);border-radius:var(--r);padding:28px;text-align:center;cursor:pointer;transition:all .2s;background:var(--surf2)}
.uzone:hover,.uzone.drag{border-color:var(--accent);background:var(--accent-lt)}
.uzone-ico{font-size:32px;display:block;margin-bottom:8px}
.uzone p{font-size:13px;color:var(--txt2)}
.uzone strong{color:var(--accent)}
#finput{display:none}
.attach-zone{border:2px dashed var(--bd2);border-radius:var(--r);padding:20px;text-align:center;cursor:pointer;transition:all .2s;background:var(--surf2);margin-bottom:12px}
.attach-zone:hover,.attach-zone.drag{border-color:var(--accent);background:var(--accent-lt)}
#ainput{display:none}
.attach-list{display:flex;flex-direction:column;gap:6px}
.attach-item{display:flex;align-items:center;gap:10px;padding:8px 12px;background:var(--surf2);border:1px solid var(--bd);border-radius:var(--r);font-size:12.5px}
.attach-item .fname{flex:1;font-family:var(--mono);font-size:12px;color:var(--txt)}
.attach-item .fsize{color:var(--txt3);font-size:11px}
.attach-item .fdel{cursor:pointer;color:var(--err);font-size:16px;line-height:1;padding:0 2px}
.attach-item .fdel:hover{opacity:.7}
.fgrid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px}
.fr{margin-bottom:14px}
label{display:block;font-size:12px;font-weight:500;color:var(--txt2);margin-bottom:4px}
input[type=text],input[type=email],input[type=number]{width:100%;padding:8px 11px;border:1.5px solid var(--bd);border-radius:var(--r);font-family:var(--font);font-size:13px;color:var(--txt);background:var(--surf);outline:none;transition:border-color .15s}
input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(37,99,235,.07)}
.hint{font-size:11px;color:var(--txt3);margin-top:3px}
.ck-editor__editable{min-height:180px !important;font-family:var(--font) !important;font-size:14px !important}
.ck.ck-editor{border-radius:var(--r) !important}
.ck.ck-toolbar{border-radius:var(--r) var(--r) 0 0 !important;background:var(--surf2) !important;border-color:var(--bd) !important}
.ck.ck-editor__main>.ck-editor__editable{border-color:var(--bd) !important;border-radius:0 0 var(--r) var(--r) !important}
.ck.ck-editor__main>.ck-editor__editable:focus{border-color:var(--accent) !important;box-shadow:0 0 0 3px rgba(37,99,235,.07) !important}
#sig-ck .ck-editor__editable{min-height:90px !important}
.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:var(--r);font-family:var(--font);font-size:13px;font-weight:500;cursor:pointer;border:1px solid var(--bd);background:var(--surf);color:var(--txt);transition:all .12s}
.btn:hover{background:var(--surf2)}.btn:active{transform:scale(.98)}.btn:disabled{opacity:.45;cursor:not-allowed;transform:none}
.btn.prim{background:var(--accent);color:#fff;border-color:var(--accent)}.btn.prim:hover{background:var(--accent-h)}
.btn.ok{background:var(--ok);color:#fff;border-color:var(--ok)}.btn.ok:hover{background:#047857}
.btn.danger{background:var(--err);color:#fff;border-color:var(--err)}.btn.danger:hover{background:#b91c1c}
.btn.lg{padding:10px 22px;font-size:14px}
.badge{display:inline-block;padding:2px 8px;border-radius:10px;font-size:10.5px;font-weight:500}
.badge.bl{background:var(--accent-lt);color:var(--accent);border:1px solid var(--accent-bd)}
.badge.gr{background:var(--ok-lt);color:var(--ok);border:1px solid var(--ok-bd)}
.pill{display:flex;align-items:center;gap:8px;padding:9px 13px;border-radius:var(--r);font-size:12.5px}
.pill.ok{background:var(--ok-lt);border:1px solid var(--ok-bd);color:var(--ok)}
.pill.warn{background:var(--warn-lt);border:1px solid var(--warn-bd);color:var(--warn)}
.junk-banner{background:var(--skip-lt);border:1px solid var(--skip-bd);border-radius:var(--r);padding:10px 14px;margin-top:10px;font-size:12.5px;color:var(--skip);display:none;}
.junk-banner strong{font-weight:600}
/* Timer row */
.timer-row{display:flex;align-items:center;gap:12px;margin-bottom:14px;flex-wrap:wrap}
.timer-row label{margin:0;white-space:nowrap}
.timer-row input[type=number]{width:80px}
.timer-info{font-size:12px;color:var(--txt3);background:var(--surf2);border:1px solid var(--bd);border-radius:var(--r);padding:6px 12px}
.log-wrap{max-height:300px;overflow-y:auto}
table{width:100%;border-collapse:collapse;font-size:12px}
thead{background:var(--surf2);position:sticky;top:0}
th{padding:8px 12px;text-align:left;font-weight:500;color:var(--txt3);font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;border-bottom:1px solid var(--bd)}
td{padding:7px 12px;border-bottom:1px solid var(--bd);font-family:var(--mono);font-size:11.5px}
td.name-cell{font-family:var(--font);font-size:12px}
tr:last-child td{border-bottom:none}
tr:hover td{background:var(--surf2)}
.tag{display:inline-flex;align-items:center;gap:3px;padding:2px 8px;border-radius:10px;font-size:10.5px;font-weight:500}
.tag.ok{background:var(--ok-lt);color:var(--ok)}
.tag.err{background:var(--err-lt);color:var(--err)}
.tag.skip{background:var(--skip-lt);color:var(--skip)}
.div{height:1px;background:var(--bd);margin:14px 0}
.row{display:flex;align-items:center;gap:10px}.gap{flex:1}
.live{font-size:12.5px;color:var(--txt3);margin-top:12px;min-height:20px}
.bdots{display:flex;flex-wrap:wrap;gap:5px;margin-top:12px}
.bdot{width:13px;height:13px;border-radius:50%;transition:transform .15s;cursor:default}
.bdot:hover{transform:scale(1.5)}
/* countdown */
.countdown{display:none;align-items:center;gap:8px;margin-top:10px;font-size:12px;color:var(--warn)}
.countdown.on{display:flex}
.cd-bar-wrap{flex:1;height:6px;background:var(--surf2);border-radius:10px;overflow:hidden;border:1px solid var(--bd)}
.cd-bar{height:100%;background:var(--warn);border-radius:10px;transition:width .1s linear}
.panel{display:none}.panel.on{display:block}
.toasts{position:fixed;bottom:20px;right:20px;z-index:999;display:flex;flex-direction:column;gap:7px}
.toast{display:flex;align-items:center;gap:8px;padding:10px 16px;border-radius:var(--r);font-size:13px;font-weight:500;box-shadow:var(--shadow-md);animation:tin .2s ease;min-width:240px}
.toast.ok{background:var(--ok);color:#fff}.toast.err{background:var(--err);color:#fff}.toast.info{background:var(--accent);color:#fff}
@keyframes tin{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
.notice-box{background:linear-gradient(135deg,#fef9c3,#fff7ed);border:1px solid var(--warn-bd);border-radius:var(--r-lg);padding:18px 20px;margin-bottom:18px}
.notice-box h3{font-size:13.5px;font-weight:600;color:var(--warn);margin-bottom:10px;display:flex;align-items:center;gap:6px}
.notice-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.notice-item{background:#fff;border-radius:var(--r);padding:10px 12px;border:1px solid var(--warn-bd)}
.notice-item strong{display:block;font-size:12px;font-weight:600;color:var(--txt);margin-bottom:3px}
.notice-item p{font-size:11.5px;color:var(--txt2);line-height:1.5}
.notice-item.good{border-color:var(--ok-bd);background:var(--ok-lt)}.notice-item.good strong{color:var(--ok)}
.notice-item.bad{border-color:var(--err-bd);background:var(--err-lt)}.notice-item.bad strong{color:var(--err)}
</style>
</head>
<body>
<div class="app">

<aside class="sb">
  <div class="sb-logo">
    <div class="logo-r">
      <div class="logo-ico">✉</div>
      <div>
        <div class="logo-name">BulkMailer</div>
        <div class="logo-ver">v2.2 · DB-logging · 20/batch</div>
      </div>
    </div>
  </div>
  <nav class="sb-nav">
    <div class="nav-sec">Campaign</div>
    <div class="nav-i on" data-p="compose"><span class="ico">✏️</span> Compose</div>
    <div class="nav-i" data-p="send"><span class="ico">🚀</span> Send Emails</div>
    <div class="nav-i" data-p="logs"><span class="ico">📋</span> Delivery Log</div>
    <div class="nav-sec" style="margin-top:10px">More</div>
    <!--<div class="nav-i" data-p="impact"><span class="ico">⚠️</span> Server Impact</div>-->
    <div class="nav-i" id="nav-reset"><span class="ico">🔄</span> Reset All</div>
  <a href="campaigns.php">  <div class="nav-i" ><span class="ico"></span> Campaign Data</div></a>
  </nav>
</aside>

<main class="main">

  <div class="topbar">
    <div>
      <div class="pg-title" id="pg-title">Compose Campaign</div>
      <div class="pg-sub" id="pg-sub">Upload Excel · rich-text message · attachments · send in batches of 20</div>
    </div>
    <div class="st-badge" id="st-badge"><div class="dot"></div><span id="st-txt">Idle</span></div>
  </div>

  <div class="stats">
    <div class="sc"><div class="sc-lbl">Total</div><div class="sc-val tot" id="sv-tot">0</div><div class="sc-sub">recipients</div></div>
    <div class="sc"><div class="sc-lbl">Delivered</div><div class="sc-val ok" id="sv-sent">0</div><div class="sc-sub" id="sv-sp">—</div></div>
    <div class="sc"><div class="sc-lbl">Failed</div><div class="sc-val err" id="sv-fail">0</div><div class="sc-sub" id="sv-fp">—</div></div>
    <div class="sc"><div class="sc-lbl">Skipped</div><div class="sc-val skip" id="sv-skip">0</div><div class="sc-sub">bad domain</div></div>
    <div class="sc"><div class="sc-lbl">Batches</div><div class="sc-val bl" id="sv-bat">0</div><div class="sc-sub">× 20 per batch</div></div>
  </div>

  <div class="prog-card">
    <div class="prog-hd"><span>Campaign progress</span><span id="prog-lbl">0 / 0 processed</span></div>
    <div class="prog-bar"><div class="prog-fill" id="prog-fill"></div></div>
  </div>

  <!-- COMPOSE -->
  <div class="panel on" id="panel-compose">
    <div class="card">
      <div class="card-hd">
        <div class="card-title">📂 Upload Recipients Excel</div>
        <span class="badge bl">Step 1</span>
      </div>
      <div class="card-body">
        <div class="uzone" id="uzone" onclick="document.getElementById('finput').click()">
          <span class="uzone-ico">📊</span>
          <p><strong>Click to upload</strong> or drag & drop your .xlsx file</p>
          <p style="font-size:11.5px;color:var(--txt3);margin-top:3px">Required columns: first_name · last_name · email</p>
        </div>
        <input type="file" id="finput" accept=".xlsx,.xls">
        <div id="finfo" style="margin-top:10px"></div>
        <div class="junk-banner" id="junk-banner">
          ⚠️ <strong id="junk-count">0</strong> rows were removed before sending because their email domains are
          junk, test, or server hostnames (e.g. <code>.test-google-a.com</code>, bare IPs, <code>secureserver.net</code>).
          These will appear as <strong>Skipped — bad domain</strong> in the delivery log and CSV export.
        </div>
      </div>
    </div>

    <div class="card">
      <div class="card-hd">
        <div class="card-title">📎 Attachments</div>
        <span class="badge bl">Optional</span>
      </div>
      <div class="card-body">
        <div class="attach-zone" id="azone" onclick="document.getElementById('ainput').click()">
          <span style="font-size:24px;display:block;margin-bottom:6px">📎</span>
          <p style="font-size:13px;color:var(--txt2)"><strong>Click or drag files</strong> to attach</p>
          <p style="font-size:11.5px;color:var(--txt3);margin-top:2px">PDF, DOC, DOCX, XLS, XLSX, PPT, ZIP, JPG, PNG — max 10MB per file</p>
        </div>
        <input type="file" id="ainput" multiple>
        <div class="attach-list" id="attach-list" style="margin-top:10px"></div>
      </div>
    </div>

    <div class="card">
      <div class="card-hd">
        <div class="card-title">✏️ Compose Email</div>
        <span class="badge bl">Step 2</span>
      </div>
      <div class="card-body">
        <div class="fgrid">
          <div><label>From Name</label><input type="text" id="from_name" placeholder="Your Name or Company"></div>
          <div><label>From Email</label><input type="email" id="from_email" placeholder="you@yourdomain.com"></div>
        </div>
        <div class="fr"><label>Subject Line</label><input type="text" id="subject" placeholder="Email subject…"></div>
        <div class="fr">
          <label>Message Body <span style="font-size:10.5px;color:var(--txt3);font-weight:400">(rich text — use {{first_name}} {{last_name}} {{email}})</span></label>
          <div id="body-ck"></div>
          <div class="hint">Personalisation: <code>{{first_name}}</code> <code>{{last_name}}</code> <code>{{email}}</code> are auto-replaced per recipient</div>
        </div>
        <div class="fr">
          <label>Signature</label>
          <div id="sig-ck"></div>
        </div>
        <button class="btn ok" onclick="saveCampaign()" style="margin-top:4px">💾 Save Campaign Settings</button>
      </div>
    </div>
  </div>

  <!-- SEND -->
  <div class="panel" id="panel-send">
    <div class="card">
      <div class="card-hd"><div class="card-title">🚀 Launch Campaign</div></div>
      <div class="card-body">
        <div id="not-ready" class="pill warn">⚠️ Please upload an Excel file and save campaign settings before sending.</div>
        <div id="is-ready" class="pill ok" style="display:none">
          ✅ <strong id="rc-count">0</strong> recipients ready · <strong id="rc-batches">0</strong> batches of 20 · <span id="rc-attach">no attachments</span>
        </div>
        <div class="div"></div>

        <!-- ── Timer setting ── -->
        <div class="timer-row">
          <label>⏱ Delay between batches:</label>
          <input type="number" id="batch-delay" value="60" min="5" max="3600" style="width:80px">
          <span style="font-size:13px;color:var(--txt2)">seconds</span>
          <span class="timer-info" id="timer-info">20 emails then wait 60s, repeat</span>
        </div>

        <div class="row">
          <button class="btn ok lg" id="btn-start" onclick="startSend()" disabled>▶ Start Sending</button>
          <button class="btn" id="btn-pause" onclick="togglePause()" style="display:none">⏸ Pause</button>
          <button class="btn danger" id="btn-stop" onclick="stopSend()" style="display:none">⏹ Stop</button>
        </div>

        <!-- countdown bar shown between batches -->
        <div class="countdown" id="countdown">
          <span id="cd-text">Next batch in 60s</span>
          <div class="cd-bar-wrap"><div class="cd-bar" id="cd-bar" style="width:100%"></div></div>
        </div>

        <div class="live" id="live-status">Waiting to start…</div>
        <div class="bdots" id="bdots"></div>
      </div>
    </div>
  </div>

  <!-- LOGS -->
  <div class="panel" id="panel-logs">
    <div class="card">
      <div class="card-hd">
        <div class="card-title">📋 Delivery Log</div>
        <div class="row" style="gap:8px">
          <span class="badge gr" id="log-count">0 entries</span>
          <button class="btn" style="padding:5px 10px;font-size:12px" onclick="exportCSV()">⬇ Export CSV</button>
        </div>
      </div>
      <div class="card-body" style="padding:0">
        <div class="log-wrap">
          <table>
            <thead><tr><th>#</th><th>Name</th><th>Email</th><th>Status</th><th>Time</th><th>Note</th></tr></thead>
            <tbody id="log-body">
              <tr><td colspan="6" style="text-align:center;padding:24px;color:var(--txt3);font-family:var(--font)">No entries yet — start sending to see the log</td></tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
  </div>

  <!-- IMPACT -->
<!--  <div class="panel" id="panel-impact">-->
<!--    <div class="notice-box">-->
<!--      <h3>⚠️ Will bulk mailing affect your domain or server?</h3>-->
<!--      <div class="notice-grid">-->
<!--        <div class="notice-item bad"><strong>🔴 Domain Reputation (SPF/DKIM)</strong><p>Sending 5,000+ emails from a shared server without proper SPF, DKIM, and DMARC records will get your domain flagged as spam. Gmail, Outlook, and Yahoo all check these. Setup is mandatory.</p></div>-->
<!--        <div class="notice-item bad"><strong>🔴 Shared Hosting — High Risk</strong><p>Most shared hosts (GoDaddy, Bluehost, Hostinger) limit mail() to 200–500 emails/hour. Exceeding this gets your account suspended. Use an SMTP relay instead of PHP mail().</p></div>-->
<!--        <div class="notice-item bad"><strong>🔴 IP Blacklisting</strong><p>A shared server IP is shared with hundreds of sites. If anyone else on that IP sends spam, your emails are already blocked. Check your IP at MXToolbox. Use a dedicated IP or relay.</p></div>-->
<!--        <div class="notice-item bad"><strong>🔴 Bounce Handling</strong><p>Without bounce management, hard bounces accumulate and ISPs penalise your domain permanently. You need a system to unsubscribe invalid addresses.</p></div>-->
<!--        <div class="notice-item good"><strong>✅ Safe with SMTP Relay</strong><p>Replace PHP mail() with SendGrid, Mailgun, Amazon SES, or Brevo. They handle delivery, bounces, unsubscribes, and reputation. Free tiers cover 100–300 emails/day.</p></div>-->
<!--        <div class="notice-item good"><strong>✅ Batching + Timer (This App)</strong><p>Sends 20 emails per batch with a configurable delay between batches, which prevents server overload and looks more natural to spam filters.</p></div>-->
<!--        <div class="notice-item good"><strong>✅ Domain Filtering (v2.2)</strong><p>BulkMailer auto-removes test domains (.test-google-a.com), bare IPs, secureserver.net host literals, and other junk addresses before sending. These are logged as "Skipped – bad domain" in the DB.</p></div>-->
<!--        <div class="notice-item good"><strong>✅ DB Logging (v2.2 New)</strong><p>Every campaign run and every email attempt is now saved to your MySQL database (ubsforums_campaign). Tables: bm_campaigns and bm_email_log — auto-created on first run.</p></div>-->
<!--      </div>-->
<!--    </div>-->
<!--    <div class="card">-->
<!--      <div class="card-hd"><div class="card-title">📋 Quick SMTP Integration Guide</div></div>-->
<!--      <div class="card-body">-->
<!--        <p style="font-size:13px;color:var(--txt2);margin-bottom:10px">To use an SMTP relay instead of PHP mail(), install PHPMailer via Composer and replace the <code>sendMailWithAttachments()</code> function:</p>-->
<!--        <pre style="background:var(--surf2);border:1px solid var(--bd);border-radius:var(--r);padding:14px;font-family:var(--mono);font-size:11.5px;overflow-x:auto;line-height:1.7">// composer require phpmailer/phpmailer-->
<!--use PHPMailer\PHPMailer\PHPMailer;-->
<!--use PHPMailer\PHPMailer\SMTP;-->

<!--$mail = new PHPMailer(true);-->
<!--$mail->isSMTP();-->
<!--$mail->Host       = 'smtp.sendgrid.net';-->
<!--$mail->SMTPAuth   = true;-->
<!--$mail->Username   = 'apikey';-->
<!--$mail->Password   = 'SG.xxxxxx';-->
<!--$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;-->
<!--$mail->Port       = 587;-->
<!--$mail->setFrom($from_email, $from_name);-->
<!--$mail->addAddress($to, $to_name);-->
<!--$mail->isHTML(true);-->
<!--$mail->Subject = $subject;-->
<!--$mail->Body    = $body_html;-->
<!--foreach ($attachments as $path) $mail->addAttachment($path);-->
<!--$mail->send();</pre>-->
<!--      </div>-->
<!--    </div>-->
<!--  </div>-->

</main>
</div>

<div class="toasts" id="toasts"></div>

<script>
const S = { total:0, sent:0, failed:0, skipped:0, batches:0, running:false, paused:false, excelOk:false, campaignOk:false, log:[] };
let sid = null, cdInterval = null, logN = 0;
let bodyEditor = null, sigEditor = null;
let attachments = [];

// Update timer-info label whenever delay input changes
document.getElementById('batch-delay').addEventListener('input', function() {
  const v = parseInt(this.value) || 60;
  document.getElementById('timer-info').textContent = `20 emails then wait ${v}s, repeat`;
});

ClassicEditor.create(document.getElementById('body-ck'), {
  toolbar: ['heading','|','bold','italic','underline','|','fontColor','fontBackgroundColor','|','link','bulletedList','numberedList','|','blockQuote','insertTable','|','undo','redo'],
  placeholder: 'Write your email body here… Use {{first_name}}, {{last_name}}, {{email}} for personalisation.'
}).then(e => { bodyEditor = e; }).catch(console.error);

ClassicEditor.create(document.getElementById('sig-ck'), {
  toolbar: ['bold','italic','|','link','|','undo','redo'],
  placeholder: 'Your signature here…'
}).then(e => { sigEditor = e; }).catch(console.error);

const panels = {
  compose: ['Compose Campaign',      'Upload Excel · rich text message · attachments · send in batches of 20'],
  send:    ['Send Campaign',          'Launch and monitor your email campaign in real time'],
  logs:    ['Delivery Log',           'Real-time per-email delivery tracking with domain issue flags'],
  impact:  ['Server & Domain Impact', 'What you need to know before sending bulk email']
};
document.querySelectorAll('.nav-i[data-p]').forEach(el => {
  el.addEventListener('click', () => {
    document.querySelectorAll('.nav-i').forEach(n => n.classList.remove('on'));
    document.querySelectorAll('.panel').forEach(p => p.classList.remove('on'));
    el.classList.add('on');
    document.getElementById('panel-' + el.dataset.p)?.classList.add('on');
    const [t, s] = panels[el.dataset.p] || ['',''];
    document.getElementById('pg-title').textContent = t;
    document.getElementById('pg-sub').textContent   = s;
  });
});

document.getElementById('nav-reset').addEventListener('click', async () => {
  if (S.running && !confirm('Stop campaign and reset everything?')) return;
  clearTimeout(sid); clearInterval(cdInterval);
  await fetch('?action=reset', { method:'POST' });
  Object.assign(S, {total:0,sent:0,failed:0,skipped:0,batches:0,running:false,paused:false,excelOk:false,campaignOk:false,log:[]});
  logN = 0; attachments = [];
  updateStats(); updateSendPanel(); renderAttachments();
  document.getElementById('finfo').innerHTML = '';
  document.getElementById('junk-banner').style.display = 'none';
  document.getElementById('log-body').innerHTML = '<tr><td colspan="6" style="text-align:center;padding:24px;color:var(--txt3);font-family:var(--font)">No entries yet</td></tr>';
  document.getElementById('log-count').textContent = '0 entries';
  document.getElementById('bdots').innerHTML = '';
  document.getElementById('btn-start').style.display = '';
  document.getElementById('btn-pause').style.display = 'none';
  document.getElementById('btn-stop').style.display = 'none';
  document.getElementById('countdown').classList.remove('on');
  setStatus('', 'Idle'); setLive('Waiting to start…');
  toast('Campaign reset', 'info');
});

// ── EXCEL UPLOAD
const uzone = document.getElementById('uzone');
uzone.addEventListener('dragover', e => { e.preventDefault(); uzone.classList.add('drag'); });
uzone.addEventListener('dragleave', () => uzone.classList.remove('drag'));
uzone.addEventListener('drop', e => { e.preventDefault(); uzone.classList.remove('drag'); if(e.dataTransfer.files[0]) uploadExcel(e.dataTransfer.files[0]); });
document.getElementById('finput').addEventListener('change', e => { if(e.target.files[0]) uploadExcel(e.target.files[0]); });

async function uploadExcel(file) {
  const fd = new FormData(); fd.append('excel', file);
  toast('Parsing Excel…', 'info');
  const res = await fetch('?action=upload_excel', { method:'POST', body:fd });
  const d = await res.json();
  if (d.ok) {
    S.total = d.total; S.excelOk = true;
    S.skipped = d.junk_count || 0;
    updateStats(); updateSendPanel();
    document.getElementById('finfo').innerHTML =
      `<div class="pill ok">✅ <strong>${file.name}</strong> — <strong>${d.total}</strong> clean recipients loaded</div>`;
    if (d.junk_count > 0) {
      document.getElementById('junk-count').textContent = d.junk_count;
      document.getElementById('junk-banner').style.display = 'flex';
    } else {
      document.getElementById('junk-banner').style.display = 'none';
    }
    toast(`${d.total} recipients loaded${d.junk_count > 0 ? ` (${d.junk_count} junk domains removed)` : ''}`, 'ok');
  } else { toast(d.msg || 'Upload failed', 'err'); }
}

// ── ATTACHMENTS
const azone = document.getElementById('azone');
azone.addEventListener('dragover', e => { e.preventDefault(); azone.classList.add('drag'); });
azone.addEventListener('dragleave', () => azone.classList.remove('drag'));
azone.addEventListener('drop', e => { e.preventDefault(); azone.classList.remove('drag'); [...e.dataTransfer.files].forEach(uploadAttach); });
document.getElementById('ainput').addEventListener('change', e => { [...e.target.files].forEach(uploadAttach); e.target.value=''; });

async function uploadAttach(file) {
  if (file.size > 10 * 1024 * 1024) { toast(`${file.name} exceeds 10MB limit`, 'err'); return; }
  const fd = new FormData(); fd.append('attachment', file);
  toast(`Uploading ${file.name}…`, 'info');
  const res = await fetch('?action=upload_attachment', { method:'POST', body:fd });
  const d = await res.json();
  if (d.ok) { attachments.push({ name:d.name, size:d.size, path:d.path }); renderAttachments(); updateSendPanel(); toast(`${d.name} attached`, 'ok'); }
  else { toast('Upload failed', 'err'); }
}
async function removeAttach(name) {
  const fd = new FormData(); fd.append('name', name);
  await fetch('?action=remove_attachment', { method:'POST', body:fd });
  attachments = attachments.filter(a => a.name !== name);
  renderAttachments(); updateSendPanel(); toast(`${name} removed`, 'info');
}
function renderAttachments() {
  const list = document.getElementById('attach-list');
  if (!attachments.length) { list.innerHTML = ''; return; }
  list.innerHTML = attachments.map(a => `
    <div class="attach-item">
      <span>📎</span><span class="fname">${a.name}</span><span class="fsize">${a.size}</span>
      <span class="fdel" onclick="removeAttach('${a.name}')" title="Remove">✕</span>
    </div>`).join('');
}

// ── SAVE CAMPAIGN
async function saveCampaign() {
  const body      = bodyEditor ? bodyEditor.getData() : '';
  const sig       = sigEditor  ? sigEditor.getData()  : '';
  const subject   = document.getElementById('subject').value.trim();
  const from_name = document.getElementById('from_name').value.trim();
  const from_email= document.getElementById('from_email').value.trim();
  if (!subject || !body || !from_email) { toast('Fill Subject, From Email & Body', 'err'); return; }
  const fd = new FormData();
  fd.append('body', body); fd.append('signature', sig);
  fd.append('subject', subject); fd.append('from_name', from_name); fd.append('from_email', from_email);
  const res = await fetch('?action=save_campaign', { method:'POST', body:fd });
  const d = await res.json();
  if (d.ok) { S.campaignOk = true; updateSendPanel(); toast('Campaign settings saved ✓', 'ok'); }
}

function updateSendPanel() {
  const ok = S.excelOk && S.campaignOk;
  document.getElementById('not-ready').style.display = ok ? 'none' : 'flex';
  document.getElementById('is-ready').style.display  = ok ? 'flex' : 'none';
  document.getElementById('btn-start').disabled      = !ok;
  if (ok) {
    document.getElementById('rc-count').textContent   = S.total.toLocaleString();
    document.getElementById('rc-batches').textContent = Math.ceil(S.total / 20);
    document.getElementById('rc-attach').textContent  = attachments.length
      ? `${attachments.length} attachment${attachments.length>1?'s':''}`
      : 'no attachments';
  }
}

// ── SENDING
function getBatchDelay() {
  return (parseInt(document.getElementById('batch-delay').value) || 60) * 1000;
}

function startSend() {
  if (S.running) return;
  S.running = true; S.paused = false;
  document.getElementById('btn-start').style.display = 'none';
  document.getElementById('btn-pause').style.display = '';
  document.getElementById('btn-stop').style.display  = '';
  setStatus('sending', '🟡 Sending…');
  runBatch();
}
function togglePause() {
  S.paused = !S.paused;
  document.getElementById('btn-pause').textContent = S.paused ? '▶ Resume' : '⏸ Pause';
  clearInterval(cdInterval);
  document.getElementById('countdown').classList.remove('on');
  if (!S.paused) runBatch();
  else setLive('⏸ Paused — click Resume to continue.');
}
function stopSend() {
  S.running = false; S.paused = false;
  clearTimeout(sid); clearInterval(cdInterval);
  document.getElementById('btn-start').style.display = '';
  document.getElementById('btn-pause').style.display = 'none';
  document.getElementById('btn-stop').style.display  = 'none';
  document.getElementById('countdown').classList.remove('on');
  setStatus('', 'Stopped'); setLive('Campaign stopped manually.');
}

async function runBatch() {
  if (!S.running || S.paused) return;
  if (S.sent + S.failed >= S.total) { finish(); return; }
  document.getElementById('countdown').classList.remove('on');
  clearInterval(cdInterval);
  setLive(`Sending batch ${S.batches + 1} of ${Math.ceil(S.total / 20)}…`);
  try {
    const res = await fetch('?action=send_batch');
    const d   = await res.json();
    if (!d.ok) { toast('Batch error: ' + (d.msg||''), 'err'); return; }
    S.sent    = d.sent;
    S.failed  = d.failed;
    S.skipped = d.skipped ?? S.skipped;
    S.batches = d.batch_done;
    if (d.log_entries) {
      S.log = S.log.concat(d.log_entries);
      appendLog(d.log_entries);
    }
    updateStats();
    addDot(d.batch_sent, d.batch_fail);
    if (d.done) { finish(); return; }

    // ── Countdown timer between batches ──
    const delaySec = Math.round(getBatchDelay() / 1000);
    setLive(`Batch ${d.batch_done} done — ${d.batch_sent} sent, ${d.batch_fail} failed/skipped. Next batch in ${delaySec}s…`);
    startCountdown(delaySec, () => {
      if (S.running && !S.paused) runBatch();
    });
  } catch(e) { toast('Network error', 'err'); S.running = false; }
}

function startCountdown(totalSec, cb) {
  const cdEl   = document.getElementById('countdown');
  const cdText = document.getElementById('cd-text');
  const cdBar  = document.getElementById('cd-bar');
  cdEl.classList.add('on');
  let remaining = totalSec;
  cdText.textContent = `Next batch in ${remaining}s`;
  cdBar.style.width  = '100%';
  clearInterval(cdInterval);
  cdInterval = setInterval(() => {
    if (!S.running || S.paused) { clearInterval(cdInterval); cdEl.classList.remove('on'); return; }
    remaining--;
    cdText.textContent = `Next batch in ${remaining}s`;
    cdBar.style.width  = Math.round((remaining / totalSec) * 100) + '%';
    if (remaining <= 0) {
      clearInterval(cdInterval);
      cdEl.classList.remove('on');
      cb();
    }
  }, 1000);
}

function finish() {
  S.running = false;
  clearInterval(cdInterval);
  document.getElementById('btn-start').style.display = '';
  document.getElementById('btn-pause').style.display = 'none';
  document.getElementById('btn-stop').style.display  = 'none';
  document.getElementById('countdown').classList.remove('on');
  setStatus('done', '✅ Complete');
  setLive(`🎉 Done! ${S.sent.toLocaleString()} delivered, ${S.failed} failed, ${S.skipped} skipped (bad domain).`);
  toast(`Campaign complete! ${S.sent}/${S.total} delivered`, 'ok');
}
function addDot(s, f) {
  const d = document.createElement('div');
  d.className = 'bdot';
  d.title = `Sent: ${s}  Failed/Skipped: ${f}`;
  d.style.background = f===0 ? 'var(--ok)' : s===0 ? 'var(--err)' : 'var(--warn)';
  document.getElementById('bdots').appendChild(d);
}

function updateStats() {
  document.getElementById('sv-tot').textContent  = S.total.toLocaleString();
  document.getElementById('sv-sent').textContent = S.sent.toLocaleString();
  document.getElementById('sv-fail').textContent = S.failed.toLocaleString();
  document.getElementById('sv-skip').textContent = S.skipped.toLocaleString();
  document.getElementById('sv-bat').textContent  = S.batches;
  const done = S.sent + S.failed;
  const pct  = S.total > 0 ? Math.round(done / S.total * 100) : 0;
  document.getElementById('prog-fill').style.width = pct + '%';
  document.getElementById('prog-lbl').textContent  = `${done.toLocaleString()} / ${S.total.toLocaleString()} processed`;
  document.getElementById('sv-sp').textContent = S.total > 0 ? Math.round(S.sent / S.total * 100) + '% rate' : '—';
  document.getElementById('sv-fp').textContent = S.total > 0 ? Math.round(S.failed / S.total * 100) + '% rate' : '—';
}

// ── LOG
function appendLog(entries) {
  const tb = document.getElementById('log-body');
  if (logN === 0) tb.innerHTML = '';
  entries.forEach(e => {
    logN++;
    const isSkip = e.status === 'skipped';
    const tagCls = e.status === 'sent' ? 'ok' : isSkip ? 'skip' : 'err';
    const tagTxt = e.status === 'sent' ? '✓ Sent' : isSkip ? '⊘ Skipped' : '✗ Failed';
    const tr = document.createElement('tr');
    if (isSkip) tr.style.background = 'var(--skip-lt)';
    tr.innerHTML = `
      <td>${logN}</td>
      <td class="name-cell">${e.name||'—'}</td>
      <td>${e.email}</td>
      <td><span class="tag ${tagCls}">${tagTxt}</span></td>
      <td>${e.time}</td>
      <td style="color:var(--txt3);font-family:var(--font)">${e.reason||''}</td>`;
    tb.appendChild(tr);
  });
  document.getElementById('log-count').textContent = logN.toLocaleString() + ' entries';
  const w = document.querySelector('.log-wrap'); w.scrollTop = w.scrollHeight;
}

// ── CSV EXPORT
function exportCSV() {
  if (!S.log.length) { toast('No log data yet', 'info'); return; }
  let csv = 'No,Name,Email,Status,Time,Note,DomainIssue\n';
  S.log.forEach((e,i) => {
    const domainFlag = e.domain_issue ? 'YES' : '';
    csv += `${i+1},"${(e.name||'').replace(/"/g,'""')}","${e.email}","${e.status}","${e.time}","${(e.reason||'').replace(/"/g,'""')}","${domainFlag}"\n`;
  });
  const a = document.createElement('a');
  a.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv);
  a.download = 'delivery_log_' + new Date().toISOString().slice(0,10) + '.csv';
  a.click();
  toast('CSV exported', 'ok');
}

function setStatus(cls, txt) {
  const b = document.getElementById('st-badge');
  b.className = 'st-badge' + (cls ? ' '+cls : '');
  document.getElementById('st-txt').textContent = txt;
}
function setLive(m) { document.getElementById('live-status').textContent = m; }
function toast(msg, type='info') {
  const w = document.getElementById('toasts');
  const t = document.createElement('div');
  t.className = `toast ${type}`;
  t.textContent = msg;
  w.appendChild(t);
  setTimeout(() => t.remove(), 3500);
}

updateStats(); updateSendPanel();
</script>
</body>
</html>