<?php
declare(strict_types=1);

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception as MailException;

class ContactController
{
    /** Allowed category keys; labels are localized at use site via t('contact_cat_*'). */
    private const CATEGORY_KEYS = ['bug', 'feature', 'praise', 'critique', 'question', 'other'];

    public static function submit(): void
    {
        header('Content-Type: application/json');

        $cfgPath = BASE_PATH . '/config/contact.php';
        if (!file_exists($cfgPath)) {
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_not_configured')]);
            exit;
        }
        $cfg = require $cfgPath;

        // CSRF
        $token = $_POST['csrf_token'] ?? '';
        if (!Auth::verifyCsrfToken($token)) {
            http_response_code(403);
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_invalid')]);
            exit;
        }

        // Honeypot: bots fill this field, humans leave it empty
        if (!empty($_POST['_hp'] ?? '')) {
            echo json_encode(['ok' => true]);
            exit;
        }

        // Time check: form must have been open for at least 3 seconds
        $ft = (int)($_POST['_ft'] ?? 0);
        if ($ft > 0 && (time() - $ft) < 3) {
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_too_fast')]);
            exit;
        }

        // Rate limiting: one submission per IP per 90 seconds.
        // The flag is stamped at the START of validation (after CSRF + honeypot
        // pass) so an attacker cannot hammer the endpoint with deliberately
        // invalid payloads to bypass the limit — see security-audit #12.
        // Use the centralized client-IP resolver so the limit honours
        // `trust_proxy_headers` and rate-limits real visitors rather than the
        // upstream proxy they share.
        $ip       = Helper::clientIp() ?? '0.0.0.0';
        $rlFile   = sys_get_temp_dir() . '/plenvo_contact_' . md5($ip) . '.tmp';
        $lastSent = file_exists($rlFile) ? (int)file_get_contents($rlFile) : 0;
        if ((time() - $lastSent) < 90) {
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_rate_limit')]);
            exit;
        }
        @file_put_contents($rlFile, (string)time());

        // Input validation
        $name     = trim($_POST['name']     ?? '');
        $email    = trim($_POST['email']    ?? '');
        $category = trim($_POST['category'] ?? '');
        $message  = trim($_POST['message']  ?? '');

        if ($name === '' || mb_strlen($name) > 100) {
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_name')]);
            exit;
        }
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_email')]);
            exit;
        }
        if (!in_array($category, self::CATEGORY_KEYS, true)) {
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_category')]);
            exit;
        }
        if (mb_strlen($message) < 10 || mb_strlen($message) > 5000) {
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_message')]);
            exit;
        }

        // Optional file attachment: images + PDF, max 10 MB. Validated by MIME (finfo), not extension.
        $attachment = null;
        if (!empty($_FILES['attachment']) && ($_FILES['attachment']['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE) {
            $f = $_FILES['attachment'];
            if ($f['error'] !== UPLOAD_ERR_OK) {
                echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_attachment_upload')]);
                exit;
            }
            if ($f['size'] > 10 * 1024 * 1024) {
                echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_attachment_size')]);
                exit;
            }
            $allowed = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'application/pdf'];
            $finfo   = finfo_open(FILEINFO_MIME_TYPE);
            $mime    = $finfo ? finfo_file($finfo, $f['tmp_name']) : null;
            if ($finfo) finfo_close($finfo);
            if (!$mime || !in_array($mime, $allowed, true)) {
                echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_attachment_type')]);
                exit;
            }
            // Sanitize the original filename to prevent header injection
            $safeName = preg_replace('/[^A-Za-z0-9._\- ]/', '_', basename($f['name'])) ?: 'attachment';
            $attachment = ['path' => $f['tmp_name'], 'name' => $safeName, 'mime' => $mime];
        }

        $categoryLabel = Lang::get('contact_cat_' . $category);

        try {
            $mail = new PHPMailer(true);
            $mail->isSMTP();
            $mail->CharSet   = 'UTF-8';
            $mail->SMTPDebug = SMTP::DEBUG_OFF;
            $mail->Host      = $cfg['smtp_host'];
            $mail->Port      = (int)($cfg['smtp_port'] ?? 465);
            $mail->SMTPAuth  = true;
            $mail->Username  = $cfg['smtp_username'];
            $mail->Password  = $cfg['smtp_password'];

            match ($cfg['smtp_encryption'] ?? 'ssl') {
                'ssl'   => $mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS,
                'tls'   => $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS,
                default => (function () use ($mail) {
                    $mail->SMTPSecure  = '';
                    $mail->SMTPAutoTLS = false;
                })(),
            };

            $mail->setFrom($cfg['from_email'], $cfg['from_name'] ?? 'Plenvo');
            $mail->addAddress($cfg['to_email'], $cfg['to_name'] ?? '');
            // Defence in depth before handing $name to PHPMailer: strip any CR/LF
            // and clamp length so a maliciously crafted name field cannot smuggle
            // additional mail headers through the Reply-To line (security-audit #22).
            $safeReplyName = mb_substr(preg_replace('/[\r\n\0]+/', ' ', $name) ?? '', 0, 100);
            $mail->addReplyTo($email, $safeReplyName);

            $mail->isHTML(true);
            $mail->Subject = '[Plenvo ' . Lang::get('contact_eyebrow') . '] ' . $categoryLabel . ': ' . $name;
            $mail->Body    = self::buildBody($name, $email, $categoryLabel, $message, $ip);
            $mail->AltBody = Lang::get('contact_category') . ": $categoryLabel\n"
                           . Lang::get('contact_name')     . ": $name\n"
                           . Lang::get('email')            . ": $email\n\n"
                           . Lang::get('contact_message')  . ":\n$message";

            if ($attachment !== null) {
                $mail->addAttachment($attachment['path'], $attachment['name'], 'base64', $attachment['mime']);
            }

            $mail->send();

            echo json_encode(['ok' => true]);
        } catch (MailException $e) {
            error_log('ContactController: ' . $e->getMessage());
            echo json_encode(['ok' => false, 'error' => Lang::get('contact_err_send')]);
        }
        exit;
    }

    private static function buildBody(string $name, string $email, string $categoryLabel, string $message, string $ip): string
    {
        $eName     = htmlspecialchars($name,          ENT_QUOTES);
        $eEmail    = htmlspecialchars($email,         ENT_QUOTES);
        $eCat      = htmlspecialchars($categoryLabel, ENT_QUOTES);
        $eMessage  = htmlspecialchars($message,       ENT_QUOTES);
        $eIp       = htmlspecialchars($ip,            ENT_QUOTES);
        $timestamp = date('d.m.Y H:i:s');

        $hdrTitle = htmlspecialchars(Lang::get('contact_admin_mail_title'),    ENT_QUOTES);
        $lblCat   = htmlspecialchars(Lang::get('contact_category'),            ENT_QUOTES);
        $lblName  = htmlspecialchars(Lang::get('contact_name'),                ENT_QUOTES);
        $lblMail  = htmlspecialchars(Lang::get('email'),                       ENT_QUOTES);
        $lblMsg   = htmlspecialchars(Lang::get('contact_message'),             ENT_QUOTES);
        $footer   = htmlspecialchars(Lang::get('contact_admin_mail_footer', ['ip' => $ip, 'time' => $timestamp]), ENT_QUOTES);

        return <<<HTML
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body style="font-family:system-ui,sans-serif;background:#f3f4f6;padding:20px;margin:0">
  <div style="max-width:600px;margin:0 auto;background:#fff;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.1)">
    <div style="background:#4f46e5;padding:20px">
      <h1 style="color:#fff;margin:0;font-size:18px">{$hdrTitle}</h1>
    </div>
    <div style="padding:24px">
      <table style="border-collapse:collapse;width:100%;margin-bottom:20px">
        <tr><td style="padding:8px 12px;background:#f8fafc;font-weight:600;width:110px;border-bottom:1px solid #e2e8f0">{$lblCat}</td><td style="padding:8px 12px;border-bottom:1px solid #e2e8f0">{$eCat}</td></tr>
        <tr><td style="padding:8px 12px;background:#f8fafc;font-weight:600;border-bottom:1px solid #e2e8f0">{$lblName}</td><td style="padding:8px 12px;border-bottom:1px solid #e2e8f0">{$eName}</td></tr>
        <tr><td style="padding:8px 12px;background:#f8fafc;font-weight:600">{$lblMail}</td><td style="padding:8px 12px"><a href="mailto:{$eEmail}" style="color:#4f46e5">{$eEmail}</a></td></tr>
      </table>
      <h3 style="margin:0 0 10px;font-size:15px">{$lblMsg}</h3>
      <div style="background:#f8fafc;padding:16px;border-radius:6px;white-space:pre-wrap;font-size:.9rem;line-height:1.6">{$eMessage}</div>
    </div>
    <div style="background:#f9fafb;padding:12px 24px;font-size:.75rem;color:#9ca3af">
      {$footer}
    </div>
  </div>
</body>
</html>
HTML;
    }
}
