<?php
if (!defined('ABSPATH')) {
    die('You are not allowed to call this page directly.');
}

class FormShieldSpamProtection {
    private static $instance = null;
    private $ip_cache = array();
    private $spam_patterns = array();
    
    public static function get_instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        // Инициализираме спам шаблоните
        $this->init_spam_patterns();

        // NOTE: comment spam is handled by FormShieldWPCommentsIntegration (pre_comment_approved).
        // Do NOT hook preprocess_comment here to avoid double-blocking.

        // Добавяме проверки за форми
        add_action('wp_ajax_formshield_check_spam', array($this, 'check_submission_spam'));
        add_action('wp_ajax_nopriv_formshield_check_spam', array($this, 'check_submission_spam'));
    }
    
    private function init_spam_patterns() {
        $this->spam_patterns = array(
            // URL спам шаблони
            'urls' => array(
                '/\[url=.*?\].*?\[\/url\]/i',     // BBCode URLs
                '/(http|https|ftp):\/\/[^\s]*/i',  // Raw URLs
                '/href\s*=\s*["\'][^"\']*["\']/',  // HTML URLs
                '/www\.[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/i', // www URLs
                '/[a-zA-Z0-9.-]+\.(com|net|org|info|biz|co\.uk)/i', // Domain names
            ),
            
            // Спам думи и фрази
            'keywords' => array(
                '/\b(viagra|cialis|levitra|casino|porn|sex|xxx|adult)\b/i',
                '/\b(buy|cheap|discount|offer|sale|free|deal)\b.*\b(now|online|here|today)\b/i',
                '/\b(make|earn|get).*\b(money|cash|profit|income)\b.*\b(fast|online|easy|quick)\b/i',
                '/\b(click|visit|check|see)\b.*\b(here|link|url|website)\b/i',
                '/\b(guaranteed|promise|guarantee|sure|100%)\b/i',
                '/\b(limited|exclusive|special|unique|amazing)\b.*\b(offer|deal|price)\b/i'
            ),
            
            // Специални символи
            'symbols' => array(
                '/\$[0-9,]+(\.\d{2})?/',  // Цени с десетични
                '/\d{1,3}%/',   // Проценти
                '/[!?]{2,}/',    // Множество препинателни знаци
                '/[\$€£¥]{2,}/', // Множество валутни символи
                '/[\*\+\-]{3,}/', // Множество специални символи
                '/\d{4,}/' // Дълги числови последователности
            ),
            
            // Езикови аномалии
            'language' => array(
                '/(.)\1{3,}/',         // Повтарящи се символи
                '/\b[A-Z]{5,}\b/',     // Дълги CAPS LOCK думи (≥5 букви)
                '/[\x00-\x1F\x7F]/',   // ASCII контролни символи (без multibyte, за да не match-ва UTF-8)
            ),
            
            // Подозрителни фрази
            'phrases' => array(
                '/\b(no\s+spam|not\s+spam|legitimate|genuine)\b/i',
                '/\b(remove|unsubscribe|opt\s+out)\b/i',
                '/\b(winner|congratulations|you\s+won|prize)\b/i',
                '/\b(urgent|asap|immediately|right\s+now)\b/i',
                '/\b(click\s+here|visit\s+now|act\s+now)\b/i'
            )
        );
    }
    
    public function check_submission_spam() {
        // Проверяваме nonce
        check_ajax_referer('formshield_spam_check', 'nonce');
        
        $content = isset($_POST['content']) ? sanitize_textarea_field(wp_unslash($_POST['content'])) : '';
        $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '';
        $ip = formshield_get_validated_ip('Unknown');
        
        // Без директен достъп до $_POST — минаваме през helper-и
        $typing_patterns_json = formshield_get_post_string('typing_patterns', '');
        $typing_patterns = $typing_patterns_json ? json_decode($typing_patterns_json, true) : null;
        if (json_last_error() !== JSON_ERROR_NONE) {
            $typing_patterns = null; // невалиден JSON -> игнорирай
        }

        $paste_count = formshield_get_post_int('paste_count', 0);
        
        $spam_score = $this->calculate_spam_score($content, $email, $ip, $typing_patterns, $paste_count);

        // Store the score in a short-lived transient so fail_form_validation() can read it.
        if ($ip !== 'Unknown') {
            $transient_key = 'formshield_behavioral_' . md5($ip);
            set_transient($transient_key, $spam_score, 5 * MINUTE_IN_SECONDS);
        }

        if ($spam_score >= 70) {
            wp_send_json_error(array(
                'message' => 'Suspected spam content',
                'score'   => $spam_score,
            ));
        } else {
            wp_send_json_success(array(
                'score' => $spam_score,
            ));
        }
    }
    
    public function check_comment_spam($commentdata) {
        $content = $commentdata['comment_content'];
        $email = $commentdata['comment_author_email'];
        $ip = formshield_get_validated_ip('Unknown');
        
        $spam_score = $this->calculate_spam_score($content, $email, $ip);
        
        if ($spam_score >= 70) {
            wp_die( esc_html__( 'Comment blocked as potential spam. Please try again with different content.', 'formshield' ) );
        }
        
        return $commentdata;
    }
    
    private function calculate_spam_score($content, $email, $ip, $typing_patterns = null, $paste_count = 0) {
        $score = 0;

        // IP: internal activity log reputation.
        $internal_ip_score = $this->check_ip_spam_score($ip);
        if ($internal_ip_score > 50) {
            $score += 30;
        }

        // IP: AbuseIPDB external reputation.
        $abuse_score = $this->check_ip_abuseipdb($ip);
        if ($abuse_score >= 75) {
            $score += 50;
        } elseif ($abuse_score >= 50) {
            $score += 30;
        } elseif ($abuse_score >= 25) {
            $score += 15;
        }
        
        // Проверка за спам шаблони
        foreach ($this->spam_patterns as $type => $patterns) {
            foreach ($patterns as $pattern) {
                if (preg_match_all($pattern, $content, $matches)) {
                    $score += (count($matches[0]) * 10);
                }
            }
        }
        
        // Проверка на имейл
        if ($this->is_disposable_email($email)) {
            $score += 20;
        }
        
        // Проверка за скорост на изпращане
        if ($this->is_rapid_submission($ip)) {
            $score += 25;
        }
        
        // Проверка за дължина на съдържанието
        $content_length = strlen($content);
        if ($content_length < 10) {
            $score += 10;
        } elseif ($content_length > 1000) {
            $score += 15;
        }
        
        // Проверка на поведенчески данни от JavaScript
        if ($typing_patterns && isset($typing_patterns['suspicious']) && $typing_patterns['suspicious']) {
            $score += 20; // Подозрителни typing patterns
        }
        
        // Проверка за твърде много paste операции
        if ($paste_count > 3) {
            $score += 15; // Вероятно копиране на голям текст
        }
        
        // Логваме високите резултати
        if ($score >= 50) {
            $this->log_spam_attempt($content, $email, $ip, $score);
        }
        
        return $score;
    }
    
    private function check_ip_spam_score($ip) {
        global $wpdb;
        
        // Проверяваме кеша първо
        if (isset($this->ip_cache[$ip])) {
            return $this->ip_cache[$ip];
        }
        
        // Проверяваме историята на IP адреса
        $table_name = $wpdb->prefix . 'formshield_activity_log';
        $last_24h = gmdate('Y-m-d H:i:s', strtotime('-24 hours'));
        
        // Get stats with caching
        $cache_key = 'formshield_spam_stats_' . md5($ip . $last_24h);
        $stats = wp_cache_get($cache_key, 'formshield');
        if (false === $stats) {
            $table_name = $wpdb->prefix . 'formshield_activity_log';

            $stats = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- read-only към вътрешна таблица
                $wpdb->prepare(
                    "SELECT COUNT(*) AS total_attempts, SUM(is_suspicious) AS suspicious_attempts FROM {$table_name} WHERE ip_address = %s AND `timestamp` > %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table_name е вътрешна таблица от $wpdb->prefix
                    $ip,
                    $last_24h
                )
            );
            wp_cache_set($cache_key, $stats, 'formshield', 300); // 5 минути
        }
        
        $score = 0;
        
        if ($stats) {
            // Повече от 10 опита за 24 часа
            if ($stats->total_attempts > 10) {
                $score += 20;
            }
            
            // Повече от 50% подозрителни опити
            if ($stats->total_attempts > 0) {
                $suspicious_ratio = $stats->suspicious_attempts / $stats->total_attempts;
                if ($suspicious_ratio > 0.5) {
                    $score += 30;
                }
            }
        }
        
        // Кешираме резултата
        $this->ip_cache[$ip] = $score;
        
        return $score;
    }
    
    private function is_disposable_email($email) {
        $pos = strrpos($email, '@');
        if (false === $pos) {
            return false;
        }
        $domain = strtolower(substr($email, $pos + 1));

        $disposable_domains = array(
            // Classic temp-mail services
            'mailinator.com', 'guerrillamail.com', 'guerrillamail.info', 'guerrillamail.biz',
            'guerrillamail.de', 'guerrillamail.net', 'guerrillamail.org', 'spam4.me',
            'trashmail.com', 'trashmail.at', 'trashmail.io', 'trashmail.me', 'trashmail.net',
            'trashmail.org', 'trashmail.xyz', 'throwam.com', 'throwawaymail.com',
            'tempmail.com', 'temp-mail.org', 'temp-mail.io', 'tempmail.net', 'tempmail.de',
            'tempr.email', 'tmpmail.net', 'tmpmail.org', 'yopmail.com', 'yopmail.fr',
            'cool.fr.nf', 'jetable.fr.nf', 'nospam.ze.tc', 'nomail.xl.cx',
            'mega.zik.dj', 'speed.1s.fr', 'courriel.fr.nf', 'moncourrier.fr.nf',
            'monemail.fr.nf', 'monmail.fr.nf',
            // 10 Minute Mail variants
            '10minutemail.com', '10minutemail.net', '10minutemail.org', '10minutemail.co.uk',
            '10minutemail.de', '10minutemail.ru', '10minutemail.co.za', '10minutemail.info',
            '10minemail.com', '10mail.org', '10minutemail.be', '10minutemail.cf',
            // Disposable / anonymous services
            'dispostable.com', 'disposableemailaddresses.com', 'discard.email',
            'discardmail.com', 'discardmail.de', 'spamgourmet.com', 'spamgourmet.net',
            'spamgourmet.org', 'spamhere.tk', 'spamhereplease.com', 'spamoff.de',
            'spaml.com', 'spaml.de', 'spamspot.com', 'spamstack.net', 'spamthis.co.uk',
            'spamthisplease.com', 'spamtrap.ro', 'spamwc.de', 'spoofmail.de',
            'binkmail.com', 'bobmail.info', 'chammy.info', 'devnullmail.com',
            'hatespam.org', 'ieatspam.eu', 'ieatspam.info', 'inoutmail.com',
            'inoutmail.de', 'inoutmail.eu', 'inoutmail.net', 'junk1.tk',
            'kasmail.com', 'klzlk.com', 'lol.ovpn.to', 'maboard.com', 'mail.by',
            'mail4trash.com', 'mailbidon.com', 'mailblocks.com', 'mailcat.biz',
            'mailcatch.com', 'maildrop.cc', 'maileimer.de', 'mailexpire.com',
            'mailf5.com', 'mailforspam.com', 'mailfreeonline.com', 'mailguard.me',
            'mailin8r.com', 'mailinater.com', 'mailismagic.com', 'mailme.lv',
            'mailnew.com', 'mailnull.com', 'mailpick.biz', 'mailscrap.com',
            'mailshell.com', 'mailsiphon.com', 'mailslite.com', 'mailzilla.org',
            'makemetheking.com', 'manybrain.com', 'mega.zik.dj', 'meltmail.com',
            'mierdamail.com', 'mintemail.com', 'moncourrier.fr.nf', 'monemail.fr.nf',
            'monmail.fr.nf', 'mswork.ru', 'myfastmail.com', 'myspaceinc.org',
            'nbzmr.com', 'netzidiot.de', 'nh3.ro', 'noclickemail.com', 'nogmailspam.info',
            'nospamfor.us', 'nospammail.net', 'notmailinator.com', 'nowmymail.com',
            'nowhere.org', 'nowsecondl.net', 'nubescontrol.com', 'obobbo.com',
            'odaymail.com', 'oneoffemail.com', 'onewaymail.com', 'ooemail.com',
            'ordinaryamerican.net', 'o-tay.com', 'owlpic.com', 'pancakemail.com',
            'pjjkp.com', 'plexolan.de', 'poczta.onet.pl', 'politikerclub.de',
            'poofy.org', 'pookmail.com', 'privacy.net', 'proxymail.eu',
            'prtnx.com', 'prtz.eu', 'pubmail.io', 'putthisinyourspamdatabase.com',
            'qq.com.cn', 'quickinbox.com', 'rcpt.at', 'recode.me', 'recursor.net',
            'regbypass.com', 'regbypass.comsafe-mail.net', 'safetymail.info',
            'safetypost.de', 'sandelf.de', 'sanfinder.com', 'saynotospams.com',
            'shieldedmail.com', 'shiftmail.com', 'shit.dns-cloud.net', 'shitmail.de',
            'shitmail.me', 'shitmail.org', 'shortmail.net', 'sibmail.com',
            'sinnlos-mail.de', 'skeefmail.com', 'slapsfromlastnight.com',
            'slaskpost.se', 'slopsbox.com', 'smellfear.com', 'snakemail.com',
            'sneakemail.com', 'sneakmail.de', 'sofort-mail.de', 'sogetthis.com',
            'spacemail.com', 'spam.la', 'spamavert.com', 'spambob.com',
            'spambob.net', 'spambob.org', 'spamcon.org', 'spamcorptastic.com',
            'spamcowboy.com', 'spamcowboy.net', 'spamcowboy.org', 'spamday.com',
            'spamex.com', 'spamfree.eu', 'spamfree24.de', 'spamfree24.eu',
            'spamfree24.info', 'spamfree24.net', 'spamfree24.org', 'spamfriendly.com',
            // Fake-looking generic domains often used for spam signups
            'fakeinbox.com', 'fakeinbox.net', 'fakemail.net', 'fakemailgenerator.com',
            'filzmail.com', 'fivemail.de', 'fixmail.tk', 'fizmail.com',
            'fleckens.hu', 'fls4.gleamrewards.com', 'flyspam.com', 'footard.com',
            'forgetmail.com', 'fr33mail.info', 'frapmail.com', 'freemails.ml',
            'front14.org', 'fuhoy.com', 'garliclife.com', 'get1mail.com',
            'get2mail.fr', 'getairmail.com', 'getcool.nl', 'getformfree.com',
            'getmails.eu', 'getonemail.com', 'getonemail.net', 'gmail.fr',
            'gowikibooks.com', 'gowikicampus.com', 'gowikicars.com', 'gowikifilms.com',
            'gowikigames.com', 'gowikimusic.com', 'gowikinetwork.com', 'gowikitravel.com',
            'gowikitv.com', 'grandmamail.com', 'great-host.in', 'greensloth.com',
            'grish.de', 'groovymail.org', 'gtrgio.com', 'gustr.com',
            'h.mintemail.com', 'hailmail.net', 'harakirimail.com', 'haz.email',
            'herp.in', 'hidemail.de', 'hochsitze.com', 'hopemail.biz',
            'hosterbox.de', 'hot-mail.cf', 'hot-mail.ga', 'hot-mail.gq',
            'hot-mail.ml', 'hot-mail.tk', 'hotpop.com', 'hulapla.de',
            // Additional well-known temporary mail providers
            'guerrillamailblock.com', 'sharklasers.com', 'grr.la', 'guerrillamail.info',
            'spam.su', 'mailnesia.com', 'mytrashmail.com', 'mt2009.com',
            'no-spam.ws', 'nospam.ze.tc', 'nobody.com', 'notbuggin.com',
            'spamfighter.tk', 'spamgob.com', 'spamgolf.com', 'spaml.de',
            'mailezee.com', 'nothn.net', 'trashdevil.com', 'trashdevil.de',
            'trashemail.de', 'trashmail.at', 'trashmail.io', 'trashmail.me',
            'turual.com', 'twinmail.de', 'tyldd.com', 'uggsrock.com',
            'uroid.com', 'us.af', 'venompen.com', 'veryrealemail.com',
            'vidchart.com', 'viditag.com', 'viewcastmedia.com', 'viewcastmedia.net',
            'viewcastmedia.org', 'viralplays.com', 'w3internet.co.uk', 'walala.org',
            'watchfull.net', 'webemail.me', 'webm4il.info', 'wegwerf-email.de',
            'wegwerfadresse.de', 'wegwerfemail.de', 'wegwerfmail.de', 'wegwerfmail.info',
            'wegwerfmail.net', 'wegwerfmail.org', 'wetrainbayarea.com',
            'wetrainbayarea.org', 'wh4f.org', 'whyspam.me', 'wickmail.net',
            'wilemail.com', 'willhackforfood.biz', 'willselfdestruct.com', 'winemaven.info',
            'wronghead.com', 'wuzupmail.net', 'www.e4ward.com', 'www.mailinator.com',
            'xagloo.com', 'xemaps.com', 'xents.com', 'xmaily.com', 'xoxy.net',
            'xww.ro', 'xyzfree.net', 'yahooo.be', 'yapped.net', 'yeah.net',
            'yep.it', 'yogamaven.com', 'yomail.info', 'yuurok.com', 'z1p.biz',
            'za.com', 'zehnminuten.de', 'zehnminutenmail.de', 'zetmail.com',
            'zippymail.info', 'zoemail.com', 'zoemail.net', 'zoemail.org',
        );

        return in_array($domain, $disposable_domains, true);
    }
    
    private function is_rapid_submission($ip) {
        $ip_hash = md5($ip);

        // Check 1: last submission < 30 seconds ago.
        $last_key  = 'formshield_last_sub_' . $ip_hash;
        $last_time = get_transient($last_key);
        if ($last_time && (time() - (int)$last_time) < 30) {
            return true;
        }
        set_transient($last_key, time(), HOUR_IN_SECONDS);

        // Check 2: more than 20 submissions in the last hour.
        $hour_key   = 'formshield_hour_ct_' . $ip_hash;
        $hour_count = (int) get_transient($hour_key);
        $hour_count++;
        set_transient($hour_key, $hour_count, HOUR_IN_SECONDS);
        if ($hour_count > 20) {
            return true;
        }

        // Check 3: more than 50 submissions in the last 24 hours.
        $day_key   = 'formshield_day_ct_' . $ip_hash;
        $day_count = (int) get_transient($day_key);
        $day_count++;
        set_transient($day_key, $day_count, DAY_IN_SECONDS);
        if ($day_count > 50) {
            return true;
        }

        return false;
    }
    
    private function log_spam_attempt($content, $email, $ip, $score) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'formshield_activity_log';
        
        $data = array(
            'ip_address' => $ip,
            'timestamp' => current_time('mysql'),
            'is_suspicious' => 1,
            'validation_data' => wp_json_encode(array(
                'content_sample' => substr($content, 0, 255),
                'email' => $email,
                'spam_score' => $score
            ))
        );
        
        // Use WordPress cache to avoid direct database queries
        $cache_key = 'formshield_activity_' . md5($ip . '|' . $data['timestamp']);
        wp_cache_delete($cache_key, 'formshield');
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert(
            $table_name,
            $data,
            array('%s', '%s', '%d', '%s') // ip_address, timestamp, is_suspicious, validation_data
        );
        
        // Record attack in FormShield statistics for Analytics Overview
        // Try to find the form ID from the current context
        $form_id = $this->get_current_form_id();
        if ($form_id) {
            FormShieldForms::record_spam_attack($form_id, $email, $content);
        }
        
        // Ако имаме много спам от това IP, добавяме го в черния списък
        if ($score > 90) {
            $this->add_to_blacklist($ip);
        }
    }
    
    /**
     * Try to determine the current form ID from context
     * This is a fallback method when we can't get the form ID directly
     */
    private function get_current_form_id() {
        // Check if we're in a form submission context
        $form_id = formshield_get_post_string('form_id');
        if ($form_id) {
            return sanitize_text_field($form_id);
        }
        
        // Check for common form fields
        $cf7_id = formshield_get_post_string('contact-form-7-id');
        if ($cf7_id) {
            return 'cf7_' . sanitize_text_field($cf7_id);
        }
        
        // front-end WPForms submit; няма как да изискваме наш nonce тук
        // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
        $wpforms_post = isset($_POST['wpforms']) ? wp_unslash($_POST['wpforms']) : array();

        $wpforms_raw = is_array($wpforms_post)
            ? array_map('sanitize_text_field', $wpforms_post)
            : array();
        
        $form_id = 0;
        if (is_array($wpforms_raw) && isset($wpforms_raw['id'])) {
            $form_id = absint($wpforms_raw['id']);
        }
        
        if ($form_id) {
            return 'wpforms_' . $form_id;
        }
        
        // Default fallback
        return 'unknown_form';
    }
    
    private function add_to_blacklist($ip) {
        $blacklist = get_option('formshield_ip_blacklist', array());
        if (!in_array($ip, $blacklist, true)) {
            $blacklist[] = $ip;
            update_option('formshield_ip_blacklist', $blacklist);
        }
    }

    /**
     * Query AbuseIPDB for the given IP and return its abuse confidence score (0-100).
     * Results are cached via transient for 1 hour to avoid redundant API calls.
     * Returns 0 if no API key is configured, IP is unknown, or the request fails.
     */
    public function check_ip_abuseipdb($ip) {
        if (empty($ip) || 'Unknown' === $ip) {
            return 0;
        }

        $options = FormShield::get_options();
        $api_key = isset($options['abuseipdb_api_key']) ? trim($options['abuseipdb_api_key']) : '';
        if (empty($api_key)) {
            return 0;
        }

        $cache_key = 'formshield_abuseipdb_' . md5($ip);
        $cached    = get_transient($cache_key);
        if (false !== $cached) {
            return (int) $cached;
        }

        $response = wp_remote_get(
            add_query_arg(array('ipAddress' => $ip, 'maxAgeInDays' => 30), 'https://api.abuseipdb.com/api/v2/check'),
            array(
                'headers' => array(
                    'Key'    => $api_key,
                    'Accept' => 'application/json',
                ),
                'timeout' => 5,
            )
        );

        $score = 0;
        if (!is_wp_error($response) && 200 === (int) wp_remote_retrieve_response_code($response)) {
            $body = json_decode(wp_remote_retrieve_body($response), true);
            if (isset($body['data']['abuseConfidenceScore'])) {
                $score = (int) $body['data']['abuseConfidenceScore'];
                // Auto-blacklist IPs with very high abuse score.
                if ($score >= 90) {
                    $this->add_to_blacklist($ip);
                }
            }
        }

        set_transient($cache_key, $score, HOUR_IN_SECONDS);
        return $score;
    }
}


// Инициализираме класа
FormShieldSpamProtection::get_instance();
