<?php
/**
 * Handle Captcha settings.
 *
 * @package WP_Defender\Model\Setting
 */

namespace WP_Defender\Model\Setting;

use Calotes\Model\Setting;

/**
 * Model for Captcha settings.
 */
class Captcha extends Setting {

	/**
	 * Option name.
	 * We'll keep the current table for all Captcha for backward compatibility. There's no need to edit the table name to store settings in the database.
	 *
	 * @var string
	 */
	protected $table = 'wd_recaptcha_settings';

	/**
	 * Feature status.
	 *
	 * @var bool
	 * @defender_property
	 */
	public $enabled = false;

	/**
	 * Captcha Provider.
	 *
	 * @var string
	 * @defender_property
	 */
	public $provider = 'recaptcha';

	/**
	 * Active Captcha type.
	 *
	 * @var string
	 * @defender_property
	 * @rule required
	 * @rule in[v2_checkbox,v2_invisible,v3_recaptcha,turnstile]
	 */
	public $active_type = 'v2_checkbox';

	/**
	 * Data for v2 checkbox reCaptcha.
	 *
	 * @var array
	 * @defender_property
	 */
	public $data_v2_checkbox;

	/**
	 * Data for v2 invisible reCaptcha.
	 *
	 * @var array
	 * @defender_property
	 */
	public $data_v2_invisible;

	/**
	 * Data for v3 reCaptcha.
	 *
	 * @var array
	 * @defender_property
	 */
	public $data_v3_recaptcha;

	/**
	 * Data for Cloudflare Turnstile.
	 *
	 * @var array
	 * @defender_property
	 */
	public $data_turnstile;

	/**
	 * Language for Captcha.
	 *
	 * @var string
	 * @defender_property
	 * @rule required
	 */
	public $language = '';

	/**
	 * Message for reCaptcha.
	 *
	 * @var string
	 * @defender_property
	 * @sanitize sanitize_textarea_field
	 */
	public $message = '';

	/**
	 * Locations for Captcha.
	 *
	 * @var array
	 * @defender_property
	 * @rule required
	 */
	public $locations = array();

	/**
	 * Flag to detect WooCommerce.
	 *
	 * @var bool
	 * @defender_property
	 */
	public $detect_woo = false;

	/**
	 * Checked locations for WooCommerce.
	 *
	 * @var array
	 * @defender_property
	 * @rule required
	 */
	public $woo_checked_locations = array();

	/**
	 * Flag to detect BuddyPress.
	 *
	 * @var bool
	 * @defender_property
	 */
	public $detect_buddypress = false;

	/**
	 * Checked locations for BuddyPress.
	 *
	 * @var array
	 * @defender_property
	 * @rule required
	 */
	public $buddypress_checked_locations = array();

	/**
	 * Flag to disable Captcha for known users.
	 *
	 * @var bool
	 * @defender_property
	 */
	public $disable_for_known_users = true;

	/**
	 * Rules for validating the Captcha settings.
	 *
	 * @var array
	 */
	protected $rules = array(
		array( array( 'enabled', 'detect_woo', 'detect_buddypress' ), 'boolean' ),
		array( array( 'active_type' ), 'in', array( 'v2_checkbox', 'v2_invisible', 'v3_recaptcha', 'turnstile' ) ),
	);

	/**
	 * Retrieves the default values for the Captcha settings.
	 *
	 * @return array An associative array containing the default values.
	 */
	public function get_default_values(): array {
		return array(
			'message'           => esc_html__( 'reCAPTCHA verification failed. Please try again.', 'defender-security' ),
			'turnstile_message' => esc_html__( 'Turnstile verification failed. Please try again.', 'defender-security' ),
		);
	}

	/**
	 *  Load default values.
	 *
	 * @return void
	 */
	protected function before_load(): void {
		$default_values          = $this->get_default_values();
		$this->provider          = 'recaptcha';
		$this->message           = $default_values['message'];
		$this->language          = 'automatic';
		$this->data_v2_checkbox  = array(
			'key'    => '',
			'secret' => '',
			'size'   => 'normal',
			'style'  => 'light',
		);
		$this->data_v2_invisible = array(
			'key'    => '',
			'secret' => '',
		);
		$this->data_v3_recaptcha = array(
			'key'       => '',
			'secret'    => '',
			'threshold' => '0.5',
		);
		$this->data_turnstile    = array(
			'key'      => '',
			'secret'   => '',
			'size'     => 'normal',
			'style'    => 'auto',
			'message'  => $default_values['turnstile_message'],
			'language' => 'auto',
		);
	}

	/**
	 * Checks if the given Captcha type is valid and has all the necessary data.
	 *
	 * @param  string $active_type  The Captcha type to check.
	 *
	 * @return bool Returns true if the Captcha type is valid and has all the necessary data, false otherwise.
	 */
	private function check_captcha_type( string $active_type ): bool {
		if (
			'v2_checkbox' === $active_type
			&& '' !== $this->data_v2_checkbox['key']
			&& '' !== $this->data_v2_checkbox['secret']
		) {
			return true;
		} elseif (
			'v2_invisible' === $active_type
			&& '' !== $this->data_v2_invisible['key']
			&& '' !== $this->data_v2_invisible['secret']
		) {
			return true;
		} elseif (
			'v3_recaptcha' === $active_type
			&& '' !== $this->data_v3_recaptcha['key'] && '' !== $this->data_v3_recaptcha['secret']
		) {
			return true;
		} elseif (
			'turnstile' === $active_type &&
			'' !== $this->data_turnstile['key'] &&
			'' !== $this->data_turnstile['secret']
		) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Checks if the CAPTCHA is active.
	 *
	 * @return bool Returns true if the CAPTCHA is active, false otherwise.
	 */
	public function is_active(): bool {
		$hook_name = 'turnstile' === $this->active_type ? 'wd_turnstile_enable' : 'wd_recaptcha_enable';
		return (bool) apply_filters(
			$hook_name,
			$this->enabled
			&& '' !== $this->active_type
			&& '' !== $this->language
			// For each Captcha type.
			&& $this->check_captcha_type( $this->active_type )
		);
	}

	/**
	 * Is activated any default location?
	 *
	 * @return bool
	 */
	public function enable_default_location(): bool {
		return array() !== $this->locations;
	}

	/**
	 * Level#2 check by any activated location. Only if the plugin is enabled.
	 *
	 * @return bool
	 */
	public function enable_woo_location(): bool {
		return $this->detect_woo && array() !== $this->woo_checked_locations;
	}

	/**
	 * Level#2 check by deactivated locations. Only if the plugin is enabled.
	 *
	 * @return bool
	 */
	public function is_unchecked_woo_locations(): bool {
		return $this->detect_woo && array() === $this->woo_checked_locations;
	}

	/**
	 * Level#1 check. If the plugin is disabled, there is no point further.
	 *
	 * @param  bool $is_woo_activated  Whether WooCommerce is activated or not.
	 *
	 * @return bool
	 */
	public function check_woo_locations( $is_woo_activated ): bool {
		if ( ! $is_woo_activated ) {
			return false;
		}

		return $this->enable_woo_location();
	}

	/**
	 * Level#2 check by any activated location. Only if the plugin is enabled.
	 *
	 * @return bool
	 */
	public function enable_buddypress_location(): bool {
		return $this->detect_buddypress && array() !== $this->buddypress_checked_locations;
	}

	/**
	 * Level#2 check by deactivated locations. Only if the plugin is enabled.
	 *
	 * @return bool
	 */
	public function is_unchecked_buddypress_locations(): bool {
		return $this->detect_buddypress && array() === $this->buddypress_checked_locations;
	}

	/**
	 * Level#1 Checks if the BuddyPress locations are valid and have all the necessary data.
	 *
	 * @param  bool $is_buddypress_activated  Whether BuddyPress is activated or not.
	 *
	 * @return bool Returns true if the BuddyPress locations are valid and have all the necessary data, false otherwise.
	 */
	public function check_buddypress_locations( $is_buddypress_activated ): bool {
		if ( ! $is_buddypress_activated ) {
			return false;
		}

		return $this->enable_buddypress_location();
	}

	/**
	 * Define settings labels.
	 *
	 * @return array
	 */
	public function labels(): array {
		return array(
			'enabled'                 => self::get_module_name(),
			'active_type'             => esc_html__( 'Configure CAPTCHA', 'defender-security' ),
			'v2_checkbox'             => esc_html__( 'V2 Checkbox', 'defender-security' ),
			'v2_invisible'            => esc_html__( 'V2 Invisible', 'defender-security' ),
			'v3_recaptcha'            => esc_html__( 'reCAPTCHA V3', 'defender-security' ),
			'language'                => esc_html__( 'Language', 'defender-security' ),
			'message'                 => esc_html__( 'Error Message', 'defender-security' ),
			'locations'               => esc_html__( 'CAPTCHA Locations', 'defender-security' ),
			'detect_woo'              => esc_html__( 'WooCommerce', 'defender-security' ),
			'detect_buddypress'       => esc_html__( 'BuddyPress', 'defender-security' ),
			'disable_for_known_users' => esc_html__( 'Disable for logged in users', 'defender-security' ),
		);
	}

	/**
	 * Validates the form after submission.
	 *
	 * @return void
	 */
	protected function after_validate(): void {
		// Case with multi errors.
		if ( $this->is_unchecked_woo_locations() && $this->is_unchecked_buddypress_locations() ) {
			// The text of the notation is only in the first key, but we add the number of keys depending on the disabled locations of the plugins.
			$this->errors['enable_woo']        = esc_html__(
				'You have enabled reCaptcha for more than one plugin. Please select at least one form location for each plugin and click Save Changes again.',
				'defender-security'
			);
			$this->errors['enable_buddypress'] = '';
		} else {
			// Individual cases with plugins.
			if ( $this->is_unchecked_woo_locations() ) {
				$this->errors['enable_woo'] = esc_html__(
					'reCAPTCHA for WooCommerce is enabled, but no WooCommerce forms are selected. Please select at least one WooCommerce form location and then click Save Changes again.',
					'defender-security'
				);
			}
			if ( $this->is_unchecked_buddypress_locations() ) {
				$this->errors['enable_buddypress'] = esc_html__(
					'reCAPTCHA for BuddyPress is enabled, but no BuddyPress forms are selected. Please select at least one BuddyPress form location and then click Save Changes again.',
					'defender-security'
				);
			}
		}
	}

	/**
	 * Disable for logged in users or enable.
	 *
	 * @return bool
	 */
	public function display_for_known_users(): bool {
		return ! ( $this->disable_for_known_users && is_user_logged_in() );
	}

	/**
	 * Get module name.
	 *
	 * @return string
	 */
	public static function get_module_name(): string {
		return esc_html__( 'CAPTCHA', 'defender-security' );
	}
}
