<?php

namespace Formatrica\Submission\Security;

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

use Formatrica\Submission\Exception\Submission_Exception;
use Formatrica\Support\Secrets;

/**
 * Handles CAPTCHA verification for multiple providers
 */
final class Captcha_Service
{
    /**
     * Verify a CAPTCHA token
     *
     * @param string $provider
     * @param string $token
     * @param array<string, mixed> $config
     * @throws Submission_Exception
     */
    public function verify(string $provider, string $token, array $config): void
    {
        if ($provider === 'none') {
            return;
        }

        if ($token === '') {
            $this->fail();
        }

        $stored_secret = isset($config['secret_key']) ? (string) $config['secret_key'] : '';
        $secret = Secrets::decrypt($stored_secret);

        if ($secret === '' && $stored_secret !== '') {
            $secret = $stored_secret;
        }

        if ($secret === '') {
            $this->fail();
        }

        switch ($provider) {
            case 'recaptcha_v3':
                $this->verify_recaptcha_v3($token, $secret, $config['ip'] ?? '', (int) ($config['form_id'] ?? 0));
                break;
            case 'turnstile':
                $this->verify_turnstile($token, $secret, $config['ip'] ?? '');
                break;
            case 'friendlycaptcha':
                $site_key = isset($config['site_key']) ? (string) $config['site_key'] : '';
                if ($site_key === '') {
                    $this->fail();
                }
                $this->verify_friendlycaptcha($token, $secret, $site_key);
                break;
            default:
                // Unknown providers are treated as failures to avoid bypasses
                $this->fail();
        }
    }

    /**
     * Verify reCAPTCHA v3 token
     *
     * @param string $token
     * @param string $secret
     * @param string $ip_address
     * @param int $form_id
     * @throws Submission_Exception
     */
    private function verify_recaptcha_v3(string $token, string $secret, string $ip_address, int $form_id): void
    {
        $response = wp_remote_post('https://www.google.com/recaptcha/api/siteverify', [
            'body' => [
                'secret' => $secret,
                'response' => $token,
                'remoteip' => $ip_address,
            ],
            'timeout' => 10,
        ]);

        if (is_wp_error($response)) {
            $this->fail();
        }

        $code = wp_remote_retrieve_response_code($response);

        if ((int) $code !== 200) {
            $this->fail();
        }

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

        if (!is_array($body) || empty($body['success'])) {
            $this->fail();
        }

        $score = isset($body['score']) ? (float) $body['score'] : 0.0;
        $threshold = (float) apply_filters('formatrica_recaptcha_v3_min_score', 0.5, $form_id);

        if ($score < $threshold) {
            $this->fail();
        }
    }

    /**
     * Verify Cloudflare Turnstile token
     *
     * @param string $token
     * @param string $secret
     * @param string $ip_address
     * @throws Submission_Exception
     */
    private function verify_turnstile(string $token, string $secret, string $ip_address): void
    {
        $response = wp_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
            'body' => [
                'secret' => $secret,
                'response' => $token,
                'remoteip' => $ip_address,
            ],
            'timeout' => 10,
        ]);

        if (is_wp_error($response)) {
            $this->fail();
        }

        $code = wp_remote_retrieve_response_code($response);

        if ((int) $code !== 200) {
            $this->fail();
        }

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

        if (!is_array($body) || empty($body['success'])) {
            $this->fail();
        }
    }

    /**
     * Verify FriendlyCaptcha token
     *
     * @param string $token
     * @param string $secret
     * @param string $site_key
     * @throws Submission_Exception
     */
    private function verify_friendlycaptcha(string $token, string $secret, string $site_key): void
    {
        $response = wp_remote_post('https://api.friendlycaptcha.com/api/v1/siteverify', [
            'headers' => [
                'Content-Type' => 'application/json',
            ],
            'body' => wp_json_encode([
                'solution' => $token,
                'secret' => $secret,
                'sitekey' => $site_key,
            ]),
            'timeout' => 15,
        ]);

        if (is_wp_error($response)) {
            $this->fail();
        }

        $code = wp_remote_retrieve_response_code($response);

        if ((int) $code !== 200) {
            $this->fail();
        }

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

        if (!is_array($body) || empty($body['success'])) {
            $this->fail();
        }
    }

    /**
     * Fail CAPTCHA verification
     *
     * @throws Submission_Exception
     */
    private function fail(): void
    {
        throw new Submission_Exception(esc_html__('Captcha verification failed. Please try again.', 'formatrica'), 400);
    }
}
