<?php
/**
 * Captcha-Wrapper: hCaptcha + Cloudflare Turnstile.
 *
 * Privacy-Choice: KEIN Google reCAPTCHA (Default), sondern:
 * - hCaptcha (EU-friendly, GDPR-compliant, gratis)
 * - Cloudflare Turnstile (kein Cookie, kein User-Tracking)
 *
 * Provider-Wahl + Site-Key + Secret-Key in Settings.
 * Rendert auf wp-login.php (login + register + lost-password) plus
 * konfigurierbar auf Kommentar-Formularen.
 *
 * Server-Side-Verify via wp_remote_post mit Timeout-Schutz.
 *
 * @package Brainwerk Security Suite
 */

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

class Shieldforge_Captcha {

    const VERIFY_URLS = array(
        'hcaptcha'  => 'https://hcaptcha.com/siteverify',
        'turnstile' => 'https://challenges.cloudflare.com/turnstile/v0/siteverify',
    );

    const SCRIPT_URLS = array(
        'hcaptcha'  => 'https://js.hcaptcha.com/1/api.js',
        'turnstile' => 'https://challenges.cloudflare.com/turnstile/v0/api.js',
    );

    public static function register( Shieldforge_Loader $loader ) {
        if ( ! Shieldforge_Settings::get( 'captcha_enabled', false ) ) return;
        if ( ! self::has_keys() ) return;
        // Render-Hooks
        $loader->add_action( 'login_enqueue_scripts', __CLASS__, 'enqueue_script' );
        $loader->add_action( 'login_form',            __CLASS__, 'render' );
        $loader->add_action( 'register_form',         __CLASS__, 'render' );
        $loader->add_action( 'lostpassword_form',     __CLASS__, 'render' );
        // Verify-Hooks
        $loader->add_filter( 'wp_authenticate_user', __CLASS__, 'verify_login', 30, 2 );
        $loader->add_filter( 'registration_errors',  __CLASS__, 'verify_register', 10, 3 );
        $loader->add_action( 'lostpassword_post',    __CLASS__, 'verify_lostpassword' );
    }

    private static function has_keys() {
        return Shieldforge_Settings::get( 'captcha_site_key', '' ) && Shieldforge_Settings::get( 'captcha_secret_key', '' );
    }
    private static function provider() {
        $p = Shieldforge_Settings::get( 'captcha_provider', 'hcaptcha' );
        return in_array( $p, array( 'hcaptcha', 'turnstile' ), true ) ? $p : 'hcaptcha';
    }

    public static function enqueue_script() {
        $url = self::SCRIPT_URLS[ self::provider() ];
        wp_enqueue_script( 'shieldforge-captcha', $url, array(), null, true );
    }

    public static function render() {
        $site_key = Shieldforge_Settings::get( 'captcha_site_key', '' );
        $cls = ( 'hcaptcha' === self::provider() ) ? 'h-captcha' : 'cf-turnstile';
        echo '<div class="' . esc_attr( $cls ) . '" data-sitekey="' . esc_attr( $site_key ) . '" style="margin:10px 0;"></div>';
    }

    private static function get_response_field() {
        $field = ( 'hcaptcha' === self::provider() ) ? 'h-captcha-response' : 'cf-turnstile-response';
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- hooked into WP core login/register/lostpassword forms, which carry no nonce by design; captcha response itself is server-side verified against the provider API.
        return isset( $_POST[ $field ] ) ? sanitize_text_field( wp_unslash( $_POST[ $field ] ) ) : '';
    }

    private static function verify_response( $response ) {
        if ( '' === $response ) return false;
        $url    = self::VERIFY_URLS[ self::provider() ];
        $secret = Shieldforge_Settings::get( 'captcha_secret_key', '' );
        $r = wp_remote_post( $url, array(
            'timeout' => 8,
            'body'    => array(
                'secret'   => $secret,
                'response' => $response,
                'remoteip' => Shieldforge_Utils::get_real_ip(),
            ),
        ) );
        if ( is_wp_error( $r ) ) return false;
        $body = json_decode( wp_remote_retrieve_body( $r ), true );
        return ! empty( $body['success'] );
    }

    /** Login: WP_Error abbrechen wenn Captcha-Check fehlschlägt. */
    public static function verify_login( $user, $password ) {
        if ( is_wp_error( $user ) ) return $user;
        // Skip wenn nicht von Form (z. B. Cookie-Login)
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- read-only presence check on WP core login form fields, which carry no nonce by design.
        if ( empty( $_POST['log'] ) && empty( $_POST['user_login'] ) ) return $user;
        $resp = self::get_response_field();
        if ( ! self::verify_response( $resp ) ) {
            return new WP_Error( 'shieldforge_captcha', __( '<strong>Sicherheits-Check fehlgeschlagen.</strong> Bitte CAPTCHA erneut bestätigen.', 'brainwerk-security-suite' ) );
        }
        return $user;
    }

    /** Registration. */
    public static function verify_register( $errors, $login, $email ) {
        $resp = self::get_response_field();
        if ( ! self::verify_response( $resp ) ) {
            $errors->add( 'shieldforge_captcha', __( 'CAPTCHA-Check fehlgeschlagen. Bitte erneut versuchen.', 'brainwerk-security-suite' ) );
        }
        return $errors;
    }

    /** Lost-Password. */
    public static function verify_lostpassword( $errors ) {
        $resp = self::get_response_field();
        if ( ! self::verify_response( $resp ) ) {
            if ( is_wp_error( $errors ) ) {
                $errors->add( 'shieldforge_captcha', __( 'CAPTCHA-Check fehlgeschlagen.', 'brainwerk-security-suite' ) );
            } else {
                wp_die( esc_html__( 'CAPTCHA-Check fehlgeschlagen.', 'brainwerk-security-suite' ), 403 );
            }
        }
    }
}
