<?php
/**
 * Contact form handler for danix.xyz
 *
 * Spam protection:
 *   1. Honeypot field  — bots fill it, humans don't see it
 *   2. Timing check    — reject submissions faster than MIN_ELAPSED seconds
 *   3. IP rate limit   — one submission per IP per RATE_WINDOW seconds
 */

ob_start();
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('Referrer-Policy: strict-origin-when-cross-origin');
header('Cache-Control: no-store');

// ── Configuration ─────────────────────────────────────────────────────────────
// Credentials are read from server environment variables — never hardcoded here.
// Set them in your nginx fastcgi_param, Apache SetEnv, or hosting panel.
const SENDER_DOMAIN  = 'danix.xyz';         // used in From name and subject prefix
const ALLOWED_ORIGIN = 'https://danix.xyz';  // CSRF: only accept from this origin
const MIN_ELAPSED    = 4;                   // seconds before submission is accepted
const RATE_WINDOW    = 300;                 // seconds between allowed submissions per IP
const RATE_DIR       = '/var/lib/danixxyz/cf_rate'; // owned by www-data:www-data, mode 0700

// $cfg = [
//     'recipient'   => getenv('MAIL_RECIPIENT')  ?: '',
//     'smtp_host'   => getenv('MAIL_SMTP_HOST')  ?: '',
//     'smtp_port'   => (int)(getenv('MAIL_SMTP_PORT') ?: 587),
//     'smtp_secure' => getenv('MAIL_SMTP_SECURE') ?: 'tls',
//     'smtp_user'   => getenv('MAIL_SMTP_USER')  ?: '',
//     'smtp_pass'   => getenv('MAIL_SMTP_PASS')  ?: '',
// ];

$cfg = require dirname(__DIR__, 2) . '/mail-config.php';

if ($cfg['recipient'] === '' || $cfg['smtp_host'] === '' || $cfg['smtp_user'] === '') {
    out(500, 'Mail not configured');
}
// ──────────────────────────────────────────────────────────────────────────────

// Only accept POST
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    out(405, 'Method not allowed');
}

// ── CSRF: verify request originates from our own domain ───────────────────────
$origin  = $_SERVER['HTTP_ORIGIN']  ?? '';
$referer = $_SERVER['HTTP_REFERER'] ?? '';

if ($origin !== '') {
    if ($origin !== ALLOWED_ORIGIN) {
        out(403, 'Forbidden');
    }
} elseif (strpos($referer, ALLOWED_ORIGIN . '/') !== 0) {
    out(403, 'Forbidden');
}
// ─────────────────────────────────────────────────────────────────────────────

// ── Honeypot ──────────────────────────────────────────────────────────────────
// If the hidden "website" field is filled, it's a bot — silently pretend success.
if (!empty($_POST['website'])) {
    out(200, null, true);
}

// ── Timing check ──────────────────────────────────────────────────────────────
$loadedAt = (int) filter_input(INPUT_POST, '_t', FILTER_SANITIZE_NUMBER_INT);
$elapsed  = time() - $loadedAt;

if ($loadedAt === 0 || $elapsed < MIN_ELAPSED || $elapsed > 7200) {
    out(400, 'Invalid submission');
}

// ── Rate limiting ─────────────────────────────────────────────────────────────
$ip      = preg_replace('/[^a-f0-9:.]/', '', $_SERVER['REMOTE_ADDR'] ?? '');
$lockDir = rtrim(RATE_DIR, '/');

if (!is_dir($lockDir)) {
    @mkdir($lockDir, 0700, true);
}

$lockFile = $lockDir . '/' . md5($ip) . '.lock';

// Atomic check+write using exclusive lock — prevents TOCTOU race condition
$fp = fopen($lockFile, 'c+');
if ($fp === false || !flock($fp, LOCK_EX)) {
    out(500, 'Server error');
}
$lastTime = (int) fread($fp, 20);
if ($lastTime > 0 && (time() - $lastTime) < RATE_WINDOW) {
    flock($fp, LOCK_UN);
    fclose($fp);
    out(429, 'Too many requests');
}

// ── Sanitize & validate ───────────────────────────────────────────────────────
$name    = clean(INPUT_POST, 'name',    100);
$email   = trim(filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL) ?? '');
$subject = clean(INPUT_POST, 'subject', 200);
$message = clean(INPUT_POST, 'message', 5000);

if ($name === '' || $email === '' || $message === '') {
    flock($fp, LOCK_UN); fclose($fp);
    out(400, 'Missing required fields');
}

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    flock($fp, LOCK_UN); fclose($fp);
    out(400, 'Invalid email address');
}

// ── Build and send the email via PHPMailer ────────────────────────────────────
require __DIR__ . '/vendor/autoload.php';

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception as MailerException;

$subjectLine = '[contact from danix.xyz] - ' . ($subject !== '' ? $subject : 'New message from ' . $name);

$body = implode("\n", [
    'Name:    ' . $name,
    'Email:   ' . $email,
    'Subject: ' . ($subject ?: '—'),
    '',
    str_repeat('─', 48),
    '',
    $message,
]);

$mail = new PHPMailer(true);

try {
    $mail->isSMTP();
    $mail->Host       = $cfg['smtp_host'];
    $mail->SMTPAuth   = true;
    $mail->Username   = $cfg['smtp_user'];
    $mail->Password   = $cfg['smtp_pass'];
    $mail->SMTPSecure = $cfg['smtp_secure'] === 'ssl' ? PHPMailer::ENCRYPTION_SMTPS : PHPMailer::ENCRYPTION_STARTTLS;
    $mail->Port       = $cfg['smtp_port'];
    $mail->CharSet    = 'UTF-8';

    $mail->setFrom('danix@' . SENDER_DOMAIN, SENDER_DOMAIN);
    $mail->addReplyTo($email, $name);
    $mail->addAddress($cfg['recipient']);

    $mail->Subject = $subjectLine;
    $mail->Body    = $body;

    $mail->send();

    rewind($fp);
    ftruncate($fp, 0);
    fwrite($fp, (string) time());
    flock($fp, LOCK_UN);
    fclose($fp);
    out(200, null, true);
} catch (MailerException $e) {
    flock($fp, LOCK_UN);
    fclose($fp);
    out(500, 'Failed to send email');
}

// ── Helpers ───────────────────────────────────────────────────────────────────

function clean(int $type, string $key, int $maxLen): string
{
    $val = filter_input($type, $key, FILTER_UNSAFE_RAW) ?? '';
    $val = strip_tags(trim((string) $val));
    return mb_substr($val, 0, $maxLen);
}

function out(int $code, ?string $error, bool $success = false): void
{
    ob_end_clean();
    http_response_code($code);
    echo json_encode(['success' => $success, 'error' => $error]);
    exit;
}
