<?php
/**
 * @package     Splitit_WooCommerce_Plugin
 *
 * File - class-splitit-flexfields-payment-plugin-api.php
 * Class for working with SplitIt API
 */

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

require_once __DIR__ . '/../vendor/autoload.php';

// FIX: conflict of Guzzle library versions.
if ( ! function_exists( 'GuzzleHttp\describe_type' ) ) {
	require_once __DIR__ . '/../vendor/guzzlehttp/guzzle/src/functions_include.php';
}

use Splitit\Client;
use Splitit\Configuration;
use Splitit\Model\AddressDataModel;
use Splitit\Model\PlanDataModel;
use Splitit\Model\ShopperData;

/**
 * Class SplitIt_FlexFields_Payment_Plugin_API
 */
class SplitIt_FlexFields_Payment_Plugin_API {

	/**
	 * @var string
	 */
	protected $api_key;

	/**
	 * @var ?string
	 */
	protected $username;

	/**
	 * @var ?string
	 */
	protected $password;

	/**
	 * @var string
	 */
	protected $environment;

	/**
	 * @var mixed
	 */
	protected $auto_capture;

	/**
	 * @var mixed
	 */
	protected $secure_3d;

	/**
	 * @var string
	 */
	protected $refund_strategy;

	/**
	 * @var int
	 */
	protected $default_number_of_installments;

	/**
	 * @var ?string
	 */
	protected $session_id;

	/**
	 * API constructor.
	 *
	 * @param array      $settings Settings from DB
	 * @param null | int $default_number_of_installments Default number of installments
	 */
	public function __construct( $settings, $default_number_of_installments = null ) {
		$this->api_key                        = $this->get_api_key( $settings );
		$this->username                       = $settings['splitit_api_username'] ?? '';
		$this->password                       = $settings['splitit_api_password'] ?? '';
		$this->environment                    = $settings['splitit_environment'] ?? 'sandbox';
		$this->auto_capture                   = $settings['splitit_auto_capture'] ?? '';
		$this->secure_3d                      = $settings['splitit_settings_3d'] ?? '';
		$this->refund_strategy                = $settings['splitit_refund_strategy'] ?? \Splitit\Model\RefundStrategy::FUTURE_INSTALLMENTS_FIRST;
		$this->default_number_of_installments = $default_number_of_installments ?? 0;
		$this->session_id                     = null;
	}

	/**
	 * @param $settings
	 *
	 * @return mixed
	 */
	public function get_api_key( $settings ) {
		$environment = $settings['splitit_environment'] ?? 'sandbox';
		if ( get_option( 'splitit_' . $environment . '_new_login' ) ) {
			$current_api_key = get_option( 'splitit_' . $environment . '_api_key' );
		} else {
			$current_api_key = $settings['splitit_api_key'] ?? '';
		}
		return $current_api_key;
	}

	/**
	 * Login method
	 *
	 * @param bool $check_credentials For check credentials.
	 *
	 * @return array[]|string
	 * @throws Exception
	 */
	public function login( $check_credentials = false ) {
		$data = array(
			'user_id' => get_current_user_id(),
			'method'  => __( 'login() API Splitit', 'splitit-installment-payments' ),
		);

		$environment = $this->environment;

		$client_id     = get_option( 'splitit_' . $environment . '_client_id' ) ? get_option( 'splitit_' . $environment . '_client_id' ) : $this->username;
		$client_secret = get_option( 'splitit_' . $environment . '_client_secret' ) ? get_option( 'splitit_' . $environment . '_client_secret' ) : $this->password;

		try {
			$api_url = 'https://id.' . $environment . '.splitit.com/connect/token';

			$response = wp_remote_post(
				$api_url,
				array(
					'headers'   => array(
						'Content-Type' => 'application/x-www-form-urlencoded',
					),
					'body'      => array(
						'client_id'     => $client_id,
						'client_secret' => $client_secret,
						'grant_type'    => 'client_credentials',
						'scope'         => 'api.v3',
					),
					'sslverify' => false,
				)
			);

			if ( is_wp_error( $response ) ) {
				$error_message = 'Method login API Get Access Token - ' . $response->get_error_message();
				SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'login() API - error: ' . $error_message, 'error' );
				throw new Exception( $error_message );
			}

			$body = wp_remote_retrieve_body( $response );

			$decoded_response = json_decode( $body );

			if ( json_last_error() !== JSON_ERROR_NONE ) {
				throw new Exception( 'Invalid JSON response: ' . json_last_error_msg() );
			}

			if ( isset( $decoded_response->access_token ) ) {
				$access_token = $decoded_response->access_token;
			} else {
				throw new Exception( 'Access token not found in the response: ' . json_encode( $decoded_response ) );
			}

			$this->session_id = $access_token ?? null;

			return $access_token;
		} catch ( Exception $e ) {
			$message = 'Error. File - ' . $e->getFile() . ', message - ' . $e->getMessage() . ', row' . $e->getLine();
			SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'login() API - ' . $message, 'error' );

			return array( 'error' => array( 'message' => $e->getMessage() ) );
		}
	}

	/**
	 * Initiate method
	 *
	 * @param array $data Data for initiate.
	 *
	 * @return false|string
	 * @throws Exception
	 */
	public function initiate( $data ) {
		global $plugin_version;

		$log_data = array(
			'user_id' => get_current_user_id(),
			'method'  => __( 'initiate() API Splitit', 'splitit-installment-payments' ),
		);

		try {
			$access_token = $this->login();
			$config       = Configuration::getDefaultConfiguration();

			if ( 'sandbox' === $this->environment ) {
				$config->setHost( 'https://web-api-v3.sandbox.splitit.com' );
				$config->setTokenUrl( 'https://id.sandbox.splitit.com/connect/token' );
			}

			$config->setAccessToken( $access_token );

			$client_id     = get_option( 'splitit_' . $this->environment . '_client_id' ) ? get_option( 'splitit_' . $this->environment . '_client_id' ) : $this->username;
			$client_secret = get_option( 'splitit_' . $this->environment . '_client_secret' ) ? get_option( 'splitit_' . $this->environment . '_client_secret' ) : $this->password;

			$config->setClientId( $client_id );
			$config->setClientSecret( $client_secret );

			if ( isset( $access_token ) && ! isset( $access_token['error'] ) ) {
				$splitit = new Client( '', null, null, null, '', $config );

				$plan_data = new PlanDataModel();
				$plan_data->setTerminalId( $this->api_key );

				if ( ! empty( $data['numberOfInstallments'] ) ) {
					$plan_data->setNumberOfInstallments( $data['numberOfInstallments'] );
				}
				if ( ! empty( $data['amount'] ) ) {
					$plan_data->setTotalAmount( number_format( round( $data['amount'], 2 ), 2, '.', '' ) );
				}
				if ( ! empty( $data['currency_code'] ) ) {
					$plan_data->setCurrency( $data['currency_code'] );
				}

				$plan_data->setPurchaseMethod( \Splitit\Model\PurchaseMethod::E_COMMERCE );

				$billing_address     = array();
				$consumer_data       = array();
				$redirect_urls       = array();
				$x_splitit_test_mode = 'None';

				if ( isset( $data['billingAddress'] ) ) {
					$billing_address = new AddressDataModel(
						array(
							'address_line1' => $data['billingAddress']['AddressLine'],
							'address_line2' => $data['billingAddress']['AddressLine2'],
							'city'          => $data['billingAddress']['City'],
							'state'         => $data['billingAddress']['State'],
							'country'       => $data['billingAddress']['Country'],
							'zip'           => $data['billingAddress']['Zip'],
						)
					);
				}
				if ( isset( $data['consumerData'] ) ) {
					$consumer_data = new ShopperData(
						array(
							'full_name'    => $data['consumerData']['FullName'],
							'email'        => $data['consumerData']['Email'],
							'phone_number' => $data['consumerData']['PhoneNumber'],
							'culture'      => $data['consumerData']['CultureName'],
						)
					);
				}

				$ux_settings = new \Splitit\Model\UxSettingsModel();
				$ux_settings->setDisplayedInstallmentOptions( $data['installments'] );

				$events_endpoints = array(
					'CreateSucceeded' => site_url() . '/wc-api/splitit_payment_success_async',
				);

				$initiate_response = $splitit->installmentPlan->post(
					(bool) $this->auto_capture,
					wp_generate_uuid4(),
					'WooCommercePlugin.' . $plugin_version,
					(bool) $this->secure_3d,
					$consumer_data,
					$plan_data,
					$billing_address,
					$redirect_urls,
					$ux_settings,
					(object) $events_endpoints,
					array(),
					$x_splitit_test_mode
				);

				$success = $initiate_response->getStatus() && 'Initialized' == $initiate_response->getStatus();

				if ( $success ) {
					$field_data = array(
						'installmentPlanNumber' => $initiate_response->getInstallmentPlanNumber(),
						'purchaseMethod'        => $initiate_response->getPurchaseMethod(),
						'currency'              => $initiate_response->getCurrency(),
						'amount'                => $initiate_response->getAmount(),
						'checkoutUrl'           => $initiate_response->getCheckoutUrl(),
						'shopper'               => $initiate_response->getShopper(),
						'billingAddress'        => $initiate_response->getBillingAddress(),
						'numberOfInstallments'  => ( isset( $data['numberOfInstallments'] ) && ! empty( $data['numberOfInstallments'] ) ) ? $data['numberOfInstallments'] : null,
					);

					SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $log_data, 'initiate() API - Successful initiate', 'info' );

					return wp_json_encode( $field_data );
				} else {
					$message = __( 'Failed initiate', 'splitit-installment-payments' );
					SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $log_data, 'initiate() API - error: ' . $message, 'error' );
					$error_data = array( 'error' => array( 'message' => $message ) );

					return wp_json_encode( $error_data );
				}
			} else {
				$message = __( 'Initiate failed login. Please make sure that you are using the correct merchant and terminal and that you have the necessary accesses for them.', 'splitit-installment-payments' );
				SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $log_data, 'initiate() API - error: ' . $message, 'error' );
				$error_data = array( 'error' => array( 'message' => $message ) );

				return wp_json_encode( $error_data );
			}
		} catch ( Exception $e ) {
			$message = 'Error. File - ' . $e->getFile() . ', message - ' . $e->getMessage() . ', row' . $e->getLine();
			SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $log_data, 'initiate() API - error: ' . $message, 'error' );

			$message_for_displaying = 'Initiate failed login. For more information, please contact the Splitit Support Team';

			preg_match( '/"Message":"(.*?)"/', $e->getMessage(), $matches );

			if ( isset( $matches[1] ) ) {
				$message_for_displaying = $matches[1];
			}

			return wp_json_encode( array( 'error' => array( 'message' => $message_for_displaying ) ) );
		}
	}

	/**
	 * Update method
	 *
	 * @param int $order_id Order ID.
	 * @param int $ipn Installment plan number.
	 *
	 * @throws Exception
	 */
	public function update( $order_id, $ipn ) {
		global $plugin_version;

		$attempt      = 1;
		$max_attempts = 4;

		$data = array(
			'user_id' => get_current_user_id(),
			'method'  => __( 'update() API Splitit', 'splitit-installment-payments' ),
		);

		while ( $attempt <= $max_attempts ) {
			try {
				$api_instance    = $this->get_api_instance();
				$idempotency_key = wp_generate_uuid4();

				$api_instance->installmentPlan->updateOrder(
					$ipn,
					$idempotency_key,
					'WooCommercePlugin.' . $plugin_version,
					'',
					$order_id,
					\Splitit\Model\ShippingStatus::PENDING
				);

				SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, __( 'update() API - Update was successful', 'splitit-installment-payments' ), 'info' );

				return;
			} catch ( Exception $e ) {
				$message = 'Error. File - ' . $e->getFile() . ', message - ' . $e->getMessage() . ', row' . $e->getLine() . ', code: ' . $e->getCode();

				SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'update() API - error: ' . $message, 'error' );

				$status_code = $e->getCode();

				if ( 0 === strpos( (string) $status_code, '5' ) || '422' == $status_code ) {
					SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'update() API - Update attempt# ' . $attempt, 'info' );
					if ( $attempt < $max_attempts ) {
						sleep( $attempt );
					}

					++$attempt;
				} else {
					return;
				}
			}
		}

		SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'update() API - number of attempts exhausted. Order Id = ' . $order_id . ' ipn: ' . $ipn, 'error' );
		throw new Exception( esc_html__( 'Update plan failed.', 'splitit-installment-payments' ) );
	}

	/**
	 * Refund method
	 *
	 * @param null   $amount Amount.
	 * @param string $currency_code Currency.
	 * @param string $ipn installments plan number.
	 *
	 * @return bool
	 * @throws Exception
	 */
	public function refund( $amount = null, $currency_code = '', $ipn = '', $order_id = '', $reason = '', $action_type = '' ) {
		global $plugin_version;

		$data = array(
			'user_id' => get_current_user_id(),
			'method'  => __( 'refund() API Splitit', 'splitit-installment-payments' ),
		);

		$api_instance = $this->get_api_instance();
		$amount       = number_format( $amount, 2, '.', '' );

		SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'refund() API - RefundStrategy: ' . $this->refund_strategy, 'info' );

		$response = $api_instance->installmentPlan->refund(
			$amount,
			$ipn,
			wp_generate_uuid4(),
			'WooCommercePlugin.' . $plugin_version,
			$this->refund_strategy
		);

		if ( $response->getRefundId() && 0 == $response->getSummary()->getFailedAmount() ) {
			SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'refund() API - Refund was successful, no failed amount', 'splitit-installment-payments' );

			$data['order_id']      = $order_id;
			$data['ipn']           = $ipn;
			$data['refund_id']     = $response->getRefundId();
			$data['refund_amount'] = $amount;
			$data['refund_reason'] = $reason;
			$data['action_type']   = $action_type;
			SplitIt_FlexFields_Payment_Plugin_Log::save_refund_info( $data );

			return true;
		} else {
			throw new Exception( esc_html__( 'Refund unable to be processed online, consult your Splitit Account to process manually', 'splitit-installment-payments' ) );
		}
	}

	/**
	 * Cancel method
	 *
	 * @param int $installment_plan_number Installment plan number.
	 *
	 * @return bool
	 * @throws Exception
	 */
	public function cancel( $installment_plan_number ) {
		global $plugin_version;

		$data = array(
			'user_id' => get_current_user_id(),
			'method'  => __( 'cancel() API Splitit', 'splitit-installment-payments' ),
		);

		$api_instance = $this->get_api_instance();
		$response     = $api_instance->installmentPlan->cancel(
			$installment_plan_number,
			wp_generate_uuid4(),
			'WooCommercePlugin.' . $plugin_version
		);

		if ( $response->getInstallmentPlanNumber() ) {
			SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'cancel() API - Canceled was successful', 'splitit-installment-payments' );

			return true;
		} else {
			SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'cancel() API - error: ' . $response->getError(), 'error' );
			throw new Exception( esc_html__( 'Cancel unable to be processed online, consult your Splitit Account to process manually', 'splitit-installment-payments' ) );
		}
	}

	/**
	 * Method for getting instance
	 *
	 * @return Client
	 */
	public function get_api_instance() {
		global $plugin_version;

		$access_token = $this->login();

		$config = Configuration::getDefaultConfiguration();
		if ( 'sandbox' === $this->environment ) {
			$config->setHost( 'https://web-api-v3.sandbox.splitit.com' );
			$config->setTokenUrl( 'https://id.sandbox.splitit.com/connect/token' );
		}

		$config->setAccessToken( $access_token );
		$client_id     = get_option( 'splitit_' . $this->environment . '_client_id' ) ? get_option( 'splitit_' . $this->environment . '_client_id' ) : $this->username;
		$client_secret = get_option( 'splitit_' . $this->environment . '_client_secret' ) ? get_option( 'splitit_' . $this->environment . '_client_secret' ) : $this->password;

		$config->setClientId( $client_id );
		$config->setClientSecret( $client_secret );

		return new Client( '', null, null, null, '', $config );
	}

	/**
	 * Method for getting information by ipn
	 *
	 * @param int $installment_plan_number Installment plan number.
	 *
	 * @throws Exception
	 */
	public function get_ipn_info( $installment_plan_number ) {
		global $plugin_version;

		$api_instance = $this->get_api_instance();

		try {
			return $api_instance->installmentPlan->get(
				$installment_plan_number,
				wp_generate_uuid4(),
				'WooCommercePlugin.' . $plugin_version
			);
		} catch ( \Exception $e ) {
			throw new Exception( 'Exception when calling InstallmentPlanApi->get: ' . esc_html( $e->getMessage() ) );
		}
	}

	/**
	 * @param $installment_plan_number
	 * @param $order_id
	 *
	 * @return bool
	 * @throws Exception
	 */
	public function start_installments( $installment_plan_number, $order_id ) {
		global $plugin_version;

		$log_data = array(
			'user_id' => get_current_user_id(),
			'method'  => __( 'start_installments() API Splitit', 'splitit-installment-payments' ),
		);

		$api_instance = $this->get_api_instance();
		try {

			$result = $api_instance->installmentPlan->updateOrder(
				$installment_plan_number,
				wp_generate_uuid4(),
				'WooCommercePlugin.' . $plugin_version,
				'',
				$order_id,
				\Splitit\Model\ShippingStatus::SHIPPED,
				true
			);

			if ( \Splitit\Model\PlanStatus::ACTIVE == $result->getStatus() ) {
				SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $log_data, 'start_installments() API - StartInstallment was successful', 'splitit-installment-payments' );

				return true;
			} else {
				throw new Exception( 'Invalid Installment Plan Status' );
			}
		} catch ( \Exception $e ) {
			throw new Exception( 'start_installments() API - Exception when calling InstallmentPlanApi->updateOrder in StartInstallment method: ' . esc_html( $e->getMessage() ) );
		}
	}

	/**
	 * Verify method
	 *
	 * @param int $installment_plan_number Installment plan number.
	 *
	 * @return \Splitit\Model\VerifyAuthorizationResponse
	 * @throws Exception
	 */
	public function verify_payment( $installment_plan_number ) {

		global $plugin_version;

		$data = array(
			'user_id' => get_current_user_id(),
			'method'  => __( 'verifyPayment() API Splitit', 'splitit-installment-payments' ),
		);

		$attempt       = 1;
		$max_attempts  = 4;
		$error_message = '';

		while ( $attempt <= $max_attempts ) {
			$api_instance = $this->get_api_instance();

			try {
				$result = $api_instance->installmentPlan->verifyAuthorization(
					$installment_plan_number,
					wp_generate_uuid4(),
					'WooCommercePlugin.' . $plugin_version
				);

				SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, __( 'verifyPayment() API - VerifyPayment was successful', 'splitit-installment-payments' ), 'info' );

				return $result;
			} catch ( \Exception $e ) {
				$error_message = $e->getMessage();
				$status_code   = $e->getCode();

				if ( 0 === strpos( (string) $status_code, '5' ) || '422' == $status_code ) {
					SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'verifyPayment() API - Verify attempt# ' . $attempt, 'info' );

					if ( $attempt < $max_attempts ) {
						sleep( $attempt );
					}

					++$attempt;
				} else {
					return;
				}
			}
		}

		SplitIt_FlexFields_Payment_Plugin_Log::save_log_info( $data, 'verifyPayment() API - Number of attempts exhausted. Ipn: ' . $installment_plan_number . ', error: ' . $error_message, 'error' );
		throw new Exception( esc_html( $error_message ) );
	}
}
