<?php
declare(strict_types=1);

require_once __DIR__ . '/SmtpMailer.php';

/**
 * Batch mailer with file-based status tracking.
 *
 * Config is read from DATA_DIR/mail_config.json (never stored in the editor).
 * Status is written to DATA_DIR/mail_status.json after every sent mail so the
 * frontend can poll GET /guests/mail/status while the POST is still running.
 *
 * Template variables replaced per recipient:
 *   {{name}}            guest.name
 *   {{begrüssung}}      guest.greeting
 *   {{hochzeitsdatum}}  mail_config.wedding_date
 *   {{websitelink}}     site_url/?g=TOKEN
 *   {{rsvp_link}}       site_url/?g=TOKEN&lang=LANG
 */
class Mailer
{
    private string $dataDir;
    private string $statusFile;
    private string $configFile;
    private string $rootDir;

    public function __construct(string $dataDir, string $rootDir = '')
    {
        $this->dataDir    = rtrim($dataDir, '/\\');
        $this->statusFile = $this->dataDir . '/mail_status.json';
        $this->configFile = $this->dataDir . '/mail_config.json';
        $this->rootDir    = $rootDir ?: dirname($dataDir);
    }

    // ── Public API ────────────────────────────────────────────────────────────

    public function getStatus(): array
    {
        if (!file_exists($this->statusFile)) {
            return $this->emptyStatus();
        }
        $data = json_decode(file_get_contents($this->statusFile), true);
        return is_array($data) ? $data : $this->emptyStatus();
    }

    public function isRunning(): bool
    {
        return (bool)($this->getStatus()['running'] ?? false);
    }

    /**
     * Acquire an exclusive non-blocking file lock.
     * Returns the lock handle on success, null if already locked.
     * Caller must release with flock($handle, LOCK_UN) + fclose($handle).
     *
     * @return resource|null
     */
    private function acquireLock()
    {
        $lockFile = $this->statusFile . '.lock';
        $fh       = fopen($lockFile, 'c');
        if ($fh === false) return null;
        if (!flock($fh, LOCK_EX | LOCK_NB)) {
            fclose($fh);
            return null;
        }
        return $fh;
    }

    /**
     * Start synchronous batch send.
     * Uses ignore_user_abort(true) + set_time_limit(0) so the PHP process
     * continues writing status even after the HTTP connection is dropped.
     *
     * @param array  $recipients    Array of guest objects (email, name, greeting, token, language)
     * @param array  $templates     Language-keyed templates, e.g. ['de'=>['subject'=>'…','body'=>'…'], 'en'=>[…]]
     * @param string $defaultLang   Fallback language code when a guest has no matching template
     * @param string $headerImageUrl  Relative URL to header image, e.g. "uploads/user/abc.webp"
     *
     * @throws RuntimeException if already running or config missing
     */
    public function sendBatch(array $recipients, array $templates, string $defaultLang, string $headerImageUrl = '', array $design = []): void
    {
        $lock = $this->acquireLock();
        if ($lock === null) {
            throw new RuntimeException('A mail job is already running.');
        }
        if (!file_exists($this->configFile)) {
            throw new RuntimeException('mail_config.json not found.');
        }

        $config = json_decode(file_get_contents($this->configFile), true);
        if (!is_array($config) || empty($config['smtp_host'])) {
            throw new RuntimeException('mail_config.json is invalid or incomplete.');
        }

        $total  = count($recipients);
        $status = [
            'running'        => true,
            'total'          => $total,
            'sent'           => 0,
            'failed'         => 0,
            'errors'         => [],
            'started_at'     => date('c'),
            'finished_at'    => null,
            'last_recipient' => null,
        ];
        $this->writeStatus($status);

        ignore_user_abort(true);
        set_time_limit(0);

        $siteUrl     = rtrim($config['site_url']   ?? '', '/');
        $weddingDate = $config['wedding_date']      ?? '';
        $footer      = $config['mail_footer']       ?? '';
        $driver      = $config['driver']            ?? 'smtp';  // 'smtp' | 'mail'
        $smtp        = $driver === 'smtp' ? new SmtpMailer($config) : null;

        // Resolve absolute header image URL — only allow paths under uploads/ or assets/img/
        $absHeaderUrl = '';
        if ($headerImageUrl !== '') {
            $clean = ltrim(str_replace('\\', '/', $headerImageUrl), '/');
            if (preg_match('#^(uploads/|assets/img/)#', $clean) && !str_contains($clean, '..')) {
                $absHeaderUrl = $siteUrl . '/' . $clean;
            }
        }

        foreach ($recipients as $guest) {
            $email = trim($guest['email'] ?? '');
            if ($email === '') {
                $status['failed']++;
                $status['errors'][] = ($guest['name'] ?? '?') . ': no email address';
                $this->writeStatus($status);
                continue;
            }

            $lang    = $guest['language'] ?? $defaultLang;
            $tpl     = $templates[$lang] ?? $templates[$defaultLang] ?? reset($templates);
            $subject = $tpl['subject'] ?? '';
            $bodyTpl = $tpl['body']    ?? '';

            try {
                $body = $this->renderTemplate($bodyTpl, $guest, $siteUrl, $weddingDate, $design);
                $html = $this->buildEmailHtml($body, $subject, $absHeaderUrl, $footer, $config, $design);
                if ($smtp !== null) {
                    $smtp->send($email, $guest['name'] ?? '', $subject, $html);
                } else {
                    $this->sendViaPhpMail($email, $guest['name'] ?? '', $subject, $html, $config);
                }
                $status['sent']++;
            } catch (\Throwable $e) {
                $status['failed']++;
                $status['errors'][] = ($guest['name'] ?? $email) . ': ' . $e->getMessage();
            }

            $status['last_recipient'] = $guest['name'] ?? $email;
            $this->writeStatus($status);
        }

        $status['running']     = false;
        $status['finished_at'] = date('c');
        $this->writeStatus($status);

        flock($lock, LOCK_UN);
        fclose($lock);
    }

    // ── Private helpers ───────────────────────────────────────────────────────

    /**
     * Send via PHP's mail() — for local dev (e.g. LocalWP/Mailpit). Set driver=mail in config.
     */
    private function sendViaPhpMail(string $toEmail, string $toName, string $subject, string $html, array $cfg): void
    {
        $fromEmail = $cfg['from_email'] ?? '';
        $fromName  = $cfg['from_name']  ?? '';

        $to      = $toName  ? "=?UTF-8?B?" . base64_encode($toName)  . "?= <{$toEmail}>" : $toEmail;
        $from    = $fromName ? "=?UTF-8?B?" . base64_encode($fromName) . "?= <{$fromEmail}>" : $fromEmail;
        $subj    = '=?UTF-8?B?' . base64_encode($subject) . '?=';
        $headers = implode("\r\n", [
            "From: {$from}",
            'MIME-Version: 1.0',
            'Content-Type: text/html; charset=UTF-8',
            'Content-Transfer-Encoding: base64',
        ]);

        if (!mail($to, $subj, chunk_split(base64_encode($html)), $headers)) {
            throw new \RuntimeException('PHP mail() returned false — check sendmail/Mailpit config.');
        }
    }

    private function renderTemplate(string $template, array $guest, string $siteUrl, string $weddingDate, array $design = []): string
    {
        $token       = $guest['token']    ?? '';
        $lang        = $guest['language'] ?? 'de';
        $websiteLink = $siteUrl . '/?g=' . $token;
        $rsvpLink    = $siteUrl . '/?g=' . $token . '&lang=' . $lang;

        $btnBg    = htmlspecialchars($design['btn_bg']    ?? '#B99954', ENT_QUOTES, 'UTF-8');
        $btnColor = htmlspecialchars($design['btn_color'] ?? '#ffffff', ENT_QUOTES, 'UTF-8');

        $rsvpButton = <<<HTML
<table role="presentation" cellpadding="0" cellspacing="0" border="0" style="margin:14px 0;">
  <tr>
    <td align="center" bgcolor="{$btnBg}" style="border-radius:4px;mso-padding-alt:0;">
      <!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="{$rsvpLink}" style="height:48px;v-text-anchor:middle;width:200px;" arcsize="8%" stroke="f" fillcolor="{$btnBg}"><w:anchorlock/><center style="color:{$btnColor};font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:bold;">Zu unserer Hochzeitswebseite</center></v:roundrect><![endif]-->
      <!--[if !mso]><!-->
      <a href="{$rsvpLink}" target="_blank"
        style="display:inline-block;padding:14px 36px;font-family:Arial,Helvetica,sans-serif;font-size:15px;font-weight:700;color:{$btnColor};text-decoration:none;border-radius:4px;mso-hide:all;">Zu unserer Hochzeitswebseite</a>
      <!--<![endif]-->
    </td>
  </tr>
</table>
HTML;

        $name       = htmlspecialchars($guest['name']     ?? '', ENT_QUOTES, 'UTF-8');
        $greeting   = htmlspecialchars($guest['greeting'] ?? '', ENT_QUOTES, 'UTF-8');
        $weddingEsc = htmlspecialchars($weddingDate,            ENT_QUOTES, 'UTF-8');

        // English and German variable names are both supported.
        // German aliases kept for backward compatibility.
        return str_replace(
            ['{{name}}',
             '{{greeting}}',    '{{begrüssung}}',
             '{{wedding_date}}','{{hochzeitsdatum}}',
             '{{website_link}}','{{websitelink}}',
             '{{rsvp_link}}',   '{{rsvp_button}}'],
            [$name,
             $greeting,         $greeting,
             $weddingEsc,       $weddingEsc,
             $websiteLink,      $websiteLink,
             $rsvpLink,         $rsvpButton],
            $template
        );
    }

    /**
     * Wrap the rendered body text in a full, inline-CSS HTML email.
     * Uses table-based layout for maximum client compatibility.
     *
     * Best practices applied:
     *  - No external stylesheets — all styles inline
     *  - Table-based layout (Outlook / old clients)
     *  - MSO conditional comments for Outlook
     *  - Background colours set on both <body> and wrapper <table>
     *  - max-width 600 px, centred
     *  - Fluid image in header (width=100%, max-width=600px)
     *  - Web-safe serif fonts with appropriate fallbacks
     */
    private function buildEmailHtml(
        string $body,
        string $subject,
        string $headerImageUrl,
        string $footer,
        array  $config,
        array  $design = []
    ): string {
        // Convert plain text to HTML if no block-level tags are present
        if (!preg_match('/<\/?(p|br|div|table|h[1-6]|ul|ol|li)\b/i', $body)) {
            $body = nl2br(htmlspecialchars($body, ENT_QUOTES, 'UTF-8'));
        }

        // Footer: convert plain text line breaks
        $footerHtml = nl2br(htmlspecialchars($footer, ENT_QUOTES, 'UTF-8'));

        // Header image row
        $headerRow = '';
        if ($headerImageUrl !== '') {
            $src = htmlspecialchars($headerImageUrl, ENT_QUOTES, 'UTF-8');
            $headerRow = <<<HTML
            <tr>
              <td style="padding:0;line-height:0;font-size:0;" align="center">
                <!--[if mso]><table role="presentation" width="600" cellpadding="0" cellspacing="0"><tr><td><![endif]-->
                <img src="{$src}" width="600" alt="" border="0"
                  style="display:block;width:100%;max-width:600px;height:auto;border-radius:4px 4px 0 0;" />
                <!--[if mso]></td></tr></table><![endif]-->
              </td>
            </tr>
HTML;
        }

        $fromName  = htmlspecialchars($config['from_name'] ?? '', ENT_QUOTES, 'UTF-8');
        $preheader = mb_substr(strip_tags($body), 0, 120);

        $outerBg   = htmlspecialchars($design['bg']         ?? '#f4f2f1', ENT_QUOTES, 'UTF-8');
        $bodyBg    = htmlspecialchars($design['body_bg']     ?? '#ffffff', ENT_QUOTES, 'UTF-8');
        $bodyColor = htmlspecialchars($design['body_color']  ?? '#3a3530', ENT_QUOTES, 'UTF-8');

        return <<<HTML
<!DOCTYPE html>
<html lang="de" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <meta name="format-detection" content="telephone=no,date=no,address=no,email=no">
  <title>{$subject}</title>
  <!--[if mso]>
  <xml><o:OfficeDocumentSettings><o:PixelsPerInch>96</o:PixelsPerInch></o:OfficeDocumentSettings></xml>
  <![endif]-->
  <style type="text/css">
    body,table,td,p,a,li { -webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; }
    table,td { mso-table-lspace:0pt; mso-table-rspace:0pt; border-collapse:collapse; }
    img { border:0; line-height:100%; outline:none; text-decoration:none; -ms-interpolation-mode:bicubic; }
    @media only screen and (max-width:620px) {
      .hw-email-outer { padding:20px 8px !important; }
      .hw-email-inner { width:100% !important; }
      .hw-email-body  { padding:28px 24px 20px !important; }
      .hw-email-foot  { padding:18px 24px 24px !important; }
    }
  </style>
</head>
<body style="margin:0;padding:0;background-color:{$outerBg};word-spacing:normal;">
  <!-- Preheader (hidden) -->
  <div style="display:none;max-height:0;overflow:hidden;mso-hide:all;font-size:1px;color:{$outerBg};">{$preheader}&nbsp;&#8199;&zwnj;&nbsp;&#8199;&zwnj;&nbsp;&#8199;&zwnj;</div>

  <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0"
    style="background-color:{$outerBg};">
    <tr>
      <td align="center" class="hw-email-outer" style="padding:40px 16px;">
        <!--[if mso]><table role="presentation" align="center" width="600" cellpadding="0" cellspacing="0" border="0"><tr><td><![endif]-->
        <table role="presentation" align="center" width="600" cellpadding="0" cellspacing="0" border="0"
          class="hw-email-inner"
          style="max-width:600px;width:100%;background-color:{$bodyBg};border-radius:4px;">

          {$headerRow}

          <!-- ── Body ─────────────────────────────────────────────────────── -->
          <tr>
            <td class="hw-email-body"
              style="padding:40px 48px 32px;font-family:Arial,Helvetica,sans-serif;font-size:17px;line-height:1.75;color:{$bodyColor};">
              {$body}
            </td>
          </tr>

          <!-- ── Divider ───────────────────────────────────────────────────── -->
          <tr>
            <td style="padding:0 40px;">
              <table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0">
                <tr><td style="height:1px;background-color:#e8e4e0;font-size:1px;line-height:1px;">&nbsp;</td></tr>
              </table>
            </td>
          </tr>

          <!-- ── Footer ───────────────────────────────────────────────────── -->
          <tr>
            <td class="hw-email-foot"
              style="padding:20px 48px 32px;font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:1.6;color:#a09890;text-align:center;">
              {$footerHtml}
            </td>
          </tr>
          <tr>
            <td style="padding:0 48px 32px;text-align:center;font-family:Arial,Helvetica,sans-serif;font-size:12px;color:#c8c0b8;">
              Mit Liebe gemacht von<br>
              <a href="https://hochzeitswebseite.de/"><img src="{$siteUrl}/assets/img/howe-logo@400px.png" width="120" height="auto" alt="Hochzeitswebseite.de" border="0"
                style="display:inline-block;width:120px;height:auto;margin-top:8px;" /></a>
            </td>
          </tr>

        </table>
        <!--[if mso]></td></tr></table><![endif]-->
      </td>
    </tr>
  </table>
</body>
</html>
HTML;
    }

    /** Atomic write via temp-file + rename to avoid partial reads during polling. */
    private function writeStatus(array $status): void
    {
        $tmp = $this->statusFile . '.tmp';
        file_put_contents($tmp, json_encode($status, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
        rename($tmp, $this->statusFile);
    }

    private function emptyStatus(): array
    {
        return [
            'running'        => false,
            'total'          => 0,
            'sent'           => 0,
            'failed'         => 0,
            'errors'         => [],
            'started_at'     => null,
            'finished_at'    => null,
            'last_recipient' => null,
        ];
    }
}
