<?php

class HappyForms_Integration_Stripe {

	private static $instance;

	private $authorize_payment_nonce = 'happyforms_ajax_stripe_authorize_payment_nonce';
	private $intent;
	private $response_id;

	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}

		self::$instance->hook();

		return self::$instance;
	}

	public function hook() {
		add_action( 'happyforms_submission_success', array( $this, 'do_checkout' ), 10, 3 );
		add_filter( 'happyforms_payment_part_has_credit_card', array( $this, 'part_has_credit_card' ), 10, 3 );
		add_action( 'happyforms_part_input_after', array( $this, 'add_stripe_controls' ), 10, 2 );
		add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
		add_action( 'wp_ajax_happyforms_stripe_authorize_payment', array( $this, 'authorize_payment' ) );
		add_action( 'wp_ajax_nopriv_happyforms_stripe_authorize_payment', array( $this, 'authorize_payment' ) );
		add_action( 'happyforms_payment_the_transaction_details', array( $this, 'do_transaction_details' ) );

		add_filter( 'happyforms_messages_fields', array( $this, 'meta_messages_fields' ) );
		add_filter( 'happyforms_messages_controls', array( $this, 'get_message_controls' ) );
	}

	/**
	 * Check if form has Stripe integration enabled.
	 *
	 * @param array $form Form data.
	 *
	 * @return boolean
	 */
	public function form_has_stripe( $form ) {
		$part = happyforms_get_form_controller()->get_first_part_by_type( $form, 'payments' );
		$service = happyforms_get_integrations()->get_service( 'stripe' );
		$has_stripe = $part && $service->is_connected();

		return $has_stripe;
	}

	/**
	 * Adds Stripe controls to the Payment part.
	 *
	 * @hooked `happyforms_part_input_after`
	 *
	 * @param array $part Part data.
	 * @param array $form Form data.
	 *
	 * @return void
	 */
	public function add_stripe_controls( $part, $form ) {
		if ( 'payments' !== $part['type'] ) {
			return;
		}

		require( happyforms_get_integrations_folder() . '/services/stripe/templates/frontend.php' );
	}

	/**
	 * Adjust value of `happyforms_payment_part_has_credit_card` function through the filter. This function
	 * is used to check if Payment part has credit card fields (and supports credit card checkout).
	 *
	 * @hooked filter `happyforms_payment_part_has_credit_card`
	 *
	 * @param boolean $has_credit_card Value at the time of calling this method.
	 * @param array   $part            Part data.
	 * @param array   $form            Form data.
	 *
	 * @return boolean
	 */
	public function part_has_credit_card( $has_credit_card, $part, $form ) {
		$service = happyforms_get_integrations()->get_service( 'stripe' );
		$has_credit_card = $service->is_connected();

		return $has_credit_card;
	}

	public function script_dependencies( $deps, $forms ) {
		$has_stripe = false;
		$form_controller = happyforms_get_form_controller();

		foreach ( $forms as $form ) {
			if ( $this->form_has_stripe( $form ) ) {
				$has_stripe = true;
				break;
			}
		}

		if ( ! happyforms_is_preview() && ! $has_stripe ) {
			return $deps;
		}

		wp_register_script(
			'stripe-v3',
			'https://js.stripe.com/v3/',
			array()
		);

		wp_register_script(
			'happyforms-cookies',
			happyforms_get_plugin_url() . 'inc/assets/js/lib/js.cookie.js',
			array(), HAPPYFORMS_UPGRADE_VERSION, true
		);

		wp_register_script(
			'happyforms-integration-stripe',
			happyforms_get_plugin_url() . 'integrations/services/stripe/assets/js/stripe.js',
			array( 'jquery', 'happyforms-cookies', 'stripe-v3' ), HAPPYFORMS_VERSION, true
		);

		$service = happyforms_get_integrations()->get_service( 'stripe' );
		$stripe_locale = apply_filters( 'happyforms_payment_stripe_locale', 'en' );

		wp_localize_script(
			'happyforms-integration-stripe',
			'_happyFormsStripeSettings',
			array(
				'key' => $service->get_publishable_key(),
				'locale' => $stripe_locale,
				'hidePostalCode' => apply_filters( 'happyforms_payment_stripe_hide_postal_code', true ),
				'ajaxurl' => admin_url( 'admin-ajax.php' ),
				'nonce' => wp_create_nonce( $this->authorize_payment_nonce )
			)
		);

		$deps[] = 'happyforms-integration-stripe';

		return $deps;
	}

	/**
	 * Check if payment succeeded and update payment information in submission and display success or error message.
	 *
	 * @hooked action `wp_ajax_happyforms_stripe_authorize_payment`
	 * @hooked action `wp_ajax_nopriv_happyforms_stripe_authorize_payment`
	 *
	 * @return void
	 */
	public function authorize_payment() {
		check_ajax_referer( $this->authorize_payment_nonce, 'nonce' );

		if ( ! isset( $_REQUEST['form_id'] ) || ! isset( $_REQUEST['success'] ) || ! isset( $_REQUEST['response_id'] ) ) {
			wp_send_json_error();
		}

		$form_id = intval( $_REQUEST['form_id'] );
		$response_id = intval( $_REQUEST['response_id'] );
		$intent_id = esc_attr( $_REQUEST['intent_id'] );
		$success = ( 'true' === $_REQUEST['success'] ) ? true : false;

		$form = happyforms_get_form_controller()->get( $form_id );

		if ( $success ) {
			$message = $form['payment_completed'];
			happyforms_get_payments_integration()->append_response_transaction( $response_id, 'stripe', 'confirmed', $intent_id );
		} else {
			$message = $form['payment_failed'];
			happyforms_get_payments_integration()->append_response_transaction( $response_id, 'stripe', 'cancelled', $intent_id );
		}

		$notices = array(
			$form_id => array(
				'type' => ( $success ) ? 'success' : 'error',
				'message' => $message
			)
		);

		ob_start();
			happyforms_the_message_notices( $notices, '' );
		$data = ob_get_clean();

		wp_send_json_success( $data );
	}

	/**
	 * Process Stripe payment.
	 *
	 * @hooked action `happyforms_submission_success`
	 *
	 * @param array $submission Submission data.
	 * @param array $form       Form data.
	 * @param array $response   Activity entry data.
	 *
	 * @return void
	 */
	public function do_checkout( $submission, $form, $response ) {
		if ( ! $this->form_has_stripe( $form ) ) {
			return;
		}

		$form_controller = happyforms_get_form_controller();
		$part = $form_controller->get_first_part_by_type( $form, 'payments' );
		$part_name = happyforms_get_part_name( $part, $form );
		$value = maybe_unserialize( $response['request'][$part_name] );
		$notices = happyforms_get_session()->get_messages( $form['ID'] );

		if ( 'stripe' !== $value['payment_method'] ) {
			return;
		}

		$form_id = $form['ID'];
		$cookie = $_COOKIE["happyforms_{$form_id}_stripe_checkout"];

		if ( ! isset( $cookie ) ) {
			return;
		}

		/**
		 * The cookie that's read and parsed here contains details on payment method used for creating
		 * Stripe payment intent and charge.
		 */
		$cookie = json_decode( wp_unslash( $cookie ) );

		if ( ! $cookie->payment_method ) {
			return;
		}

		// This is the ID of Activity entry.
		$this->response_id = $response['ID'];

		happyforms_get_session()->remove_notice( $form['ID'] );
		happyforms_get_session()->add_notice( $form['ID'], $form['stripe_processing_hint'] );

		$amount = (int) $value['price'];
		$currencies = happyforms_payment_get_currencies( 'stripe' );
		$currency = $part['currency'];

		// Validate that the currency specified is in the list of supported currencies.
		if ( ! isset( $currencies[$currency] ) ) {
			return;
		}

		/**
		 * If currency format is set to `float`, it means we're passing amount of cents (or the smallest entity of currency)
		 * to Stripe API, so we're multiplying amount by 100.
		 */
		if ( 'float' === $currencies[$currency]['format'] ) {
			$amount = (float) $value['price'];
			$amount = $amount * 100;
		}

		$secret_key = happyforms_get_integrations()->get_service( 'stripe' )->get_secret_key();
		$description = happyforms_get_payments_integration()->get_transaction_description( $form, $submission );
		$transaction = array(
			'amount' => $amount,
			'currency' => $part['currency'],
			'description' => substr( $description, 0, 1000 ),
			'payment_method_types' => array( 'card' ),
		);

		// Allow user customization
		$transaction = apply_filters( 'happyforms_stripe_transaction', $transaction );

		// Create payment intent
		$request = wp_remote_post(
			'https://api.stripe.com/v1/payment_intents',
			array(
				'headers' => array(
					'Authorization' => "Bearer {$secret_key}",
				),
				'body' => $transaction,
			)
		);

		$response = wp_remote_retrieve_body( $request );

		if ( ! $response ) {
			return;
		}

		$response_json = json_decode( $response );

		if ( ! $response_json->id || ! $response_json->client_secret ) {
			return;
		}

		$this->intent = $response_json;

		happyforms_get_payments_integration()->append_response_transaction( $this->response_id, 'stripe', 'pending', $this->intent->id );

		// Update JSON response with Stripe response data for further processing in JS.
		add_filter( 'happyforms_json_response', array( $this, 'update_response' ), 10, 3 );
	}

	/**
	 * Update form response data on successful payment with Stripe response.
	 *
	 * @param array $response   Form response data.
	 * @param array $submission Submission data.
	 * @param array $form       Form data.
	 *
	 * @return array Response with Stripe data included.
	 */
	public function update_response( $response, $submission, $form ) {
		$response['stripe'] = array(
			'intent' => array(
				'id' => $this->intent->id,
				'secret' => $this->intent->client_secret
			),
			'response_id' => $this->response_id
		);

		return $response;
	}

	public function do_transaction_details( $details ) {
		$gateway = $details['gateway'];
		$status = $details['status'];
		$transaction_id = $details['transaction_id'];

		if ( 'stripe' !== $gateway ) {
			return;
		}
		?>
		<br>
		<?php _e( 'Status', 'happyforms' ); ?>: <?php echo $status; ?>
		<?php _e( 'Transaction ID', 'happyforms' ); ?>:
		<?php echo $transaction_id; ?> (<a href="https://dashboard.stripe.com/payments/<?php echo $transaction_id; ?>" target="_blank"><?php _e( 'See details', 'happyforms' ); ?></a>)
		<?php
	}

	public function meta_messages_fields( $fields ) {
		$fields = array_merge( $fields, $this->get_messages_fields() );

		return $fields;
	}

	public function get_messages_fields() {
		$fields = array(
			'stripe_processing_hint' => array(
				'default' => __( 'Please wait while we process your payment.', 'happyforms' ),
				'sanitize' => 'sanitize_text_field',
			),
			'stripe_option_label' => array(
				'default' => __( 'Credit card', 'happyforms' ),
				'sanitize' => 'sanitize_text_field',
			),
			'card_number_label' => array(
				'default' => __( 'Card number', 'happyforms' ),
				'sanitize' => 'sanitize_text_field',
			),
		);

		return $fields;
	}

	public function get_message_controls( $controls ) {
		$controls[6160] = array(
			'type' => 'text',
			'label' => __( 'Stripe is processing payment', 'happyforms' ),
			'field' => 'stripe_processing_hint',
		);
		$controls[6162] = array(
			'type' => 'text',
			'label' => __( 'Stripe payment', 'happyforms' ),
			'field' => 'stripe_option_label',
		);
		$controls[6163] = array(
			'type' => 'text',
			'label' => __( 'Card number', 'happyforms' ),
			'field' => 'card_number_label',
		);

		return $controls;
	}

}

if ( ! function_exists( 'happyforms_get_stripe_integration' ) ):

function happyforms_get_stripe_integration() {
	return HappyForms_Integration_Stripe::instance();
}

endif;

happyforms_get_stripe_integration();
