<?php

declare(strict_types=1);
defined('ABSPATH') || exit;

/**
 * Handles reCAPTCHA v2 secret key storage (encrypted) and token verification.
 *
 * The secret key is encrypted using AES-256-CBC with WordPress's AUTH_KEY as
 * the encryption key and AUTH_SALT as the IV source. This means a database
 * dump alone cannot reveal the secret key — an attacker also needs access to
 * wp-config.php.
 */
class DSProofwp_Recaptcha_Service
{
    private const OPTION_KEY    = 'dsproofwp_recaptcha_secret_key';
    private const CIPHER        = 'AES-256-CBC';
    private const VERIFY_URL    = 'https://www.google.com/recaptcha/api/siteverify';

    // -------------------------------------------------------------------------
    // Secret key storage
    // -------------------------------------------------------------------------

    /**
     * Encrypt and persist the secret key in wp_options.
     * Passing an empty string removes the stored key.
     */
    public function save_secret_key(string $secret_key): void
    {
        if ($secret_key === '') {
            delete_option(self::OPTION_KEY);
            return;
        }

        update_option(self::OPTION_KEY, $this->encrypt($secret_key), false);
    }

    /**
     * Decrypt and return the stored secret key, or null if not set.
     */
    public function get_secret_key(): ?string
    {
        $stored = get_option(self::OPTION_KEY, '');

        if (empty($stored)) {
            return null;
        }

        $decrypted = $this->decrypt((string) $stored);

        return ($decrypted !== false && $decrypted !== '') ? $decrypted : null;
    }

    /**
     * Returns true if a secret key has been saved (without exposing the value).
     */
    public function has_secret_key(): bool
    {
        return $this->get_secret_key() !== null;
    }

    // -------------------------------------------------------------------------
    // Token verification
    // -------------------------------------------------------------------------

    /**
     * Verify a reCAPTCHA token against Google's siteverify API.
     *
     * @param  string $token   The token received from the browser.
     * @return bool            True if Google confirms the token is valid.
     */
    public function verify_token(string $token): bool
    {
        $secret_key = $this->get_secret_key();

        if ($secret_key === null || $token === '') {
            return false;
        }

        $response = wp_remote_post(self::VERIFY_URL, [
            'timeout' => 10,
            'body'    => [
                'secret'   => $secret_key,
                'response' => $token,
            ],
        ]);

        if (is_wp_error($response)) {
            return false;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        return isset($body['success']) && $body['success'] === true;
    }

    // -------------------------------------------------------------------------
    // Encryption helpers (AUTH_KEY + AUTH_SALT from wp-config.php)
    // -------------------------------------------------------------------------

    private function encrypt(string $plaintext)
    {
        $key = $this->derive_key();
        $iv  = $this->derive_iv();

        $encrypted = openssl_encrypt($plaintext, self::CIPHER, $key, 0, $iv);

        // openssl_encrypt returns false on failure; store empty string in that case
        return $encrypted !== false ? $encrypted : '';
    }

    private function decrypt(string $ciphertext)
    {
        $key = $this->derive_key();
        $iv  = $this->derive_iv();

        return openssl_decrypt($ciphertext, self::CIPHER, $key, 0, $iv);
    }

    /**
     * Derive a 32-byte key from WordPress AUTH_KEY.
     * Falls back to a site-specific value if the constant is not defined
     * (non-standard WP setups).
     */
    private function derive_key()
    {
        $source = defined('AUTH_KEY') ? AUTH_KEY : wp_salt('auth');
        return substr(hash('sha256', $source, true), 0, 32);
    }

    /**
     * Derive a 16-byte IV from WordPress AUTH_SALT.
     */
    private function derive_iv()
    {
        $source = defined('AUTH_SALT') ? AUTH_SALT : wp_salt('secure_auth');
        return substr(hash('md5', $source, true), 0, 16);
    }
}
