<?php

namespace App\Middleware;

use App\Core\Csrf;
use App\Core\ErrorHandler;
use App\Core\Logger;
use App\Core\MiddlewareInterface;
use App\Core\Request;

/**
 * CsrfMiddleware — CSRF Token Validation Middleware
 *
 * Integrates the Sprint 0 CSRF infrastructure (Csrf, Session) into the
 * middleware pipeline by enforcing token validation on state-changing
 * HTTP requests before the request reaches a controller.
 *
 * Behaviour:
 *   - Non-POST requests (GET, HEAD, OPTIONS) pass through unconditionally.
 *     This is handled transparently by Csrf::validate(), which returns true
 *     for all non-POST methods.
 *   - POST requests with a valid _csrf token: pipeline continues ($next called).
 *   - POST requests with a missing or invalid _csrf token: 403 Forbidden is
 *     rendered via ErrorHandler::renderForbidden() and the pipeline stops
 *     ($next is NOT called).
 *
 * This middleware is thin and single-purpose (SPRINT1_PLANNING.md §7):
 *   - It does NOT inspect authentication or session identity.
 *   - It does NOT produce HTML output directly.
 *   - It does NOT modify the request object.
 *   - It does NOT interact with any Controller, Model, or Service.
 *
 * Pipeline Registration (how to use):
 *   Register CsrfMiddleware in any route definition that accepts POST:
 *     'middleware' => [App\Middleware\CsrfMiddleware::class]
 *   Routes that do not require CSRF protection (e.g. webhook receivers)
 *   simply omit CsrfMiddleware from their middleware list.
 *
 * Extension Note (R-S1-01 — CSRF Middleware Timing):
 *   CSRF exemptions are handled per-route by omitting this middleware,
 *   not by any bypass logic within this class. This class always validates.
 *   See SPRINT1_PLANNING.md §11, R-S1-01 for the documented risk and mitigation.
 *
 * Dependencies (all from Sprint 0 — no new dependencies):
 *   - Csrf::validate()        (INIT-010) — token validation logic
 *   - ErrorHandler::renderForbidden() (INIT-006) — 403 response, no raw output
 *   - Logger::warning()       (INIT-006) — audit log entry on failure
 *   - MiddlewareInterface     (CORE-001) — formal contract implemented here
 *
 * @package Inveetaire
 * @ref     DBP v1.1 Ch.4 Ch.6, ADR-005, SPRINT1_PLANNING.md §5, CORE-002
 */
class CsrfMiddleware implements MiddlewareInterface
{
    /**
     * Validate the CSRF token and continue the pipeline or terminate with 403.
     *
     * Non-POST requests are passed through unconditionally (Csrf::validate()
     * returns true for GET, HEAD, OPTIONS, etc.).
     *
     * On POST with a valid token: calls $next($request) to continue.
     * On POST with a missing or invalid token: logs a warning and renders
     * a 403 Forbidden response. $next is NOT called.
     *
     * @param Request  $request The current HTTP request.
     * @param callable $next    The next middleware layer or controller action.
     *                          Signature: function(Request $request): void
     * @return void
     */
    public function handle(Request $request, callable $next): void
    {
        if (!Csrf::validate($request)) {
            Logger::warning('CSRF validation failed', [
                'method' => $request->getMethod(),
                'path'   => $request->getPath(),
            ]);

            ErrorHandler::renderForbidden();
            return;
        }

        $next($request);
    }
}
