<?php

declare(strict_types=1);

require dirname(__DIR__) . '/config/bootstrap.php';
require dirname(__DIR__) . '/config/admin_lib.php';

$allowedOrigins = array_values(array_filter(array_map(
    static fn (string $origin): string => trim($origin),
    explode(',', (string) ($env['ALLOWED_ORIGIN'] ?? '*'))
)));
$requestOrigin = trim((string) ($_SERVER['HTTP_ORIGIN'] ?? ''));
$allowAnyOrigin = in_array('*', $allowedOrigins, true);

if ($allowAnyOrigin) {
    header('Access-Control-Allow-Origin: *');
} elseif ($requestOrigin !== '' && in_array($requestOrigin, $allowedOrigins, true)) {
    header('Access-Control-Allow-Origin: ' . $requestOrigin);
    header('Vary: Origin');
}

header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Accept-Language, Cache-Control, Pragma');
header('Access-Control-Max-Age: 600');
header('Content-Type: application/json; charset=utf-8');

function respond(int $statusCode, array $payload): void
{
    http_response_code($statusCode);
    echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

function envBool(array $env, string $key, bool $default = false): bool
{
    if (!array_key_exists($key, $env)) {
        return $default;
    }

    return filter_var($env[$key], FILTER_VALIDATE_BOOLEAN);
}

function getClientIp(): string
{
    $forwardedFor = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
    if ($forwardedFor !== '') {
        $parts = explode(',', $forwardedFor);
        $first = trim($parts[0]);
        if ($first !== '') {
            return $first;
        }
    }

    $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    return trim((string) $remoteAddr);
}

function sanitizeInput(mixed $value, int $maxLength = 600): string
{
    if (!is_string($value)) {
        return '';
    }

    $clean = trim($value);
    if ($clean === '') {
        return '';
    }

    if (function_exists('mb_substr')) {
        return mb_substr($clean, 0, $maxLength);
    }

    return substr($clean, 0, $maxLength);
}

function normalizeSmtpHost(string $host): string
{
    $host = trim($host);
    $host = preg_replace('#^[a-z]+://#i', '', $host) ?? $host;
    $host = preg_replace('#/.*$#', '', $host) ?? $host;

    if (str_contains($host, '@')) {
        $parts = explode('@', $host);
        $host = (string) end($parts);
    }

    if (str_contains($host, ':')) {
        $parts = explode(':', $host);
        $host = $parts[0];
    }

    return trim($host, " \t\n\r\0\x0B[]");
}

function envString(array $env, array $keys, string $default = ''): string
{
    foreach ($keys as $key) {
        if (array_key_exists($key, $env) && trim((string) $env[$key]) !== '') {
            return trim((string) $env[$key]);
        }
    }

    return $default;
}

function envInt(array $env, array $keys, int $default): int
{
    $value = envString($env, $keys);
    if ($value === '' || !ctype_digit($value)) {
        return $default;
    }

    return (int) $value;
}

function verifyProsopoCaptcha(string $secret, string $token, string $verifyUrl): bool
{
    $postData = json_encode([
        'secret' => $secret,
        'token' => $token
    ]);

    if ($postData === false) {
        return false;
    }

    $context = stream_context_create([
        'http' => [
            'method' => 'POST',
            'header' => "Content-Type: application/json\r\n",
            'content' => $postData,
            'timeout' => 8
        ]
    ]);

    $result = @file_get_contents($verifyUrl, false, $context);
    if ($result === false) {
        return false;
    }

    $decoded = json_decode($result, true);
    return is_array($decoded) && ($decoded['verified'] ?? false) === true;
}

function parseEmailList(string $value): array
{
    $emails = preg_split('/[,;]/', $value) ?: [];
    $valid = [];

    foreach ($emails as $email) {
        $email = trim($email);
        if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $valid[] = $email;
        }
    }

    return array_values(array_unique($valid));
}

function mimeHeader(string $value): string
{
    $value = trim(preg_replace('/[\r\n]+/', ' ', $value) ?? $value);

    if ($value === '') {
        return '';
    }

    if (preg_match('/^[\x20-\x7E]*$/', $value) === 1) {
        return $value;
    }

    return '=?UTF-8?B?' . base64_encode($value) . '?=';
}

function formatAddress(string $email, string $name = ''): string
{
    $rawName = trim(preg_replace('/[\r\n]+/', ' ', $name) ?? $name);
    $encodedName = mimeHeader($rawName);
    if ($encodedName === '') {
        return '<' . $email . '>';
    }

    if ($encodedName !== $rawName) {
        return $encodedName . ' <' . $email . '>';
    }

    return '"' . addcslashes($encodedName, '"\\') . '" <' . $email . '>';
}

function readSmtpResponse($socket, array $expectedCodes): string
{
    $response = '';

    while (($line = fgets($socket, 515)) !== false) {
        $response .= $line;

        if (preg_match('/^(\d{3})([ -])/', $line, $matches) !== 1) {
            break;
        }

        if ($matches[2] === ' ') {
            $code = (int) $matches[1];
            if (!in_array($code, $expectedCodes, true)) {
                throw new RuntimeException('Unexpected SMTP response code: ' . $code);
            }

            return $response;
        }
    }

    throw new RuntimeException('SMTP server did not return a complete response');
}

function writeSmtpCommand($socket, string $command, array $expectedCodes): string
{
    if (fwrite($socket, $command . "\r\n") === false) {
        throw new RuntimeException('Unable to write SMTP command');
    }

    return readSmtpResponse($socket, $expectedCodes);
}

function openSmtpConnection(string $host, int $port, string $secure, int $timeout)
{
    $context = stream_context_create([
        'ssl' => [
            'verify_peer' => true,
            'verify_peer_name' => true,
            'peer_name' => $host,
            'SNI_enabled' => true
        ]
    ]);

    $transport = $secure === 'ssl' ? 'tls://' : 'tcp://';
    $socket = @stream_socket_client(
        $transport . $host . ':' . $port,
        $errno,
        $errstr,
        $timeout,
        STREAM_CLIENT_CONNECT,
        $context
    );

    if (!is_resource($socket)) {
        throw new RuntimeException('Unable to connect to SMTP server');
    }

    stream_set_timeout($socket, $timeout);
    readSmtpResponse($socket, [220]);

    return $socket;
}

function buildContactEmailBody(array $lead): string
{
    return implode("\n", [
        'New contact form submission',
        '',
        'Submitted: ' . $lead['timestamp'],
        'Name: ' . $lead['name'],
        'Email: ' . $lead['email'],
        'Company: ' . $lead['company'],
        'Country: ' . $lead['country'],
        '',
        'Message:',
        $lead['message'],
        '',
        'Metadata:',
        'IP hash: ' . $lead['ipHash'],
        'User agent: ' . $lead['userAgent'],
        'Accept language: ' . $lead['acceptLanguage']
    ]);
}

function writeContactDeliveryLog(array $payload): void
{
    $logDir = dirname(__DIR__) . '/storage/contact-mail';
    if (!is_dir($logDir)) {
        mkdir($logDir, 0775, true);
    }

    $payload['timestamp'] = gmdate('c');
    $logFile = $logDir . '/' . gmdate('Y-m') . '.jsonl';
    file_put_contents($logFile, json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL, FILE_APPEND | LOCK_EX);
}

function sendSmtpMail(array $config, array $recipients, string $subject, string $body, array $headers): void
{
    $host = normalizeSmtpHost($config['host']);
    $port = $config['port'];
    $secure = $config['secure'];
    $timeout = $config['timeout'];
    $username = $config['username'];
    $password = $config['password'];
    $fromEmail = $config['fromEmail'];
    $fromName = $config['fromName'];

    if ($host === '' || $username === '' || $password === '' || $fromEmail === '' || $recipients === []) {
        throw new RuntimeException('SMTP mail is not configured');
    }

    if (!filter_var($fromEmail, FILTER_VALIDATE_EMAIL)) {
        throw new RuntimeException('SMTP sender email is invalid');
    }

    $socket = openSmtpConnection($host, $port, $secure, $timeout);

    try {
        $hostname = preg_replace('/[^A-Za-z0-9.-]/', '', (string) ($_SERVER['SERVER_NAME'] ?? 'localhost')) ?: 'localhost';
        writeSmtpCommand($socket, 'EHLO ' . $hostname, [250]);

        if ($secure === 'tls') {
            writeSmtpCommand($socket, 'STARTTLS', [220]);
            if (!stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
                throw new RuntimeException('Unable to start SMTP TLS');
            }
            writeSmtpCommand($socket, 'EHLO ' . $hostname, [250]);
        }

        writeSmtpCommand($socket, 'AUTH LOGIN', [334]);
        writeSmtpCommand($socket, base64_encode($username), [334]);
        writeSmtpCommand($socket, base64_encode($password), [235]);
        writeSmtpCommand($socket, 'MAIL FROM:<' . $fromEmail . '>', [250]);

        foreach ($recipients as $recipient) {
            writeSmtpCommand($socket, 'RCPT TO:<' . $recipient . '>', [250, 251]);
        }

        writeSmtpCommand($socket, 'DATA', [354]);

        $messageHeaders = array_merge([
            'Date' => date(DATE_RFC2822),
            'From' => formatAddress($fromEmail, $fromName),
            'To' => implode(', ', array_map(static fn (string $email): string => '<' . $email . '>', $recipients)),
            'Subject' => mimeHeader($subject),
            'MIME-Version' => '1.0',
            'Content-Type' => 'text/plain; charset=UTF-8',
            'Content-Transfer-Encoding' => '8bit'
        ], $headers);

        $rawHeaders = [];
        foreach ($messageHeaders as $name => $value) {
            $name = preg_replace('/[^A-Za-z0-9-]/', '', (string) $name);
            $value = trim(preg_replace('/[\r\n]+/', ' ', (string) $value) ?? '');
            if ($name !== '' && $value !== '') {
                $rawHeaders[] = $name . ': ' . $value;
            }
        }

        $message = implode("\r\n", $rawHeaders) . "\r\n\r\n" . preg_replace("/(?<!\r)\n/", "\r\n", $body);
        $message = preg_replace('/^\./m', '..', $message) ?? $message;

        if (fwrite($socket, $message . "\r\n.\r\n") === false) {
            throw new RuntimeException('Unable to write SMTP message body');
        }

        readSmtpResponse($socket, [250]);
        writeSmtpCommand($socket, 'QUIT', [221]);
    } finally {
        if (is_resource($socket)) {
            fclose($socket);
        }
    }
}

function applyRateLimit(string $bucket, string $clientKey, int $windowSeconds, int $maxRequests): array
{
    $storageDir = dirname(__DIR__) . '/storage/rate-limit';
    if (!is_dir($storageDir)) {
        mkdir($storageDir, 0775, true);
    }

    $filePath = $storageDir . '/' . $bucket . '.json';
    $handle = fopen($filePath, 'c+');
    if ($handle === false) {
        return [true, 0];
    }

    $allowed = true;
    $retryAfter = 0;

    if (flock($handle, LOCK_EX)) {
        $content = stream_get_contents($handle);
        $data = [];

        if (is_string($content) && trim($content) !== '') {
            $decoded = json_decode($content, true);
            if (is_array($decoded)) {
                $data = $decoded;
            }
        }

        $now = time();
        $threshold = $now - $windowSeconds;

        foreach ($data as $key => $timestamps) {
            if (!is_array($timestamps)) {
                unset($data[$key]);
                continue;
            }

            $filtered = array_values(array_filter(
                $timestamps,
                static fn ($timestamp) => is_int($timestamp) && $timestamp > $threshold
            ));

            if ($filtered === []) {
                unset($data[$key]);
            } else {
                $data[$key] = $filtered;
            }
        }

        $entry = $data[$clientKey] ?? [];

        if (count($entry) >= $maxRequests) {
            $allowed = false;
            $retryAfter = max(1, ($entry[0] + $windowSeconds) - $now);
        } else {
            $entry[] = $now;
            $data[$clientKey] = $entry;
        }

        rewind($handle);
        ftruncate($handle, 0);
        fwrite($handle, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
        fflush($handle);
        flock($handle, LOCK_UN);
    }

    fclose($handle);

    return [$allowed, $retryAfter];
}

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';

if ($path === '/' && $_SERVER['REQUEST_METHOD'] === 'GET') {
    header('Location: /admin/login.php', true, 302);
    exit;
}

if ($path === '/api/health') {
    respond(200, [
        'status' => 'ok',
        'service' => $env['APP_NAME'] ?? 'backend'
    ]);
}

if ($path === '/api/contact' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $rawPayload = file_get_contents('php://input') ?: '{}';
    $payload = json_decode($rawPayload, true);

    if (!is_array($payload)) {
        respond(400, ['error' => 'Invalid JSON payload']);
    }

    $clientIp = getClientIp();
    $clientHash = hash('sha256', $clientIp . '|' . ($env['CONTACT_RATE_LIMIT_SALT'] ?? 'acs-rate-limit'));

    $windowSeconds = max(60, (int) ($env['CONTACT_RATE_LIMIT_WINDOW_SECONDS'] ?? 900));
    $maxRequests = max(1, (int) ($env['CONTACT_RATE_LIMIT_MAX_REQUESTS'] ?? 6));

    [$allowed, $retryAfter] = applyRateLimit('contact', $clientHash, $windowSeconds, $maxRequests);
    if (!$allowed) {
        header('Retry-After: ' . $retryAfter);
        respond(429, ['error' => 'Too many requests. Please retry later.']);
    }

    $name = sanitizeInput($payload['name'] ?? '', 120);
    $email = sanitizeInput($payload['email'] ?? '', 180);
    $company = sanitizeInput($payload['company'] ?? '', 160);
    $country = sanitizeInput($payload['country'] ?? '', 120);
    $message = sanitizeInput($payload['message'] ?? '', 2500);
    $website = sanitizeInput($payload['website'] ?? '', 120);
    $captchaToken = sanitizeInput($payload['captchaToken'] ?? '', 6000);

    $consentValue = filter_var($payload['consent'] ?? false, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
    $consent = $consentValue === true;

    if ($website !== '') {
        // Honeypot triggered: pretend success, but drop payload.
        respond(202, ['status' => 'accepted']);
    }

    if ($name === '' || $company === '' || $country === '' || $message === '' || !$consent) {
        respond(422, ['error' => 'Invalid request data']);
    }

    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        respond(422, ['error' => 'Invalid email format']);
    }

    $prosopoSecret = envString($env, ['PROSOPO_SECRET_KEY', 'prosopo_secret_key']);
    $prosopoEnabled = envBool($env, 'PROSOPO_ENABLED', $prosopoSecret !== '');
    $prosopoVerifyUrl = envString($env, ['PROSOPO_VERIFY_URL'], 'https://eu-api.prosopo.io/siteverify');

    if ($prosopoEnabled) {
        if ($prosopoSecret === '') {
            respond(500, ['error' => 'Captcha is not configured']);
        }

        if ($captchaToken === '') {
            respond(422, ['error' => 'Captcha token missing']);
        }

        if (!verifyProsopoCaptcha($prosopoSecret, $captchaToken, $prosopoVerifyUrl)) {
            respond(422, ['error' => 'Captcha verification failed']);
        }
    }

    $lead = [
        'timestamp' => gmdate('c'),
        'name' => $name,
        'email' => $email,
        'company' => $company,
        'country' => $country,
        'message' => $message,
        'consent' => true,
        'ipHash' => $clientHash,
        'userAgent' => sanitizeInput($_SERVER['HTTP_USER_AGENT'] ?? '', 300),
        'acceptLanguage' => sanitizeInput($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '', 120)
    ];

    $storageFile = dirname(__DIR__) . '/storage/leads/' . gmdate('Y-m') . '.jsonl';
    if (!is_dir(dirname($storageFile))) {
        mkdir(dirname($storageFile), 0775, true);
    }
    file_put_contents($storageFile, json_encode($lead, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL, FILE_APPEND | LOCK_EX);

    $smtpAccount = envString($env, ['SMTP_ACCOUNT', 'SMTP_USERNAME', 'SMTP_USER']);
    $recipients = parseEmailList(envString($env, ['CONTACT_TARGET_EMAIL', 'CONTACT_NOTIFICATION_EMAIL', 'SMTP_TO_EMAIL'], $smtpAccount));
    $fromEmail = envString($env, ['SMTP_FROM_EMAIL', 'MAIL_FROM_EMAIL'], $smtpAccount);
    $smtpPort = envInt($env, ['SMTP_PORT'], 587);
    $smtpSecure = strtolower(envString($env, ['SMTP_SECURE', 'SMTP_ENCRYPTION'], $smtpPort === 465 ? 'ssl' : 'tls'));
    if (!in_array($smtpSecure, ['tls', 'ssl'], true)) {
        $smtpSecure = $smtpPort === 465 ? 'ssl' : 'tls';
    }

    $emailSent = false;

    try {
        sendSmtpMail(
            [
                'host' => envString($env, ['SMTP_SERVER', 'SMTP_HOST']),
                'port' => $smtpPort,
                'secure' => $smtpSecure,
                'timeout' => envInt($env, ['SMTP_TIMEOUT_SECONDS'], 15),
                'username' => $smtpAccount,
                'password' => envString($env, ['SMTP_PASSWORD']),
                'fromEmail' => $fromEmail,
                'fromName' => envString($env, ['SMTP_FROM_NAME', 'MAIL_FROM_NAME'], 'A Compliance Services Contact Form')
            ],
            $recipients,
            envString($env, ['CONTACT_EMAIL_SUBJECT'], 'New contact request from ' . $company),
            buildContactEmailBody($lead),
            [
                'Reply-To' => formatAddress($email, $name),
                'Message-ID' => '<contact-' . bin2hex(random_bytes(12)) . '@' . (preg_replace('/[^A-Za-z0-9.-]/', '', (string) ($_SERVER['SERVER_NAME'] ?? 'localhost')) ?: 'localhost') . '>',
                'X-Contact-IP-Hash' => $clientHash
            ]
        );
        $emailSent = true;
    } catch (Throwable $exception) {
        error_log('Contact SMTP delivery failed: ' . $exception->getMessage());
        writeContactDeliveryLog([
            'status' => 'failed',
            'error' => $exception->getMessage(),
            'leadTimestamp' => $lead['timestamp'],
            'recipientCount' => count($recipients)
        ]);
    }

    respond(200, [
        'status' => 'accepted',
        'stored' => true,
        'emailed' => $emailSent
    ]);
}

if ($path === '/api/translations' && $_SERVER['REQUEST_METHOD'] === 'GET') {
    $lang = $_GET['lang'] ?? 'en_GB';
    $allowed = ['en_GB', 'de_DE', 'zh_CN'];

    if (!in_array($lang, $allowed, true)) {
        respond(400, ['error' => 'Unsupported language']);
    }

    $payload = acsBuildFrontendLocalePayload($lang);

    if ($payload === []) {
        respond(500, ['error' => 'Language content could not be built']);
    }

    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}

respond(404, ['error' => 'Not found']);
