<?php

namespace SabaiApps\Directories\Component\Captcha\Api;

use SabaiApps\Directories\Component\Form\Form;
use SabaiApps\Directories\Exception;

class HCaptchaApi extends AbstractApi {

	protected static $_jsLoaded, $_preRenderAdded = array();

	protected function _captchaApiInfo() {
		return array(
			'label'            => __( 'hCaptcha', 'directories-pro' ),
			'default_settings' => array(
				'sitekey' => null,
				'secret'  => null,
				'size'    => 'normal',
				'theme'   => 'light',
			),
			'weight'           => 3,
		);
	}

	public function captchaApiSettingsForm( array $settings, array $parents ) {
		return array(
			'sitekey' => array(
				'#title'         => __( 'Site key', 'directories-pro' ),
				'#type'          => 'textfield',
				'#default_value' => $settings['sitekey'],
				'#horizontal'    => true,
			),
			'secret'  => array(
				'#title'         => __( 'Secret key', 'directories-pro' ),
				'#type'          => 'textfield',
				'#default_value' => $settings['secret'],
				'#horizontal'    => true,
			),
			'size'    => array(
				'#type'          => 'select',
				'#title'         => __( 'Display size', 'directories-pro' ),
				'#options'       => array(
					'normal'  => __( 'Normal', 'directories-pro' ),
					'compact' => __( 'Compact', 'directories-pro' ),
				),
				'#default_value' => $settings['size'],
				'#horizontal'    => true,
			),
			'theme'   => array(
				'#type'          => 'select',
				'#title'         => __( 'Theme', 'directories-pro' ),
				'#options'       => array(
					'light' => __( 'Light', 'directories-pro' ),
					'dark'  => __( 'Dark', 'directories-pro' ),
				),
				'#default_value' => $settings['theme'],
				'#horizontal'    => true,
			),
		);
	}

	public function captchaApiForm( array $settings, $name, $trigger = null ) {
		if ( ! self::$_jsLoaded ) {
			$js     = sprintf(
				'var drtsHCaptchaCallback = function() {
    if (typeof(hcaptcha) === "undefined"
        || typeof(hcaptcha.render) === "undefined"
    ) return;
    jQuery(".drts-captcha-hcaptcha-form-field").each(function(i) {
        var $this = jQuery(this), options = {
            sitekey: "%s",
            size: $this.data("size"),
            theme: $this.data("theme")
        };
        $this.data("hcaptcha-widget-id", hcaptcha.render($this.attr("id"), options))
            .find("textarea").attr("name", $this.attr("id"));
    });
};',
				$this->_application->H( $settings['sitekey'] )
			);
			$locale = $this->_application->getPlatform()->getLocale();
			if ( strpos( $locale, '_' ) ) {
				$locale = explode( '_', $locale );
				$locale = in_array( $locale[0], array( 'zh', 'pt' ) ) ? $locale[0] . '-' . $locale[1] : $locale[0];
			}
			$this->_application->getPlatform()->addHead(
				'<script type="text/javascript">' . $js . '</script>
<script type="text/javascript" src="https://js.hcaptcha.com/1/api.js?onload=drtsHCaptchaCallback&render=explicit&recaptchacompat=off&hl=' . $locale . '" async defer></script>',
				'captcha_hcaptcha'
			);
			self::$_jsLoaded = true;
		}

		$id = 'drts-captcha-hcaptcha-form-field-' . $name;
		return array(
			'#type'             => 'item',
			'#element_validate' => array(
				array( array( $this, '_validateCaptcha' ), array( $settings['secret'], $trigger, $id ) ),
			),
			'#markup'           => '<div class="drts-captcha-hcaptcha-form-field" id="' . $id . '" data-size="' . $this->_application->H( $settings['size'] ) . '"'
				. ' data-theme="' . $this->_application->H( $settings['theme'] ) . '"' . ' data-trigger="' . $this->_application->H( $trigger ) . '"></div>',
		);
	}

	public function _validateCaptcha( Form $form, &$value, $element, $secret, $trigger, $id ) {
		if ( isset( $trigger ) && ! $form->getValue( $trigger ) ) {
			return;
		}

		if ( ! isset( $_POST[ $id ] )
			|| ! strlen( $_POST[ $id ] )
		) {
			if ( isset( $element ) ) {
				$form->setError( __( 'Please fill out this field.', 'directories-pro' ), $element );
			} else {
				$form->setError( __( 'Failed validating captcha value.', 'directories-pro' ) );
			}
		} elseif ( true !== $result = $this->_verify( $secret, $_POST[ $id ] ) ) {
				$error = $result['error'];
			if ( ! empty( $result['error_codes'] ) ) {
				$error .= ' (' . implode( ',', $result['error_codes'] ) . ')';
			}
				$form->setError( $error, isset( $element ) ? $element : '' );
		}
		if ( empty( self::$_preRenderAdded[ $form->settings['#id'] ] ) ) {
			$form->settings['#pre_render'][]                 = array( $this, '_preRenderCallback' );
			self::$_preRenderAdded[ $form->settings['#id'] ] = true;
		}
	}

	public function _preRenderCallback( $form ) {
		if ( $form->hasError() ) {
			$form->settings['#js_ready'][] = 'drtsHCaptchaCallback();';
		}
	}

	protected function _verify( $secret, $value ) {
		if ( empty( $secret ) ) {
			throw new Exception\InvalidArgumentException( __( 'Invalid captcha secret key.', 'directories-pro' ) );
		}

		if ( empty( $value ) ) {
			throw new Exception\InvalidArgumentException( __( 'Invalid captcha value.', 'directories-pro' ) );
		}

		$url = 'https://hcaptcha.com/siteverify?' . http_build_query(
			array(
				'secret'   => $secret,
				'response' => $value,
				'remoteip' => $this->_getIp(),
			)
		);
		if ( ( ! $json = $this->_application->getPlatform()->remoteGet( $url ) )
			|| ( ! $response = json_decode( $json, true ) )
		) {
			$this->_application->logError( 'Failed obtaining valid hCaptcha verify response from ' . $url );
			throw new Exception\RuntimeException( __( 'Failed obtaining valid hCaptcha verify response.', 'directories-pro' ) );
		}

		// Check response
		if ( empty( $response['success'] ) ) {
			$error = __( 'Unknown error.', 'directories-pro' );
			if ( ! empty( $response['error-codes'] ) ) {
				switch ( $response['error-codes'][0] ) {
					case 'missing-input-secret':
					case 'invalid-input-secret':
						$error = __( 'Invalid or missing secret parameter.', 'directories-pro' );
						break;
					case 'missing-input-response':
					case 'invalid-input-response':
						$error = __( 'Invalid or missing response parameter', 'directories-pro' );
						break;
					case 'bad-request':
						$error = __( 'Invalid or malformed request.', 'directories-pro' );
						break;
				}
			}
			return array(
				'error'       => $error,
				'error_codes' => empty( $response['error-codes'] ) ? array() : $response['error-codes'],
			);
		}

		return true;
	}
}
