<?php

declare(strict_types=1);

namespace App\Core;

/**
 * Csrf
 *
 * Synchronizer-token pattern. One token per session (not per-form) is
 * enough here since the app is same-origin only — regenerating it after
 * every check would break users with multiple tabs open. The token is
 * compared with hash_equals() to avoid timing attacks.
 */
final class Csrf
{
    private const SESSION_KEY = '_csrf_token';

    public static function token(): string
    {
        if (!Session::has(self::SESSION_KEY)) {
            Session::set(self::SESSION_KEY, bin2hex(random_bytes(32)));
        }

        return (string) Session::get(self::SESSION_KEY);
    }

    public static function verify(?string $submittedToken): bool
    {
        $real = Session::get(self::SESSION_KEY);

        if (!is_string($real) || !is_string($submittedToken) || $submittedToken === '') {
            return false;
        }

        return hash_equals($real, $submittedToken);
    }

    /** Renders a ready-to-drop-in hidden input for classic <form> posts. */
    public static function field(): string
    {
        $token = htmlspecialchars(self::token(), ENT_QUOTES, 'UTF-8');

        return '<input type="hidden" name="_csrf_token" value="' . $token . '">';
    }

    /** Renders a <meta> tag so vanilla-JS fetch() calls can read the token for the X-CSRF-Token header. */
    public static function metaTag(): string
    {
        $token = htmlspecialchars(self::token(), ENT_QUOTES, 'UTF-8');

        return '<meta name="csrf-token" content="' . $token . '">';
    }
}
