<?php
/**
 * Honeypot-style anti-bot — drops a hidden field on login / register /
 * comment forms; if a submission has that field non-empty, it's a bot.
 *
 * Why honeypot and not reCAPTCHA?
 *   - reCAPTCHA = data sent to Google = DSGVO grief, undermines our USP.
 *   - Honeypot is 100% local, no third-party calls, zero user friction.
 *   - Stops the dumb mass-bot 90% — sophisticated targeted attacks need
 *     other defenses (rate-limit / 2FA / WAF), which we already have.
 *
 * The hidden field is named with the SHIELDFORGE_HONEYPOT_FIELD constant
 * so plugins or sites can override it in wp-config (e.g. for custom CSS
 * that must hide it). Default is "sf_hp_address".
 *
 * @package Brainwerk Security Suite
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Shieldforge_Honeypot {

    const DEFAULT_FIELD = 'sf_hp_address';

    public static function register( Shieldforge_Loader $loader ) {
        if ( ! self::is_enabled() ) {
            return;
        }

        if ( Shieldforge_Settings::get( 'honeypot_login', true ) ) {
            $loader->add_action( 'login_form',          __CLASS__, 'render_field' );
            $loader->add_action( 'lostpassword_form',   __CLASS__, 'render_field' );
            $loader->add_filter( 'authenticate',        __CLASS__, 'check_login', 30, 3 );
        }
        if ( Shieldforge_Settings::get( 'honeypot_register', true ) ) {
            $loader->add_action( 'register_form',       __CLASS__, 'render_field' );
            $loader->add_filter( 'registration_errors', __CLASS__, 'check_register', 10, 3 );
        }
        if ( Shieldforge_Settings::get( 'honeypot_comment', true ) ) {
            $loader->add_action( 'comment_form',        __CLASS__, 'render_field' );
            $loader->add_filter( 'preprocess_comment',  __CLASS__, 'check_comment', 10, 1 );
        }
    }

    public static function is_enabled() {
        return (bool) Shieldforge_Settings::get( 'honeypot_enabled', true );
    }

    public static function field_name() {
        return defined( 'SHIELDFORGE_HONEYPOT_FIELD' )
            ? sanitize_key( SHIELDFORGE_HONEYPOT_FIELD )
            : self::DEFAULT_FIELD;
    }

    /* ==================================================================
     *  Render
     * ================================================================== */

    public static function render_field() {
        $name = self::field_name();
        // tabindex=-1 keeps it out of keyboard navigation.
        // autocomplete=off keeps password managers from filling it.
        // The label is included for screen readers — sighted users never
        // see it because of the inline display:none + position:absolute.
        ?>
        <p class="<?php echo esc_attr( $name ); ?>-row" style="display:none !important; position:absolute; left:-9999px;">
            <label for="<?php echo esc_attr( $name ); ?>"><?php esc_html_e( 'Leave this field empty', 'brainwerk-security-suite' ); ?></label>
            <input type="text"
                   name="<?php echo esc_attr( $name ); ?>"
                   id="<?php echo esc_attr( $name ); ?>"
                   value=""
                   autocomplete="off"
                   tabindex="-1">
        </p>
        <?php
    }

    /* ==================================================================
     *  Validators
     * ================================================================== */

    public static function check_login( $user, $username = '', $password = '' ) {
        // Skip if request didn't carry the field at all (e.g. our 2FA
        // interstitial second-step POST has no honeypot field).
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- read-only presence check on WP core login form field, which carries no nonce by design.
        if ( ! isset( $_POST[ self::field_name() ] ) ) {
            return $user;
        }
        if ( '' !== self::posted_value() ) {
            self::log_block( 'login' );
            return new WP_Error(
                'shieldforge_honeypot',
                __( 'Submission blocked: bot heuristic triggered.', 'brainwerk-security-suite' )
            );
        }
        return $user;
    }

    public static function check_register( $errors, $sanitized_user_login = '', $user_email = '' ) {
        if ( '' !== self::posted_value() ) {
            self::log_block( 'register' );
            if ( is_wp_error( $errors ) ) {
                $errors->add( 'shieldforge_honeypot', __( 'Submission blocked: bot heuristic triggered.', 'brainwerk-security-suite' ) );
            }
        }
        return $errors;
    }

    /**
     * For comments we hard-stop with wp_die — preprocess_comment runs
     * before WP creates the comment, so dying here drops the spam.
     */
    public static function check_comment( $commentdata ) {
        if ( '' !== self::posted_value() ) {
            self::log_block( 'comment' );
            wp_die(
                esc_html__( 'Submission blocked.', 'brainwerk-security-suite' ),
                esc_html__( 'Blocked', 'brainwerk-security-suite' ),
                array( 'response' => 403 )
            );
        }
        return $commentdata;
    }

    private static function posted_value() {
        $name = self::field_name();
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- read-only presence check on WP core login/register/comment form field, which carries no nonce by design.
        if ( ! isset( $_POST[ $name ] ) ) {
            return '';
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- see above; same core form field, verified only for bot heuristic (empty/non-empty), not trusted for content.
        return trim( sanitize_text_field( wp_unslash( $_POST[ $name ] ) ) );
    }

    private static function log_block( $surface ) {
        Shieldforge_Logger::log( Shieldforge_Logger::EVT_HARDENING_BLOCK, array(
            'severity' => Shieldforge_Logger::SEV_WARN,
            'context'  => array(
                'reason'  => 'honeypot_triggered',
                'surface' => $surface,
            ),
        ) );
        // Threat-network sensor: a bot tripped the honeypot — a confirmed bad
        // actor. Emit a typed event with the raw IP for cross-site correlation.
        if ( class_exists( 'Shieldforge_Event' ) && class_exists( 'Shieldforge_Utils' ) ) {
            Shieldforge_Event::emit( array(
                'event_type' => 'honeypot.hit',
                'severity'   => 'warn',
                'actor'      => array(
                    'ip'         => Shieldforge_Utils::get_real_ip(),
                    'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '',
                ),
                'context'    => array( 'surface' => $surface ),
            ) );
        }
    }
}
