<?php
/**
 * Cod Gateway helper functions.
 *
 * @package cartflows
 */

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

/**
 * Class Cartflows_Pro_Paypal_Gateway_Helper .
 */
class Cartflows_Pro_Paypal_Gateway_Helper extends Cartflows_Pro_Gateway {

	/**
	 * Live API URL.
	 *
	 * @var live_api
	 */
	public $live_api = 'https://api-3t.paypal.com/nvp';

	/**
	 * Test API URL.
	 *
	 * @var test_api
	 */
	public $test_api = 'https://api-3t.sandbox.paypal.com/nvp';

	/**
	 * Gateway parameters.
	 *
	 * @var parameters
	 */
	public $parameters = array();

	/**
	 * Add single parameter.
	 *
	 * @param string $key key.
	 * @param string $value value.
	 */
	public function add_parameter( $key, $value ) {
		$this->parameters[ $key ] = $value;
	}

	/**
	 * Add multiple parameters.
	 *
	 * @param array $params parameters.
	 */
	public function add_parameters( array $params ) {
		foreach ( $params as $key => $value ) {
			$this->add_parameter( $key, $value );
		}
	}

	/**
	 * Add payment parameters
	 *
	 * @param array $params parameters.
	 *
	 * @since 1.0.0
	 */
	private function add_payment_parameters( array $params ) {
		foreach ( $params as $key => $value ) {
			$this->add_parameter( "PAYMENTREQUEST_0_{$key}", $value );
		}
	}

	/**
	 * Set the method for request
	 *
	 * @param string $method request method param.
	 *
	 * @since 1.0.0
	 */
	public function set_method( $method ) {
		$this->add_parameter( 'METHOD', $method );
	}

	/**
	 * Update payment params
	 *
	 * @since 1.0.0
	 *
	 * @param WC_Order $order order object.
	 * @param int      $step_id step ID.
	 * @param string   $type transaction type.
	 * @param bool     $use_deprecated_params whether to use deprecated PayPal?.
	 * @param bool     $is_offer_charge is offer charge.
	 * @param int      $variation_id variation id.
	 * @param int      $input_qty input qty.
	 */
	public function add_payment_params( WC_Order $order, $step_id, $type, $use_deprecated_params = false, $is_offer_charge = false, $variation_id = '', $input_qty = '' ) {

		$calculated_total = 0;
		$order_subtotal   = 0;
		$item_count       = 0;
		$order_items      = array();
		$offer_package    = array();

		if ( true === $is_offer_charge ) {

			$offer_package = wcf_pro()->utils->get_offer_data( $step_id, $variation_id, $input_qty, $order->get_id() );

			if ( $offer_package ) {

				$order_items[] = array(
					'NAME'    => $offer_package['name'],
					'DESC'    => $offer_package['desc'],
					'AMT'     => $this->round( $offer_package['unit_price_tax'] ),
					'QTY'     => $offer_package['qty'],
					'ITEMURL' => $offer_package['url'],
				);

				$order_subtotal += $offer_package['total_unit_price_amount'];

				// Update the total if need to replace the main order. Need to update the values to prevent the cart total do not match error.
				$is_cancal_main_order = get_post_meta( $step_id, 'wcf-replace-main-order', true );
				if ( 'yes' === $is_cancal_main_order && $offer_package['cancal_main_order'] ) {
					$order_subtotal        = $offer_package['amount_diff'];
					$order_items[0]['AMT'] = $offer_package['amount_diff'];
				}
			}
		} else {

			// Add line items.
			foreach ( $order->get_items() as $item ) {

				$product = new WC_Product( $item['product_id'] );

				$order_items[] = array(
					'NAME'    => $product->get_title(),
					'DESC'    => $this->get_item_description( $item, $product, $is_offer_charge ),
					'AMT'     => $this->round( $order->get_item_subtotal( $item ) ),
					'QTY'     => ( ! empty( $item['qty'] ) ) ? absint( $item['qty'] ) : 1,
					'ITEMURL' => $product->get_permalink(),
				);

				$order_subtotal += $item['line_total'];
			}

			foreach ( $order->get_fees() as $fee ) {

				$order_items[] = array(
					'NAME' => ( $fee['name'] ),
					'AMT'  => $this->round( $fee['line_total'] ),
					'QTY'  => 1,
				);

				$order_subtotal += $fee['line_total'];
			}

			if ( $order->get_total_discount() > 0 ) {

				$order_items[] = array(
					'NAME' => __( 'Total Discount', 'cartflows-pro' ),
					'QTY'  => 1,
					'AMT'  => - $this->round( $order->get_total_discount() ),
				);
			}
		}

		if ( true === $is_offer_charge ) {

			/** Handle paypal data setup for the offers */

			/**
			 * Code for reference transaction
			 */
			$total_amount = $offer_package['total'];

			$item_names = array();

			foreach ( $order_items as $item ) {
				$item_names[] = sprintf( '%1$s x %2$s', $item['NAME'], $item['QTY'] );
			}
			$item_count = 0;

			// Add individual order items.
			foreach ( $order_items as $item ) {
				$this->add_litem_params( $item, $item_count ++, $use_deprecated_params );
			}

			if ( $use_deprecated_params ) {

				$this->add_parameters(
					array(
						'AMT'              => $total_amount,
						'CURRENCYCODE'     => wcf_pro()->wc_common->get_currency( $order ),
						'ITEMAMT'          => $this->round( $order_subtotal ),
						'SHIPPINGAMT'      => $offer_package['shipping_fee_tax'],
						'INVNUM'           => $this->wc_gateway()->get_option( 'invoice_prefix' ) . wcf_pro()->wc_common->get_order_id( $order ) . '_' . $offer_package['step_id'],
						'PAYMENTACTION'    => $type,
						'PAYMENTREQUESTID' => wcf_pro()->wc_common->get_order_id( $order ),
						'TAXAMT'           => 0,
						'CUSTOM'           => wp_json_encode(
							array(
								'_wcf_order_id' => wcf_pro()->wc_common->get_order_id( $order ),
							)
						),
					)
				);
			} else {

				$this->add_payment_parameters(
					array(
						'AMT'              => $total_amount,
						'CURRENCYCODE'     => wcf_pro()->wc_common->get_currency( $order ),
						'ITEMAMT'          => $this->round( $order_subtotal ),
						'SHIPPINGAMT'      => $offer_package['shipping_fee_tax'],
						'INVNUM'           => $this->wc_gateway()->get_option( 'invoice_prefix' ) . wcf_pro()->wc_common->get_order_id( $order ) . '_' . $offer_package['step_id'],
						'PAYMENTACTION'    => $type,
						'PAYMENTREQUESTID' => wcf_pro()->wc_common->get_order_id( $order ),
						'TAXAMT'           => 0,
						'CUSTOM'           => wp_json_encode(
							array(
								'_wcf_order_id' => wcf_pro()->wc_common->get_order_id( $order ),
							)
						),
					)
				);
			}
		} else {

			/** Do things for the main order */
			if ( $this->skip_line_items( $order, $order_items ) ) {

				$total_amount = $this->round( $order->get_total() );

				// Calculate the total.
				$calculated_total += $this->round( $order_subtotal + $order->get_cart_tax() ) + $this->round( $order->get_total_shipping() + $order->get_shipping_tax() );

				// Calculate order subtotal.
				if ( $this->price_format( $total_amount ) !== $this->price_format( $calculated_total ) ) {
					$order_subtotal = $order_subtotal - ( $calculated_total - $total_amount );
				}

				$item_names = array();

				foreach ( $order_items as $item ) {
					$item_names[] = sprintf( '%1$s x %2$s', $item['NAME'], $item['QTY'] );
				}

				// Add line item paramter.
				$this->add_litem_params(
					array(
						// translators: placeholder is blogname.
						'NAME' => sprintf( __( '%s - Order', 'cartflows-pro' ), get_option( 'blogname' ) ),
						'DESC' => ( implode( ', ', $item_names ) ),
						'AMT'  => $this->round( $order_subtotal + $order->get_cart_tax() ),
						'QTY'  => 1,
					),
					0,
					$use_deprecated_params
				);

				if ( $use_deprecated_params ) {

					$this->add_parameters(
						array(
							'AMT'              => $total_amount,
							'CURRENCYCODE'     => wcf_pro()->wc_common->get_currency( $order ),
							'ITEMAMT'          => $this->round( $order_subtotal + $order->get_cart_tax() ),
							'SHIPPINGAMT'      => $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ),
							'INVNUM'           => $this->wc_gateway()->get_option( 'invoice_prefix' ) . wcf_pro()->wc_common->get_order_id( $order ),
							'PAYMENTACTION'    => $type,
							'PAYMENTREQUESTID' => wcf_pro()->wc_common->get_order_id( $order ),
							'CUSTOM'           => wp_json_encode(
								array(
									'_wcf_order_id' => wcf_pro()->wc_common->get_order_id( $order ),
								)
							),
						)
					);
				} else {

					$this->add_payment_parameters(
						array(
							'AMT'              => $total_amount,
							'CURRENCYCODE'     => wcf_pro()->wc_common->get_currency( $order ),
							'ITEMAMT'          => $this->round( $order_subtotal + $order->get_cart_tax() ),
							'SHIPPINGAMT'      => $this->round( $order->get_total_shipping() + $order->get_shipping_tax() ),
							'INVNUM'           => $this->wc_gateway()->get_option( 'invoice_prefix' ) . wcf_pro()->wc_common->get_order_id( $order ),
							'PAYMENTACTION'    => $type,
							'PAYMENTREQUESTID' => wcf_pro()->wc_common->get_order_id( $order ),
							'CUSTOM'           => wp_json_encode(
								array(
									'_wcf_order_id' => wcf_pro()->wc_common->get_order_id( $order ),
								)
							),
						)
					);
				}
			} else {

				// Add individual order items.
				foreach ( $order_items as $item ) {
					$this->add_litem_params( $item, $item_count ++, $use_deprecated_params );
				}

				$total_amount = $this->round( $order->get_total() );

				// Add order-level parameters.
				if ( $use_deprecated_params ) {
					$this->add_parameters(
						array(
							'AMT'              => $total_amount,
							'CURRENCYCODE'     => wcf_pro()->wc_common->get_currency( $order ),
							'ITEMAMT'          => $this->round( $order_subtotal ),
							'SHIPPINGAMT'      => $this->round( $order->get_total_shipping() ),
							'TAXAMT'           => $this->round( $order->get_total_tax() ),
							'INVNUM'           => $this->wc_gateway()->get_option( 'invoice_prefix' ) . wcf_pro()->wc_common->get_order_id( $order ),
							'PAYMENTACTION'    => $type,
							'PAYMENTREQUESTID' => wcf_pro()->wc_common->get_order_id( $order ),

						)
					);
				} else {
					$this->add_payment_parameters(
						array(
							'AMT'              => $total_amount,
							'CURRENCYCODE'     => wcf_pro()->wc_common->get_currency( $order ),
							'ITEMAMT'          => $this->round( $order_subtotal ),
							'SHIPPINGAMT'      => $this->round( $order->get_total_shipping() ),
							'TAXAMT'           => $this->round( $order->get_total_tax() ),
							'INVNUM'           => $this->wc_gateway()->get_option( 'invoice_prefix' ) . wcf_pro()->wc_common->get_order_id( $order ),
							'PAYMENTACTION'    => $type,
							'PAYMENTREQUESTID' => wcf_pro()->wc_common->get_order_id( $order ),
							'CUSTOM'           => wp_json_encode(
								array(
									'_wcf_order_id' => wcf_pro()->wc_common->get_order_id( $order ),
								)
							),
						)
					);
				}
			}
		}
	}

	/**
	 * Adds a line item parameters to the request
	 *
	 * @param array $params paramters.
	 * @param int   $item_count current item count.
	 * @param bool  $use_deprecated_params whether to use deprecated PayPal?.
	 * @since 1.0.0
	 */
	public function add_litem_params( array $params, $item_count, $use_deprecated_params = false ) {
		foreach ( $params as $key => $value ) {
			if ( $use_deprecated_params ) {
				$this->add_parameter( "L_{$key}{$item_count}", $value );
			} else {
				$this->add_parameter( "L_PAYMENTREQUEST_0_{$key}{$item_count}", $value );
			}
		}
	}

	/**
	 * Skip line items and send as single item
	 *
	 * @since 1.0.0
	 *
	 * @param WC_Order $order Optional. The WC_Order object. Default null.
	 * @param array    $order_items Order items.
	 *
	 * @return bool true if line items should be skipped, false otherwise
	 */
	public function skip_line_items( $order = null, $order_items = null ) {

		$skip_line_items = false;

		if ( true != $skip_line_items && ! is_null( $order ) && ! is_null( $order_items ) ) {

			$calculated_total = 0;

			foreach ( $order_items as $item ) {
				$calculated_total += $this->round( $item['AMT'] * $item['QTY'] );
			}

			$calculated_total += $this->round( $order->get_total_shipping() ) + $this->round( $order->get_total_tax() );
			$total_amount      = $this->round( $order->get_total() );

			if ( $this->price_format( $total_amount ) !== $this->price_format( $calculated_total ) ) {
				$skip_line_items = true;
			}
		}

		// Filter for line items.
		return apply_filters( 'wcs_paypal_reference_transaction_skip_line_items', $skip_line_items, $order );
	}

	/**
	 * Convert parameter array to HTTP build string
	 *
	 * @return string
	 */
	public function to_string() {
		wcf()->logger->log( wcf_print_r( $this->get_parameters(), true ) );

		return http_build_query( $this->get_parameters(), '', '&' );
	}

	/**
	 * Get payment parameters.
	 *
	 * @return array
	 * @throws Exception Paramter errors.
	 */
	public function get_parameters() {

		/**
		 * Filter PPE request parameters.
		 *
		 * Use this to modify the PayPal request parameters prior to validation
		 *
		 * @param array $parameters
		 * @param Cartflows_Pro_Paypal_Gateway_Helper  $instance Class instance
		 */
		$instance         = new Cartflows_Pro_Paypal_Gateway_Helper();
		$this->parameters = apply_filters( 'wcs_paypal_request_params', $this->parameters, $instance );

		// validate parameters.
		foreach ( $this->parameters as $key => $value ) {

			// remove unused params.
			if ( '' === $value || is_null( $value ) ) {
				unset( $this->parameters[ $key ] );
			}

			// format and check amounts.
			if ( false !== strpos( $key, 'AMT' ) ) {

				// amounts must be 10,000.00 or less for USD.
				if ( isset( $this->parameters['PAYMENTREQUEST_0_CURRENCYCODE'] ) && 'USD' == $this->parameters['PAYMENTREQUEST_0_CURRENCYCODE'] && $value > 10000 ) {

					throw new Exception( sprintf( '%s amount of %s must be less than $10,000.00', $key, $value ) );
				}

				$this->parameters[ $key ] = $this->price_format( $value );
			}
		}

		return $this->parameters;
	}

	/**
	 * Check if api response has error
	 *
	 * @param array $response response object array.
	 * @return bool
	 */
	public function has_error_api_response( $response ) {

		// Consider error when ACK parameter is missing.
		if ( ! isset( $response['ACK'] ) ) {
			return true;
		}

		// Check if response contains success string.
		return ( 'Success' !== $this->get_value_from_response( $response, 'ACK' ) && 'SuccessWithWarning' !== $this->get_value_from_response( $response, 'ACK' ) );
	}

	/**
	 * Set variables for API calls
	 *
	 * @param int    $gateway_id gateway ID.
	 * @param string $api_env Api enviornment.
	 * @param string $api_usr API username.
	 * @param string $api_pass API password.
	 * @param string $api_sign API signature.
	 */
	public function setup_api_vars( $gateway_id, $api_env, $api_usr, $api_pass, $api_sign ) {

		$this->api_username  = $api_usr;
		$this->api_password  = $api_pass;
		$this->api_signature = $api_sign;

		$this->gateway_id = $gateway_id;

		// Rquest URI per enviornment.
		$this->request_uri = ( 'live' === $api_env ) ? $this->live_api : $this->test_api;

		// Set HTTP version to 1.1.
		$this->request_http_version = '1.1';

	}

	/**
	 * Add api credentials parameters
	 *
	 * @param string $api_username API username.
	 * @param string $api_password API password.
	 * @param string $api_signature API signature.
	 * @param string $api_version API version.
	 * @return void
	 */
	public function add_credentials_param( $api_username, $api_password, $api_signature, $api_version ) {

		$this->add_parameters(
			array(
				'USER'      => $api_username,
				'PWD'       => $api_password,
				'SIGNATURE' => $api_signature,
				'VERSION'   => $api_version,
			)
		);
	}
}
