<?php
// ============================================================
//  Buy Local Lowveld — In-house email system (Phase 2f)
// ============================================================
//
//  Delivery: PHP mail() via cPanel's local MTA.
//  Storage: email_templates (editable), email_queue (pending),
//           email_log (history).
//
//  Public API:
//    email_enqueue($slug, $to_email, $to_name=null, $vars=[], $schedule_at=null)
//    email_send_now($to, $to_name, $subject, $html, $text, $from_email, $from_name)
//    email_render($template_string, $vars)
//    email_load_template($slug)
//    email_process_queue($limit=20)        — called by cron
//    email_send_test($slug, $to_email)     — admin "send test" button
//
// ============================================================

require_once __DIR__ . '/db.php';
require_once __DIR__ . '/settings.php';

/** Load a template by slug. Returns null if not found / disabled. */
function email_load_template(string $slug): ?array {
    $t = db_row('SELECT * FROM email_templates WHERE slug=:s', ['s' => $slug]);
    if (!$t) return null;
    if ((int)$t['enabled'] !== 1) return null;
    return $t;
}

/** Replace {{vars}} in a string. */
function email_render(string $template_string, array $vars): string {
    if ($template_string === '') return '';
    return preg_replace_callback(
        '/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/',
        function ($m) use ($vars) {
            $key = $m[1];
            return array_key_exists($key, $vars) ? (string)$vars[$key] : '';
        },
        $template_string
    );
}

/**
 * Add an email to the send queue.
 * Returns the queue row id, or null on failure.
 */
function email_enqueue(string $slug, string $to_email, ?string $to_name = null, array $vars = [], ?string $schedule_at = null): ?int {
    $to_email = strtolower(trim($to_email));
    if ($to_email === '' || !filter_var($to_email, FILTER_VALIDATE_EMAIL)) {
        app_log("email_enqueue: invalid recipient '$to_email' for $slug");
        return null;
    }

    $t = email_load_template($slug);
    if (!$t) {
        app_log("email_enqueue: template '$slug' not found or disabled (recipient: $to_email)");
        return null;
    }

    try {
        $id = db_insert('email_queue', [
            'template_slug' => $slug,
            'to_email'      => $to_email,
            'to_name'       => $to_name ?: null,
            'merge_vars'    => json_encode($vars, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'scheduled_at'  => $schedule_at ?: date('Y-m-d H:i:s'),
            'status'        => 'pending',
        ]);
        return (int)$id;
    } catch (Throwable $e) {
        app_log('email_enqueue insert failed: ' . $e->getMessage());
        return null;
    }
}

/**
 * Send an email immediately via PHP mail() with proper multipart.
 * Returns ['ok'=>bool, 'error'=>string|null].
 */
function email_send_now(
    string $to_email,
    ?string $to_name,
    string $subject,
    string $html_body,
    string $text_body,
    ?string $from_email = null,
    ?string $from_name = null,
    ?string $reply_to = null,
    ?string $bcc = null
): array {
    $from_email = $from_email ?: setting_get('email.from_email', 'noreply@buylocallowveld.co.za');
    $from_name  = $from_name  ?: setting_get('email.from_name',  'Buy Local Lowveld');
    $reply_to   = $reply_to   ?: setting_get('email.reply_to',   '');
    $bcc        = $bcc        ?: setting_get('email.bcc_admin',  '');

    // Build multipart MIME with HTML + plain text fallback
    $boundary = '_==BLW_' . bin2hex(random_bytes(8));

    // Headers
    $headers = [];
    $headers[] = 'From: ' . email_format_address($from_email, $from_name);
    if ($reply_to) {
        $headers[] = 'Reply-To: ' . email_format_address($reply_to);
    }
    if ($bcc) {
        $headers[] = 'Bcc: ' . email_format_address($bcc);
    }
    $headers[] = 'MIME-Version: 1.0';
    $headers[] = 'Content-Type: multipart/alternative; boundary="' . $boundary . '"';
    $headers[] = 'X-Mailer: BuyLocalLowveld/1.0';

    // Wrap HTML in a simple frame for better deliverability + readability
    $html_full = email_wrap_html($html_body);

    $body  = "--{$boundary}\r\n";
    $body .= "Content-Type: text/plain; charset=UTF-8\r\n";
    $body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
    $body .= ($text_body !== '' ? $text_body : strip_tags($html_body)) . "\r\n\r\n";

    $body .= "--{$boundary}\r\n";
    $body .= "Content-Type: text/html; charset=UTF-8\r\n";
    $body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
    $body .= $html_full . "\r\n\r\n";

    $body .= "--{$boundary}--\r\n";

    // Properly encode the subject if non-ASCII
    $subject_encoded = '=?UTF-8?B?' . base64_encode($subject) . '?=';

    $to_full = $to_name
        ? email_format_address($to_email, $to_name)
        : $to_email;

    $ok = @mail(
        $to_full,
        $subject_encoded,
        $body,
        implode("\r\n", $headers),
        '-f' . $from_email   // envelope sender (helps with SPF / bounce routing)
    );

    if (!$ok) {
        $err = error_get_last();
        return ['ok' => false, 'error' => $err['message'] ?? 'mail() returned false'];
    }
    return ['ok' => true, 'error' => null];
}

/** Format an "Name <email@x>" header value safely. */
function email_format_address(string $email, ?string $name = null): string {
    $email = trim($email);
    if (!$name) return $email;
    // Strip dangerous chars from the name
    $clean = trim(str_replace(["\r","\n","\"","<",">"], '', $name));
    if ($clean === '') return $email;
    // Encode if non-ASCII
    if (preg_match('/[^\x20-\x7E]/', $clean)) {
        $clean = '=?UTF-8?B?' . base64_encode($clean) . '?=';
        return $clean . ' <' . $email . '>';
    }
    return '"' . $clean . '" <' . $email . '>';
}

/**
 * Wrap a body fragment in a clean HTML email frame.
 * Templates can include their own <html>/<body> if they want, in which
 * case we leave them alone.
 */
function email_wrap_html(string $body_html): string {
    if (stripos($body_html, '<html') !== false || stripos($body_html, '<body') !== false) {
        return $body_html;
    }
    return '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8">
<style>
body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:#f6f6f4;color:#1a1a1a;margin:0;padding:24px;}
.email-frame{max-width:600px;margin:0 auto;background:#fff;border-radius:10px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,.06);}
.email-head{background:#0e0e0e;color:#fff;padding:24px;text-align:center;}
.email-head h1{margin:0;font-size:1.2rem;letter-spacing:.06em;font-weight:700;}
.email-body{padding:24px 28px;line-height:1.55;font-size:15px;}
.email-body h2{margin:0 0 .75rem;font-size:1.25rem;color:#1a1a1a;}
.email-body p{margin:0 0 1rem;}
.email-body a{color:#5a7a35;}
.email-body ul{padding-left:1.25rem;}
.email-foot{padding:18px 28px;font-size:.78rem;color:#888;text-align:center;background:#fafafa;}
</style></head><body>
<div class="email-frame">
  <div class="email-head"><h1>BUY LOCAL LOWVELD</h1></div>
  <div class="email-body">' . $body_html . '</div>
  <div class="email-foot">Buy Local Lowveld — buylocallowveld.co.za</div>
</div>
</body></html>';
}

/**
 * Process up to N queued emails. Returns ['attempted'=>int, 'sent'=>int, 'failed'=>int].
 * Called from cron/email-worker.php.
 */
function email_process_queue(int $limit = 20): array {
    $jobs = db_all(
        "SELECT * FROM email_queue
          WHERE status = 'pending'
            AND scheduled_at <= NOW()
            AND attempts < max_attempts
          ORDER BY scheduled_at ASC
          LIMIT $limit"
    );

    $sent = 0; $failed = 0;
    foreach ($jobs as $job) {
        // Mark as 'sending' to prevent duplicate processing
        db_exec("UPDATE email_queue SET status='sending', attempts = attempts + 1 WHERE id=:id",
                ['id' => $job['id']]);

        $template = email_load_template($job['template_slug']);
        if (!$template) {
            db_exec(
                "UPDATE email_queue SET status='failed', last_error=:err WHERE id=:id",
                ['err' => 'Template not found or disabled', 'id' => $job['id']]
            );
            email_log_record($job['id'], $job['template_slug'], $job['to_email'], '', false, 'Template not found');
            $failed++;
            continue;
        }

        $vars = $job['merge_vars'] ? json_decode($job['merge_vars'], true) : [];
        if (!is_array($vars)) $vars = [];

        // Always make site_url available
        if (!isset($vars['site_url']) && defined('SITE_URL')) {
            $vars['site_url'] = SITE_URL;
        }
        if (!isset($vars['email'])) $vars['email'] = $job['to_email'];

        $subject  = email_render($template['subject'],   $vars);
        $html     = email_render($template['html_body'], $vars);
        $text     = email_render($template['text_body'] ?? '', $vars);

        $result = email_send_now(
            $job['to_email'],
            $job['to_name'],
            $subject,
            $html,
            $text,
            $template['from_email'] ?: null,
            $template['from_name']  ?: null
        );

        if ($result['ok']) {
            db_exec(
                "UPDATE email_queue SET status='sent', sent_at=NOW(), last_error=NULL WHERE id=:id",
                ['id' => $job['id']]
            );
            email_log_record($job['id'], $job['template_slug'], $job['to_email'], $subject, true, null);
            $sent++;
        } else {
            // Decide: retry or final-fail?
            $attempts = (int)$job['attempts'] + 1;
            $is_final = $attempts >= (int)$job['max_attempts'];
            $next_status = $is_final ? 'failed' : 'pending';
            // Backoff: 5min, 15min, 60min
            $backoff = [5*60, 15*60, 60*60][min($attempts-1, 2)] ?? 60*60;
            $next_run = date('Y-m-d H:i:s', time() + $backoff);

            db_exec(
                "UPDATE email_queue
                    SET status=:s, last_error=:e, scheduled_at=:n
                  WHERE id=:id",
                ['s' => $next_status, 'e' => $result['error'], 'n' => $next_run, 'id' => $job['id']]
            );
            email_log_record($job['id'], $job['template_slug'], $job['to_email'], $subject, false, $result['error']);
            $failed++;
        }
    }

    return ['attempted' => count($jobs), 'sent' => $sent, 'failed' => $failed];
}

/** Internal — record send attempt to email_log. */
function email_log_record(?int $queue_id, string $slug, string $to_email, string $subject, bool $success, ?string $error): void {
    try {
        db_insert('email_log', [
            'queue_id'      => $queue_id,
            'template_slug' => $slug,
            'to_email'      => $to_email,
            'subject'       => mb_substr($subject, 0, 250),
            'success'       => $success ? 1 : 0,
            'error'         => $error ? mb_substr($error, 0, 1000) : null,
        ]);
    } catch (Throwable $e) {
        error_log('email_log insert failed: ' . $e->getMessage());
    }
}

/**
 * Send a test of the given template directly to an email — for the admin
 * "Send test" button. Renders the template with placeholder data.
 */
function email_send_test(string $slug, string $to_email): array {
    $t = db_row('SELECT * FROM email_templates WHERE slug=:s', ['s'=>$slug]);
    if (!$t) return ['ok'=>false, 'error'=>'Template not found'];

    // Build sample vars from listed merge_vars
    $vars = [
        'first_name'        => 'Sam',
        'last_name'         => 'Tester',
        'business_name'     => 'Test Coffee Co',
        'tier'              => 'Bronze',
        'amount'            => 'R 250.00',
        'new_amount'        => 'R 500.00',
        'old_tier'          => 'Bronze',
        'new_tier'          => 'Silver',
        'renewal_date'      => date('j F Y', strtotime('+30 days')),
        'next_charge_date'  => date('j F Y', strtotime('+30 days')),
        'end_date'          => date('j F Y', strtotime('+30 days')),
        'email'             => $to_email,
        'site_url'          => defined('SITE_URL') ? SITE_URL : 'https://buylocallowveld.co.za',
    ];

    $subject = '[TEST] ' . email_render($t['subject'],   $vars);
    $html    = email_render($t['html_body'], $vars);
    $text    = email_render($t['text_body'] ?? '', $vars);

    $r = email_send_now(
        $to_email, null, $subject, $html, $text,
        $t['from_email'] ?: null, $t['from_name'] ?: null
    );
    email_log_record(null, $slug, $to_email, $subject, $r['ok'], $r['error']);
    return $r;
}