<?php
/**
 * Floor It Hardwood Floors — contact form handler.
 *
 * Pipeline:
 *   1. Read JSON body (32KB cap)
 *   2. Validate required fields
 *   3. Honeypot + time-on-page checks
 *   4. Altcha server-side verify
 *   5. Sliding-window per-IP rate limit (file-backed in /var/www/html/src/api/data/rate-limits/)
 *   6. POST to Resend → email to contact address
 *   7. JSON response
 *
 * Configuration is read entirely from environment variables — set these in
 * .env or the runtime environment. No hardcoded keys in this file.
 */

declare(strict_types=1);

require_once __DIR__ . '/altcha.php';

header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');

// ─── Config from environment ────────────────────────────────────────
$RESEND_API_KEY   = getenv('RESEND_API_KEY')   ?: '';
$FROM_EMAIL       = getenv('FROM_EMAIL')       ?: 'noreply@floorithardwoods.com';
$TO_EMAIL         = getenv('TO_EMAIL')         ?: 'floorithardwoodfloors@gmail.com';
$RATE_LIMIT       = (int)(getenv('RATE_LIMIT_PER_IP_PER_10MIN') ?: 5);
$TIME_MIN_SECONDS = (int)(getenv('TIME_MIN_SECONDS') ?: 3);

// ─── Helpers ────────────────────────────────────────────────────────
function send_json(array $data, int $status = 200): void {
    http_response_code($status);
    echo json_encode($data, JSON_UNESCAPED_SLASHES);
    exit;
}

function fail(string $msg, int $status = 200): void {
    send_json(['ok' => false, 'error' => $msg], $status);
}

function client_ip(): string {
    $trust = (getenv('TRUST_PROXY') === '1');
    if ($trust) {
        $fwd = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
        if ($fwd) return trim(explode(',', $fwd)[0]);
    }
    return $_SERVER['REMOTE_ADDR'] ?? 'unknown';
}

function rate_limit_check(string $ip, int $max, int $window_seconds): bool {
    $dir = '/var/www/html/src/api/data/rate-limits';
    if (!is_dir($dir)) @mkdir($dir, 0700, true);
    $file = $dir . '/' . preg_replace('/[^a-zA-Z0-9._:-]/', '_', $ip);
    $now = time();
    $events = [];
    if (is_file($file)) {
        $raw = @file_get_contents($file);
        $events = $raw ? array_filter(array_map('intval', explode("\n", $raw))) : [];
        $events = array_filter($events, fn($t) => $now - $t <= $window_seconds);
    }
    if (count($events) >= $max) return false;
    $events[] = $now;
    @file_put_contents($file, implode("\n", $events), LOCK_EX);
    return true;
}

function http_post_json(string $url, array $headers, array $body, int $timeout = 15): array {
    $ch = curl_init($url);
    $payload = json_encode($body, JSON_UNESCAPED_SLASHES);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $payload,
        CURLOPT_HTTPHEADER     => array_merge(['Content-Type: application/json'], $headers),
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT        => $timeout,
    ]);
    $resp = curl_exec($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $err  = curl_error($ch);
    curl_close($ch);
    return ['status' => $code, 'body' => $resp, 'err' => $err];
}

// ─── Method gate ────────────────────────────────────────────────────
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
    fail('Method Not Allowed', 405);
}

// ─── Read + decode JSON body (capped to prevent DoS via huge payloads) ─
$MAX_BODY_BYTES = 32 * 1024;  // 32 KB is plenty for this form
$raw = '';
$in = fopen('php://input', 'rb');
if ($in) {
    $raw = stream_get_contents($in, $MAX_BODY_BYTES + 1);
    fclose($in);
}
if (strlen($raw) > $MAX_BODY_BYTES) {
    fail('Payload too large.', 413);
}
$body = json_decode($raw, true);
if (!is_array($body)) {
    fail('Invalid request payload.');
}

$request_id = bin2hex(random_bytes(6));
$ip = client_ip();

// ─── Rate limit ─────────────────────────────────────────────────────
if (!rate_limit_check($ip, $RATE_LIMIT, 600)) {
    error_log("[floorit.form] rate_limited request_id=$request_id ip=$ip");
    fail('Too many requests. Please wait a few minutes.', 429);
}

// ─── Field extraction + validation ──────────────────────────────────
$name           = trim((string)($body['name']  ?? ''));
$email          = trim((string)($body['email'] ?? ''));
$phone          = trim((string)($body['phone'] ?? ''));
$message        = trim((string)($body['message'] ?? ''));
$website        = trim((string)($body['website'] ?? ''));         // honeypot
$form_loaded_at = trim((string)($body['form_loaded_at'] ?? ''));
$altcha_payload = trim((string)($body['altcha'] ?? ''));

$errors = [];
if (mb_strlen($name) < 2 || mb_strlen($name) > 80)  $errors[] = 'name';
if (!filter_var($email, FILTER_VALIDATE_EMAIL))     $errors[] = 'email';
if (mb_strlen($phone) > 20)                          $errors[] = 'phone';
if (mb_strlen($message) > 2000)                      $errors[] = 'message';

if ($errors) {
    error_log("[floorit.form] validation_error request_id=$request_id fields=" . implode(',', $errors));
    fail('Please check the form fields and try again.');
}

// ─── Honeypot ───────────────────────────────────────────────────────
if ($website !== '') {
    error_log("[floorit.form] honeypot_triggered request_id=$request_id ip=$ip");
    send_json(['ok' => true, 'ref' => $request_id]);  // pretend success
}

// ─── Time-on-page ───────────────────────────────────────────────────
$flagged_review = false;
if ($form_loaded_at !== '' && ctype_digit(ltrim($form_loaded_at, '-'))) {
    $loaded_ms = (int)$form_loaded_at;
    $elapsed = (microtime(true) * 1000 - $loaded_ms) / 1000.0;
    if ($elapsed < $TIME_MIN_SECONDS) $flagged_review = true;
}

// ─── Altcha verify ──────────────────────────────────────────────────
if (!VYC_Altcha::verify($altcha_payload)) {
    error_log("[floorit.form] altcha_verification_failed request_id=$request_id");
    fail('Spam check failed.');
}

// ─── Compose email body ─────────────────────────────────────────────
$subject_name = preg_replace('/[\x00-\x1F\x7F]/u', ' ', $name);
$subject_prefix = $flagged_review ? '[REVIEW] ' : '';
$subject = "{$subject_prefix}New estimate request from {$subject_name}";
$text_body =
    "A new estimate request came in through floorithardwoods.com.\n\n" .
    "Name: {$name}\n" .
    "Email: {$email}\n" .
    "Phone: " . ($phone ?: 'not provided') . "\n\n" .
    "Message:\n" . ($message ?: '(no message)') . "\n\n" .
    "Submitted at: " . gmdate('Y-m-d\TH:i:s\Z') . "\n" .
    "Request id: {$request_id}\n";
$html_body = nl2br(htmlspecialchars($text_body, ENT_QUOTES, 'UTF-8'));

// ─── Send via Resend ────────────────────────────────────────────────
if ($RESEND_API_KEY !== '') {
    $r = http_post_json(
        'https://api.resend.com/emails',
        ["Authorization: Bearer {$RESEND_API_KEY}"],
        [
            'from'     => $FROM_EMAIL,
            'to'       => [$TO_EMAIL],
            'reply_to' => $email,
            'subject'  => $subject,
            'text'     => $text_body,
            'html'     => $html_body,
        ]
    );
    if ($r['status'] >= 300) {
        error_log("[floorit.form] resend_send_failed request_id=$request_id status={$r['status']} body=" . substr($r['body'] ?? '', 0, 300));
        fail('Could not send the message. Please call (716) 602-1429 directly.');
    }
    $resend_id = (json_decode($r['body'] ?? '', true)['id'] ?? '');
    error_log("[floorit.form] resend_send_ok request_id=$request_id resend_id=$resend_id");
} else {
    // Pre-launch / no Resend key: log only, return success
    error_log("[floorit.form] resend_skipped_no_key request_id=$request_id\nSubject: $subject\n$text_body");
}

send_json(['ok' => true, 'ref' => $request_id]);
