<?php

declare(strict_types=1);

/**
 * Point d’entrée HTTP : doit être joignable en HTTPS depuis le serveur The Mail Link.
 * Authentifié par HMAC (voir Manager → clé API → relais local).
 */

use LocalMailRelay\SmtpRelaySender;

header('X-Content-Type-Options: nosniff');

$autoload = __DIR__ . '/vendor/autoload.php';
if (!is_file($autoload)) {
    relayRespond(
        500,
        ['ok' => false, 'error' => ['code' => 'DEPENDENCIES_MISSING',
            'message' => 'Lancez `composer install` dans ce dossier.',
            'details' => []]]
    );
}

require_once $autoload;

$configPath = __DIR__ . '/config.php';
if (!is_file($configPath)) {
    relayRespond(
        500,
        ['ok' => false, 'error' => ['code' => 'CONFIG_MISSING',
            'message' => 'Copiez config.dist.php vers config.php et renseignez relay_secret.',
            'details' => []]]
    );
}

/** @var array<string, mixed> $config */
$config = require $configPath;
$relaySecret = trim((string) ($config['relay_secret'] ?? ''));
if ($relaySecret === '') {
    relayRespond(
        500,
        ['ok' => false, 'error' => ['code' => 'CONFIG_INVALID',
            'message' => 'relay_secret manquant dans config.php',
            'details' => []]]
    );
}

$maxBody = isset($config['max_body_bytes']) ? (int) $config['max_body_bytes'] : 10 * 1024 * 1024;
if ($maxBody < 10240) {
    $maxBody = 10240;
}

if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
    relayRespond(
        405,
        ['ok' => false, 'error' => ['code' => 'METHOD_NOT_ALLOWED',
            'message' => 'Only POST.',
            'details' => []]]
    );
}

$ts = $_SERVER['HTTP_X_MAILRELAY_TIMESTAMP'] ?? '';
$sig = $_SERVER['HTTP_X_MAILRELAY_SIGNATURE'] ?? '';
if ($ts === '' || $sig === '') {
    relayRespond(
        403,
        ['ok' => false, 'error' => ['code' => 'RELAY_SIGNING_REQUIRED',
            'message' => 'Missing X-MailRelay-Timestamp or X-MailRelay-Signature.',
            'details' => []]]
    );
}

$tsNum = filter_var($ts, FILTER_VALIDATE_INT);
if ($tsNum === false || abs(time() - (int) $tsNum) > 180) {
    relayRespond(
        403,
        ['ok' => false, 'error' => ['code' => 'RELAY_TIMESTAMP_INVALID',
            'message' => 'Timestamp skew too large.',
            'details' => []]]
    );
}

$raw = file_get_contents('php://input');
if ($raw === false) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'EMPTY_BODY',
            'message' => 'Empty body.',
            'details' => []]]
    );
}

if (strlen($raw) > $maxBody) {
    relayRespond(
        413,
        ['ok' => false, 'error' => ['code' => 'BODY_TOO_LARGE',
            'message' => 'Payload trop volumineux.',
            'details' => ['max' => $maxBody]]]
    );
}

$expected = hash_hmac('sha256', $ts . '.' . $raw, $relaySecret);
if (!hash_equals($expected, $sig)) {
    relayRespond(
        403,
        ['ok' => false, 'error' => ['code' => 'RELAY_SIGNATURE_INVALID',
            'message' => 'Invalid relay signature.',
            'details' => []]]
    );
}

try {
    /** @var array<string, mixed>|null $data */
    $data = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'INVALID_JSON',
            'message' => $e->getMessage(),
            'details' => []]]
    );
}

if (!is_array($data)) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'INVALID_REQUEST',
            'message' => 'Body must be a JSON object.',
            'details' => []]]
    );
}

if (array_key_exists('ping', $data)) {
    if (!relayIsTruthyPingValue($data['ping'])) {
        relayRespond(
            400,
            ['ok' => false, 'error' => ['code' => 'INVALID_REQUEST',
                'message' => 'Le champ ping doit être vrai (ex. JSON {"ping":true}) pour tester le relais.',
                'details' => []]]
        );
    }
    relayRespond(200, [
        'ok' => true,
        'relay' => 'pong',
        'message' => 'Relay HMAC OK (aucun SMTP)',
    ]);
}

$smtp = $data['smtp'] ?? null;
$message = $data['message'] ?? null;
if (!is_array($smtp) || !is_array($message)) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'INVALID_REQUEST',
            'message' => 'Missing smtp or message (arrays). Si vous utilisez uniquement ping, mettre "ping": true.',
            'details' => []]]
    );
}

if (empty($smtp['host']) || empty($smtp['username'])) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'INVALID_SMTP_CONFIG',
            'message' => 'Incomplete SMTP (host, username).',
            'details' => []]]
    );
}

if (SmtpRelaySender::isXoauth2Auth($smtp)) {
    if (trim((string) ($smtp['oauth_access_token'] ?? '')) === '') {
        relayRespond(
            400,
            ['ok' => false, 'error' => ['code' => 'INVALID_SMTP_CONFIG',
                'message' => 'oauth_access_token required for xoauth2.',
                'details' => []]]
        );
    }
} elseif (empty($smtp['password'])) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'INVALID_SMTP_CONFIG',
            'message' => 'SMTP password required.',
            'details' => []]]
    );
}

if (empty($message['to']) || !is_array($message['to'])) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'INVALID_MESSAGE',
            'message' => 'Message must include to[].',
            'details' => []]]
    );
}

if (empty($message['subject'])) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'INVALID_MESSAGE',
            'message' => 'Subject required.',
            'details' => []]]
    );
}

if (empty($message['text']) && empty($message['html'])) {
    relayRespond(
        400,
        ['ok' => false, 'error' => ['code' => 'INVALID_MESSAGE',
            'message' => 'text ou html requis.',
            'details' => []]]
    );
}

if (!empty($smtp['strict_from_match'])) {
    $fromEmail = isset($message['from']['email']) ? trim((string) $message['from']['email']) : '';
    $smtpUsername = trim((string) $smtp['username']);
    if ($fromEmail === '' || strcasecmp($fromEmail, $smtpUsername) !== 0) {
        relayRespond(
            400,
            ['ok' => false, 'error' => ['code' => 'FROM_MUST_MATCH_SMTP_USERNAME',
                'message' => 'strict_from_match: message.from.email must match smtp.username.',
                'details' => []]]
        );
    }
}

$sender = new SmtpRelaySender();
$result = $sender->send($smtp, $message);

relayRespond(($result['ok'] ?? false) ? 200 : 502, $result);

function relayIsTruthyPingValue(mixed $value): bool
{
    if ($value === true) {
        return true;
    }
    if ($value === false || $value === null) {
        return false;
    }
    if (is_int($value)) {
        return $value === 1;
    }
    if (is_string($value)) {
        $lower = strtolower(trim($value));

        return in_array($lower, ['1', 'true', 'yes', 'on'], true);
    }

    return false;
}

/** @param array<string, mixed> $body */
function relayRespond(int $status, array $body): never
{
    header('Content-Type: application/json; charset=utf-8');
    header('Cache-Control: no-store');
    http_response_code($status);
    echo json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}
