<?php

declare(strict_types=1);

use WebhookMascara\Auth;
use WebhookMascara\AuthException;
use WebhookMascara\Config;
use WebhookMascara\IpGuard;
use WebhookMascara\Logger;
use WebhookMascara\Queue;
use WebhookMascara\RateLimitException;
use WebhookMascara\ValidationException;
use WebhookMascara\Validator;

require __DIR__ . '/../vendor/autoload.php';

$config = new Config();
$logger = new Logger($config);

$isSecure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
    || (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] === '443')
    || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');

$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';

$ipGuard = new IpGuard($config);
if (!$ipGuard->isAllowed($remoteAddr, $_SERVER)) {
    $logger->warning('Ingest rejected: IP not allowed', ['ip' => $remoteAddr]);
    http_response_code(403);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'Forbidden'], JSON_UNESCAPED_SLASHES);
    exit;
}

$maxBody = $config->getMaxBodyBytes();
$contentLength = (int) ($_SERVER['CONTENT_LENGTH'] ?? 0);
if ($contentLength > $maxBody) {
    $logger->warning('Ingest rejected: body too large', ['content_length' => $contentLength]);
    http_response_code(400);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'Payload too large'], JSON_UNESCAPED_SLASHES);
    exit;
}

$rawBody = file_get_contents('php://input');
if ($rawBody === false) {
    $logger->warning('Ingest rejected: failed to read body');
    http_response_code(400);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'Malformed request'], JSON_UNESCAPED_SLASHES);
    exit;
}

if (strlen($rawBody) > $maxBody) {
    $logger->warning('Ingest rejected: body exceeded max after read');
    http_response_code(400);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'Payload too large'], JSON_UNESCAPED_SLASHES);
    exit;
}

try {
    $redis = new Redis();
    $queue = new Queue($config, $redis);
    $queue->connect();

    $auth = new Auth($config, $queue, $logger);
    $auth->authenticate($_SERVER, $rawBody, $isSecure);

    $depth = $queue->depth();
    $threshold = $config->getQueueDepthThreshold();
    if ($depth >= $threshold) {
        $logger->warning('Ingest shed: queue depth exceeded', ['depth' => $depth, 'threshold' => $threshold]);
        http_response_code(503);
        header('Content-Type: application/json');
        header('Retry-After: 10');
        echo json_encode(['error' => 'Service overloaded'], JSON_UNESCAPED_SLASHES);
        exit;
    }

    $data = json_decode($rawBody, true);
    if (!is_array($data) || !isset($data['event_type']) || !isset($data['payload'])) {
        $logger->warning('Ingest rejected: malformed JSON envelope');
        http_response_code(400);
        header('Content-Type: application/json');
        echo json_encode(['error' => 'Malformed JSON envelope'], JSON_UNESCAPED_SLASHES);
        exit;
    }

    $eventType = (string) $data['event_type'];
    $payload = is_array($data['payload']) ? $data['payload'] : [];
    $clientKey = isset($data['idempotency_key']) ? (string) $data['idempotency_key'] : '';

    $validator = new Validator();
    $validator->validate($eventType, $payload);

    $timestamp = (string) ($_SERVER['HTTP_X_TIMESTAMP'] ?? time());
    $idempotencyKey = $clientKey !== ''
        ? $clientKey
        : hash('sha256', $rawBody . ':' . $timestamp);

    $envelope = [
        'event_type' => $eventType,
        'payload' => $payload,
        'received_at' => gmdate('Y-m-d H:i:s'),
        'source_ip' => $remoteAddr,
        'idempotency_key' => $idempotencyKey,
        'client_idempotency_key' => $clientKey,
        'raw_size' => strlen($rawBody),
    ];

    $queue->push($envelope);

    $logger->info('Ingest accepted', [
        'event_type' => $eventType,
        'ip' => $remoteAddr,
        'idempotency_key' => $idempotencyKey,
        'queue_depth' => $depth,
    ]);

    http_response_code(202);
    header('Content-Type: application/json');
    echo json_encode(['accepted' => true, 'idempotency_key' => $idempotencyKey], JSON_UNESCAPED_SLASHES);
} catch (AuthException $e) {
    $code = $e->getCode() >= 400 && $e->getCode() < 600 ? $e->getCode() : 401;
    $logger->warning('Ingest rejected: auth', ['code' => $code, 'message' => $e->getMessage()]);
    http_response_code($code);
    if ($e instanceof RateLimitException) {
        header('Retry-After: ' . $e->getRetryAfter());
    }
    header('Content-Type: application/json');
    echo json_encode(['error' => $e->getMessage()], JSON_UNESCAPED_SLASHES);
} catch (ValidationException $e) {
    $logger->warning('Ingest rejected: validation', ['message' => $e->getMessage()]);
    http_response_code(422);
    header('Content-Type: application/json');
    echo json_encode(['error' => $e->getMessage()], JSON_UNESCAPED_SLASHES);
} catch (Throwable $e) {
    $logger->error('Ingest error', ['message' => $e->getMessage()]);
    http_response_code(500);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'Internal error'], JSON_UNESCAPED_SLASHES);
}
