<?php

declare(strict_types=1);

namespace App\Core;

final class Csrf
{
    private const SESSION_KEY = '_csrf';
    private const INPUT_NAMES = ['_csrf', 'csrf_token'];

    public static function token(): string
    {
        Session::start();

        $token = Session::get(self::SESSION_KEY);
        if (is_string($token) && $token !== '') {
            return $token;
        }

        try {
            $token = bin2hex(random_bytes(32));
        } catch (\Throwable) {
            $token = bin2hex((string) mt_rand()) . bin2hex((string) mt_rand());
        }

        Session::put(self::SESSION_KEY, $token);
        return $token;
    }

    public static function field(string $name = '_csrf'): string
    {
        return '<input type="hidden" name="' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '" value="' . htmlspecialchars(self::token(), ENT_QUOTES, 'UTF-8') . '">';
    }

    public static function validate(?string $submittedToken): bool
    {
        $real = (string) Session::get(self::SESSION_KEY, '');
        $sent = (string) ($submittedToken ?? '');

        if ($sent === '' || $real === '') {
            return false;
        }

        return function_exists('hash_equals') ? hash_equals($real, $sent) : $real === $sent;
    }

    public static function requestToken(): string
    {
        foreach (self::INPUT_NAMES as $name) {
            if (isset($_POST[$name]) && is_string($_POST[$name])) {
                return $_POST[$name];
            }
        }

        return '';
    }

    public static function verify(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
            return;
        }

        if (!self::validate(self::requestToken())) {
            http_response_code(419);
            echo 'CSRF token mismatch. Please refresh the page and try again.';
            exit;
        }
    }
}
