<?php
/**
 * Stripe PaymentIntent
 *
 * @package SimplePay\Core\Payments\PaymentIntent
 * @copyright Copyright (c) 2022, Sandhills Development, LLC
 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since 3.6.0
 */

namespace SimplePay\Core\Payments\PaymentIntent;

use SimplePay\Core\API;
use SimplePay\Core\Payments\Stripe_API;
use SimplePay\Core\Legacy;
use SimplePay\Vendor\Stripe\Coupon;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Retrieves a PaymentIntent.
 *
 * @since 3.8.0
 *
 * @param string|array $payment_intent Payment Intent ID or {
 *   Arguments used to retrieve a PaymentIntent.
 *
 *   @type string $id Payment Intent ID.
 * }
 * @param array        $api_request_args {
 *   Additional request arguments to send to the Stripe API when making a request.
 *
 *   @type string $api_key API Secret Key to use.
 * }
 * @return \SimplePay\Vendor\Stripe\PaymentIntent
 */
function retrieve( $payment_intent, $api_request_args = array() ) {
	if ( false === is_array( $payment_intent ) ) {
		$payment_intent_args = array(
			'id' => $payment_intent,
		);
	} else {
		$payment_intent_args = $payment_intent;
	}

	return Stripe_API::request(
		'PaymentIntent',
		'retrieve',
		$payment_intent_args,
		$api_request_args
	);
}

/**
 * Retrieves PaymentIntents.
 *
 * @since 3.8.0
 *
 * @param array $payment_intents Optional arguments used when listing PaymentIntents.
 * @param array $api_request_args {
 *   Additional request arguments to send to the Stripe API when making a request.
 *
 *   @type string $api_key API Secret Key to use.
 * }
 * @return object
 */
function all( $payment_intents = array(), $api_request_args = array() ) {
	return Stripe_API::request(
		'PaymentIntent',
		'all',
		$payment_intents,
		$api_request_args
	);
}

/**
 * Creates a PaymentIntent.
 *
 * @since 3.6.0
 *
 * @param array $paymentintent_args Arguments used to create a PaymentIntent.
 * @param array $api_request_args {
 *   Additional request arguments to send to the Stripe API when making a request.
 *
 *   @type string $api_key API Secret Key to use.
 * }
 * @return \SimplePay\Vendor\Stripe\PaymentIntent
 */
function create( $paymentintent_args, $api_request_args = array() ) {
	$defaults           = array();
	$paymentintent_args = wp_parse_args( $paymentintent_args, $defaults );

	/**
	 * Filter the arguments used to generate a PaymentIntent.
	 *
	 * @since 3.6.0
	 *
	 * @param array $payment_intent_args Arguemnts used to generate a PaymentIntent.
	 */
	$paymentintent_args = apply_filters( 'simpay_create_paymentintent_args', $paymentintent_args );

	/**
	 * Allows processing before a PaymentIntenet is created.
	 *
	 * @since 3.6.0
	 *
	 * @param array $paymentintent_args Arguments used to create a PaymentIntent.
	 */
	do_action( 'simpay_before_paymentintent_created', $paymentintent_args );

	// Create PaymentIntent.
	$paymentintent = Stripe_API::request(
		'PaymentIntent',
		'create',
		$paymentintent_args,
		$api_request_args
	);

	/**
	 * Allows further processing after a PaymentIntent has been created.
	 *
	 * @since 3.6.0
	 *
	 * @param \SimplePay\Vendor\Stripe\PaymentIntent $paymentintent PaymentIntent.
	 */
	do_action( 'simpay_after_paymentintent_created', $paymentintent );

	return $paymentintent;
}

/**
 * Confirms a PaymentIntent.
 *
 * @since 3.6.0
 *
 * @param string $paymentintent_id PaymentIntent ID to confirm.
 * @param array  $api_request_args {
 *   Additional request arguments to send to the Stripe API when making a request.
 *
 *   @type string $api_key API Secret Key to use.
 * }
 * @return \SimplePay\Vendor\Stripe\PaymentIntent
 */
function confirm( $paymentintent_id, $api_request_args = array() ) {
	$paymentintent = Stripe_API::request(
		'PaymentIntent',
		'retrieve',
		$paymentintent_id,
		$api_request_args
	);

	$paymentintent->confirm();

	return $paymentintent;
}

/**
 * Uses the current payment form request to generate arguments for a PaymentIntent.
 *
 * @param SimplePay\Core\Abstracts\Form $form Form instance.
 * @param array                         $form_data Form data generated by the client.
 * @param array                         $form_values Values of named fields in the payment form.
 * @param int                           $customer_id Stripe Customer ID.
 * @return array
 */
function get_args_from_payment_form_request(
	$form,
	$form_data,
	$form_values,
	$customer_id
) {
	// Retrieve price option.
	$price = simpay_payment_form_prices_get_price_by_id(
		$form,
		$form_data['price']['id']
	);

	if ( false === $price ) {
		throw new \Exception(
			__( 'Unable to locate payment form price.', 'stripe' )
		);
	}

	$payment_args = get_payment_args_from_form_request(
		$form,
		$form_data,
		$form_values,
		$customer_id,
		$price
	);

	$paymentintent_args = array(
		'amount'      => $payment_args['amount'],
		'currency'    => $payment_args['currency'],
		'metadata'    => array_merge(
			array(
				'simpay_form_id' => $form->id,
			),
			Legacy\Hooks\simpay_payment_metadata(
				$form,
				$form_data,
				$form_values,
				$customer_id
			)
		),
	);

	// Use price option label if one is set.
	if ( null !== $price->label ) {
		$paymentintent_args['description'] = $price->get_display_label();

		// Fall back to Payment Form title if set.
		// This is a change in behavior in 4.1, but matches the Stripe Checkout
		// usage that falls back to the Product title (Payment Form title).
	} else {
		if ( ! empty( $form->company_name ) ) {
			$paymentintent_args['description'] = $form->company_name;
		}
	}

	if ( ! empty( $form->statement_descriptor ) ) {
		$paymentintent_args['statement_descriptor'] = $form->statement_descriptor;
	}

	// Legacy filter.
	$paymentintent_args = Legacy\Hooks\simpay_stripe_charge_args(
		$paymentintent_args,
		$form,
		$form_values
	);

	/**
	 * Filters arguments used to create a PaymentIntent from a payment form request.
	 *
	 * @since 3.6.0
	 *
	 * @param array                         $paymentintent_args Arguments for PaymentIntent.
	 * @param SimplePay\Core\Abstracts\Form $form Form instance.
	 * @param array                         $form_data Form data generated by the client.
	 * @param array                         $form_values Values of named fields in the payment form.
	 * @param int                           $customer_id Stripe Customer ID.
	 * @return array
	 */
	$paymentintent_args = apply_filters(
		'simpay_get_paymentintent_args_from_payment_form_request',
		$paymentintent_args,
		$form,
		$form_data,
		$form_values,
		$customer_id
	);

	return $paymentintent_args;
}

/**
 * Uses the current payment form request to generate relevant arguments for a payment.
 *
 * This currently includes `amount` and `currency`.
 *
 * @since 4.1.0
 *
 * @param SimplePay\Core\Abstracts\Form           $form Form instance.
 * @param array                                   $form_data Form data generated by the client.
 * @param array                                   $form_values Values of named fields in the Payment Porm.
 * @param int                                     $customer_id Stripe Customer ID.
 * @param \SimplePay\Core\PaymentForm\PriceOption $price Price option.
 * @return array
 */
function get_payment_args_from_form_request(
	$form,
	$form_data,
	$form_values,
	$customer_id,
	$price
) {
	// Custom amount. Verify minimum amount.
	if ( false === simpay_payment_form_prices_is_defined_price( $price->id ) ) {
		// Ensure custom amount meets minimum requirement.
		$unit_amount = $form_data['customAmount'];

		if ( $unit_amount < $price->unit_amount_min ) {
			$unit_amount = $price->unit_amount_min;
		}
	} else {
		$unit_amount = $price->unit_amount;
	}

	// Backwards compatibility amount filter.
	if ( has_filter( 'simpay_form_' . $form->id . '_amount' ) ) {
		$unit_amount = simpay_get_filtered(
			'amount',
			simpay_convert_amount_to_dollars( $unit_amount ),
			$form->id
		);

		$unit_amount = simpay_convert_amount_to_cents( $unit_amount );
	}

	$currency = $price->currency;

	// Backwards compatibility currency filter.
	if ( has_filter( 'simpay_form_' . $form->id . '_currency' ) ) {
		$currency = simpay_get_filtered(
			'currency',
			strtolower( $currency ),
			$form->id
		);
	}

	// Calculate quantity.
	$quantity = isset( $form_values['simpay_quantity'] )
		? intval( $form_values['simpay_quantity'] )
		: 1;

	$unit_amount = $unit_amount * $quantity;

	// Calculate coupon discount.
	if ( isset( $form_data['coupon'] ) && false !== $form_data['coupon'] ) {
		// Validate the coupon by ensuring it is attached to the Customer.
		$customer = API\Customers\retrieve(
			$customer_id,
			$form->get_api_request_args()
		);

		$coupon = $customer->discount ? $customer->discount->coupon : false;

		if ( $coupon instanceof Coupon ) {
			if ( $coupon->amount_off ) {
				$unit_amount = $unit_amount - $coupon->amount_off;
			} else {
				$percent_off = $coupon->percent_off / 100;
				$unit_amount = $unit_amount - ( $unit_amount * $percent_off );
			}

			$unit_amount = round( $unit_amount );
		}
	}

	// Add taxes if available. Done in Lite to avoid introducing a filter.
	if ( function_exists( 'simpay_get_payment_form_tax_rates' ) ) {

		// Legacy tax percentage.
		if ( has_filter( 'simpay_form_' . $form->id . '_tax_percent' ) ) {
			$tax_percent = floatval(
				simpay_get_filtered(
					'tax_percent',
					0,
					$form->id
				)
			);

			if ( 0 !== $tax_percent ) {
				$unit_amount = round(
					$unit_amount + ( $unit_amount * ( $tax_percent / 100 ) )
				);
			}

			// Tax rates.
		} else {
			$tax_rates = simpay_get_payment_form_tax_rates( $form );

			if ( ! empty( $tax_rates ) ) {
				// Remove inclusive tax amount.
				$inclusive_tax_amount = array_reduce(
					$tax_rates,
					function( $amount, $tax_rate ) use ( $unit_amount ) {
						if ( 'exclusive' === $tax_rate->calculation ) {
							return $amount;
						}

						return $amount + ( $unit_amount * ( $tax_rate->percentage / 100 ) );
					},
					0
				);

				$post_inclusive_unit_amount = round( $unit_amount - $inclusive_tax_amount );

				$tax = array_reduce(
					$tax_rates,
					function( $tax, $tax_rate ) use ( $post_inclusive_unit_amount ) {
						if ( 'inclusive' === $tax_rate->calculation ) {
							return $tax;
						}

						$tax_rate = $tax_rate->percentage / 100;

						return $tax + ( $post_inclusive_unit_amount * $tax_rate );
					},
					0
				);

				$unit_amount = round( $unit_amount + $tax );
			}
		}

	}

	return array(
		'amount'   => round( $unit_amount ),
		'currency' => $currency,
	);
}

/**
 * Sets the customer's default payment method to the card used to make the payment.
 *
 * Only applies to card payments made on-site. This is to maintain backwards compatibility
 * with previous versions that force-set the default payment method on customer creation.
 *
 * @since 4.4.4
 *
 * @param \SimplePay\Vendor\Stripe\Event         $event Webhook event.
 * @param \SimplePay\Vendor\Stripe\PaymentIntent $paymentintent PaymentIntent.
 * @return void
 */
function set_card_default_payment_method( $event, $payment_intent ) {
	$form_id = isset( $payment_intent->metadata->simpay_form_id )
		? $payment_intent->metadata->simpay_form_id
		: 0;
	$form    = simpay_get_form( $form_id );

	if ( false === $form ) {
		return;
	}

	$pm_types = $payment_intent->payment_method_types;

	if (
		'stripe_checkout' === $form->get_display_type() ||
		! in_array( 'card', $pm_types, true )
	) {
		return;
	}

	API\Customers\update(
		$payment_intent->customer->id,
		array(
			'invoice_settings' => array(
				'default_payment_method' => $payment_intent->payment_method,
			),
		),
		$form->get_api_request_args()
	);
}
add_action(
	'simpay_webhook_payment_intent_succeeded',
	__NAMESPACE__ . '\\set_card_default_payment_method',
	5,
	2
);
