<?php

declare(strict_types=1);



class SpamGuard
{
    private const HONEYPOT_FIELD = 'website';

     

    private const DEFAULTS = [
        'spam_honeypot'           => '1',
        'spam_timegate'           => '1',
        'spam_timegate_seconds'   => '3',
        'spam_ratelimit'          => '1',
        'spam_ratelimit_hour'     => '5',
        'spam_ratelimit_day'      => '30',
        'spam_duplicate'          => '1',
        'spam_duplicate_window'   => '60',
        'spam_links'              => '1',
        'spam_max_links'          => '0',
        'spam_length'             => '1',
        'spam_min_length'         => '2',
        'spam_max_length'         => '5000',
    ];

    private static function cfg(string $key): string
    {
        return Setting::get($key, self::DEFAULTS[$key] ?? '');
    }

    

    public static function formFields(): string
    {
        $html = '';

         
        if (self::cfg('spam_honeypot') === '1') {
            $html .= '<div style="position:absolute;left:-9999px;top:-9999px;" aria-hidden="true">';
            $html .= '<input type="text" name="' . self::HONEYPOT_FIELD . '" value="" tabindex="-1" autocomplete="off">';
            $html .= '</div>';
        }

         
        if (self::cfg('spam_timegate') === '1') {
            $ts = time();
            $hash = self::timeHash($ts);
            $html .= '<input type="hidden" name="_sg_ts" value="' . $ts . '">';
            $html .= '<input type="hidden" name="_sg_h" value="' . $hash . '">';
        }

        return $html;
    }

    

    public static function validate(string $body, string $ip, int $videoId, string $context = 'comment'): ?string
    {
         
        if (self::cfg('spam_honeypot') === '1') {
            if (!empty($_POST[self::HONEYPOT_FIELD])) {
                return 'spam_detected';
            }
        }

         
        if (self::cfg('spam_timegate') === '1') {
            $ts   = (int) ($_POST['_sg_ts'] ?? 0);
            $hash = $_POST['_sg_h'] ?? '';
            if ($ts <= 0 || !hash_equals(self::timeHash($ts), $hash)) {
                return 'spam_detected';
            }
            $minSeconds = max(1, (int) self::cfg('spam_timegate_seconds'));
            if ((time() - $ts) < $minSeconds) {
                return 'comment_too_fast';
            }
        }

         
        if (self::cfg('spam_length') === '1') {
            $len = mb_strlen($body);
            $minLen = max(1, (int) self::cfg('spam_min_length'));
            $maxLen = max($minLen, (int) self::cfg('spam_max_length'));
            if ($len < $minLen) {
                return 'comment_too_short';
            }
            if ($len > $maxLen) {
                return 'comment_too_long';
            }
        }

         
        if (self::cfg('spam_links') === '1') {
            $maxLinks = max(0, (int) self::cfg('spam_max_links'));
            $urlCount = (int) preg_match_all('#https?://#i', $body);
            if ($urlCount > $maxLinks) {
                return 'comment_too_many_links';
            }
        }

         
        if (self::cfg('spam_ratelimit') === '1') {
            $perHour = max(1, (int) self::cfg('spam_ratelimit_hour'));
            $perDay  = max(1, (int) self::cfg('spam_ratelimit_day'));
            if (!RateLimiter::throttle($context, $perHour, $perDay)) {
                return 'comment_rate_limited';
            }
        }

         
        if (self::cfg('spam_duplicate') === '1') {
            $window = max(1, (int) self::cfg('spam_duplicate_window'));
            if (self::isDuplicate($body, $ip, $videoId, $window, $context)) {
                return 'comment_duplicate';
            }
        }

        return null;
    }

    private static function isDuplicate(string $body, string $ip, int $videoId, int $windowMinutes, string $context = 'comment'): bool
    {
        if ($context === 'report') {
            $existing = Database::fetchOne(
                'SELECT id FROM reports
                 WHERE video_id = ? AND ip_address = ?
                   AND created_at > DATE_SUB(NOW(), INTERVAL ? MINUTE)
                 LIMIT 1',
                [$videoId, $ip, $windowMinutes]
            );
            return $existing !== null;
        }

        $bodyHash = self::bodyHash($body);

        $existing = Database::fetchOne(
            'SELECT id FROM comments
             WHERE video_id = ? AND ip_address = ? AND body_hash = ?
               AND created_at > DATE_SUB(NOW(), INTERVAL ? MINUTE)
             LIMIT 1',
            [$videoId, $ip, $bodyHash, $windowMinutes]
        );

        return $existing !== null;
    }

    private static function timeHash(int $ts): string
    {
        $secret = $_SESSION['csrf_token'] ?? 'tubepress';
        return hash_hmac('sha256', (string) $ts, $secret);
    }

    public static function ip(): string
    {
        return RateLimiter::ip();
    }

    public static function bodyHash(string $body): string
    {
        return hash('sha256', mb_strtolower(trim($body)));
    }
}
