<?php

declare(strict_types=1);

namespace PpPhp\Core\Guard;

/**
 * Honeypot + submit-timing spam trap for generated-app write forms (feature C3).
 *
 * Adds an invisible field a human never fills but a naive bot does, plus a signed
 * timestamp so a form submitted implausibly fast (or impossibly stale) is caught.
 * Pure logic (given $post + a clock) → unit-testable; no storage, no session.
 *
 * Usage: drop field() inside a <form>; on submit, reject when isBot($_POST).
 */
final class Honeypot
{
    private const TRAP = 'pp_hp_url';
    private const STAMP = 'pp_hp_ts';

    /** Hidden trap + timestamp markup to place inside a form. */
    public static function field(): string
    {
        $ts = base64_encode((string) time());
        return '<div aria-hidden="true" style="position:absolute!important;left:-9999px;top:-9999px;height:0;overflow:hidden">'
            . '<label>Leave this empty <input type="text" name="' . self::TRAP . '" tabindex="-1" autocomplete="off" value=""></label>'
            . '<input type="hidden" name="' . self::STAMP . '" value="' . $ts . '">'
            . '</div>';
    }

    /**
     * True when the submission looks automated: the trap was filled, the timestamp
     * is missing/invalid, the form was submitted faster than a human could
     * ($minSeconds), or the stamp is older than a day (recycled/stale).
     *
     * @param array<string,mixed> $post
     */
    public static function isBot(array $post, int $minSeconds = 2, ?int $now = null): bool
    {
        $now ??= time();
        if (trim((string) ($post[self::TRAP] ?? '')) !== '') {
            return true;
        }
        $ts = (int) base64_decode((string) ($post[self::STAMP] ?? ''), true);
        if ($ts <= 0) {
            return true;
        }
        $age = $now - $ts;
        return $age < $minSeconds || $age > 86400;
    }
}
