<?php // phpcs:ignore WordPress.Files.FileName

/**
 * The abstract class that manage the Payouts Service
 *
 * @package YITH\PayPalPayouts\Classes
 */

use PaypalPayoutsSDK\Core\SandboxEnvironment;
use PaypalPayoutsSDK\Core\ProductionEnvironment;
use PaypalPayoutsSDK\Core\PayPalHttpClient;
use PaypalPayoutsSDK\Payouts\PayoutsGetRequest;
use PaypalPayoutsSDK\Payouts\PayoutsItemGetRequest;
use PaypalPayoutsSDK\Payouts\PayoutsPostRequest;
use Sample\PayPalClient;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( 'YITH_Abstract_PayOuts_Service' ) ) {
	/**
	 * Class YITH_Abstract_PayOuts_Service
	 */
	class YITH_Abstract_PayOuts_Service {
		/**
		 * The business email
		 *
		 * @var string
		 */
		protected $business_email;
		/**
		 * The payouts application id
		 *
		 * @var string
		 */
		protected $application_id;
		/**
		 * The application secret key
		 *
		 * @var string
		 */
		protected $application_secret_key;
		/**
		 * Enable or not the log
		 *
		 * @var bool
		 */
		protected $enable_log;
		/**
		 * Is sandbox
		 *
		 * @var bool
		 */
		protected $is_sandbox;
		/**
		 * The logger
		 *
		 * @var WC_Logger
		 */
		protected $logger;

		/**
		 * YITH_Abstract_PayOuts_Service constructor.
		 */
		public function __construct() {

			$this->business_email         = get_option( 'yith_payouts_business_email', '' );
			$this->application_id         = get_option( 'yith_payouts_application_id', '' );
			$this->application_secret_key = get_option( 'yith_payouts_application_secret_key', '' );
			$this->is_sandbox             = ( 'yes' === get_option( 'yith_payouts_sandbox_mode', 'no' ) );
			$this->enable_log             = ( 'yes' === get_option( 'yith_payouts_enable_log', 'no' ) );
			$this->logger                 = wc_get_logger();

			add_action( 'woocommerce_checkout_order_processed', array( $this, 'register_payouts_on_checkout' ), 20, 1 );
			add_action( 'woocommerce_store_api_checkout_order_processed', array(
				$this,
				'register_payouts_on_api_checkout'
			), 20, 1 );
			add_action( 'woocommerce_api_yith_payouts_response', array( $this, 'trigger_webhook_response' ) );

		}

		/**
		 * Get the paypal business email
		 *
		 * @return string
		 * @since 1.0.0
		 * @author YITH <plugins@yithemes.com>
		 */
		public function get_business_email() {

			return $this->business_email;
		}

		/**
		 * Get the paypal application client id
		 *
		 * @return string
		 * @since 1.0.0
		 */
		public function get_application_id() {

			return $this->application_id;
		}

		/**
		 * Get the paypal application client secret key
		 *
		 * @return string
		 * @since 1.0.0
		 */
		public function get_application_secret_key() {

			return $this->application_secret_key;
		}

		/**
		 * Check if enabled sandbox mode
		 *
		 * @return bool
		 * @since 1.0.0
		 */
		public function is_sandbox() {

			return $this->is_sandbox;
		}

		/**
		 * Check if the plugin is configured properly
		 *
		 * @return bool
		 * @since 1.0.0
		 */
		public function check_service_configuration() {

			$check_configuration = ( ! empty( $this->get_business_email() ) && ! empty( $this->get_application_id() ) && ! empty( $this->get_application_secret_key() ) );

			return apply_filters( 'yith_payout_check_service_configuration', $check_configuration );
		}


		/**
		 * Check if is enabled the log
		 *
		 * @return bool
		 * @since 1.0.0
		 */
		public function is_log_enabled() {
			return $this->enable_log;
		}

		/**
		 * Write the log
		 *
		 * @param mixed $message The message, can be string or object.
		 *
		 * @since 2.0.0
		 */
		public function maybe_write_log( $message ) {
			if ( $this->enable_log ) {
				$message = ! is_string( $message ) ? print_r( $message, true ) : $message; // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
				$this->logger->add( 'yith-paypal-payouts-for-woocommerce', $message );
			}
		}

		/**
		 * Get the Client object
		 *
		 * @since 2.0.0
		 */
		public function get_client() {
			$client = false;
			if ( $this->check_service_configuration() ) {
				$client_id  = $this->get_application_id();
				$client_key = $this->get_application_secret_key();
				if ( $this->is_sandbox() ) {
					$environment = new SandboxEnvironment( $client_id, $client_key );
				} else {
					$environment = new ProductionEnvironment( $client_id, $client_key );
				}

				$client = new PayPalHttpClient( $environment );
			}

			return $client;
		}


		/**
		 * Get the payout item
		 * $args is array with this structure
		 * array(
		 *      "recipient_type" => "EMAIL",
		 *        "receiver" => "shirt-supplier-three@mail.com",
		 *        "note" => "Thank you.",
		 *        "sender_item_id" => uniqid(),
		 *        "amount" => array(
		 *        "value" => "0.90",
		 *        "currency" => "USD"
		 *        )
		 *
		 *  ).
		 *
		 * @param array $args The payout args.
		 *
		 * @return \PayPal\Api\PayoutItem
		 * @since 1.0.0
		 */
		public function get_payout_item( $args ) {

			return new \PayPal\Api\PayoutItem(
				$args
			);
		}

		/**
		 * Create the sender batch header
		 *
		 * @param int $unique_id ( should be order_id or other unique_id ).
		 *
		 * @return \PayPal\Api\PayoutSenderBatchHeader
		 * @since 1.0.0
		 */
		public function get_batch_header( $unique_id ) {

			$senderBatchHeader = new \PayPal\Api\PayoutSenderBatchHeader(); // phpcs:ignore WordPress.NamingConventions.ValidVariableName
			$senderBatchHeader->setSenderBatchId( $unique_id )->setEmailSubject( 'You have a payment' ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName

			return $senderBatchHeader;    // phpcs:ignore WordPress.NamingConventions.ValidVariableName
		}

		/**
		 * This method create an array with all payout items
		 *
		 * @param int $order_id The order id.
		 *
		 * @return array
		 * @since 1.0.0
		 * @see get_payout_item
		 */
		public function get_payout_items( $order_id ) {

			$order = wc_get_order( $order_id );

			$receivers    = apply_filters( 'yith_payouts_receivers', get_option( 'yith_payouts_receiver_list', array() ) );
			$receivers    = empty( $receivers ) ? array() : $receivers;
			$payout_items = array();

			if ( count( $receivers ) > 0 ) {

				$order_items = $order->get_items();
				$currency    = $order->get_currency();

				foreach ( $order_items as $item ) {

					if ( apply_filters( 'yith_payouts_include_item', true, $item, $order_id ) ) {
						$line_total = $order->get_line_total( $item, true );

						foreach ( $receivers as $receiver ) {

							$user_id         = $receiver['user_id'];
							$user_email      = $receiver['paypal_email'];
							$commission_rate = floatval( $receiver['commission_rate'] / 100 );

							if ( ! isset( $payout_items[ $user_id ] ) ) {
								$payout_items[ $user_id ] = array(
									'recipient_type' => 'EMAIL',
									'receiver'       => $user_email,
									'note'           => 'Thank you.',
									'amount'         => array(
										'value'    => round( $commission_rate * $line_total, 2 ),
										'currency' => $currency,
									),
								);
							} else {
								$payout_items[ $user_id ]['amount']['value'] += $commission_rate * $line_total;
							}
						}
					}
				}
			}

			return apply_filters( 'yith_payout_items_receiver', $payout_items, $order_id );
		}

		/**
		 * Get the payout items by sender id
		 *
		 * @param string $sender_batch_id The sender batch id.
		 *
		 * @return array
		 * @since 1.0.0
		 */
		public function get_payout_items_by_sender_batch_id( $sender_batch_id ) {

			$payout_items = YITH_Payout_Items()->get_payout_items(
				array(
					'sender_batch_id' => $sender_batch_id,
					'fields'          => array(
						'receiver',
						'amount',
						'currency',
					),
				)
			);

			$items = array();

			foreach ( $payout_items as $payout_item ) {

				$item = array(
					'recipient_type' => 'EMAIL',
					'receiver'       => $payout_item['receiver'],
					'amount'         => array(
						'value'    => number_format( round( $payout_item['amount'], 2 ), 2 ),
						'currency' => $payout_item['currency'],
					),
				);

				$items[] = $item;
			}

			return $items;
		}


		/**
		 * Make a payout for an order
		 *
		 * @param array $args The args.
		 *
		 * @return PayPalHttp\HttpResponse|string
		 * @since 1.0.0
		 */
		public function PayOuts( $args ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName

			$sender_batch_id = isset( $args['sender_batch_id'] ) ? $args['sender_batch_id'] : '';
			$sender_items    = isset( $args['sender_items'] ) ? $args['sender_items'] : array();
			$output          = null;
			$body_request    = array(
				'sender_batch_header' => array(),
				'items'               => $sender_items,
			);


			if ( ! empty( $sender_batch_id ) && count( $sender_items ) > 0 ) {
				$payout = new YITH_Payout( $sender_batch_id );

				if ( 'affiliate' === $payout->payout_mode ) {
					$email_subject = get_option( 'yith_payouts_affiliate_notification_subject' );
					$email_content = get_option( 'yith_payouts_affiliate_notification_content' );
					if ( isset( $args['commission_ids'] ) ) {
						$email_content = str_replace( '{commission_ids}', "#".implode( ',#', $args['commission_ids'] ), $email_content );
					}
				} elseif ( 'commission' === $payout->payout_mode ) {
					$email_subject = get_option( 'yith_payouts_vendor_notification_subject' );
					$email_content = get_option( 'yith_payouts_vendor_notification_content' );
					if ( isset( $args['order_id'] ) ) {
						$email_content = str_replace( '{order_id}', '#' . $args['order_id'], $email_content );
					}

					if ( isset( $args['commission_ids'] ) ) {
						$email_content = str_replace( '{commission_ids}', "#".implode( ',#', $args['commission_ids'] ), $email_content );
					}
				} else {
					$email_subject = get_option( 'yith_payouts_notification_subject' );
					$email_content = get_option( 'yith_payouts_notification_content' );
				}
				$body_request['sender_batch_header'] = array(
					'email_subject'   => $email_subject,
					'email_message'   => $email_content,
					'sender_batch_id' => $sender_batch_id,
				);

				try {
					$request       = new PayoutsPostRequest();
					$request->body = $body_request;
					$client        = $this->get_client();
					if ( $client ) {
						$output = $client->execute( $request );

						$this->maybe_write_log( "Status Code: {$output->statusCode}" );
						$this->maybe_write_log( "Status: {$output->result->batch_header->batch_status}" );
						$this->maybe_write_log( "Batch ID: {$output->result->batch_header->payout_batch_id}" );
						$this->maybe_write_log( 'Links:' );
						foreach ( $output->result->links as $link ) {
							$this->maybe_write_log( "\t{$link->rel}: {$link->href}\tCall Type: {$link->method}" );
						}
						$this->maybe_write_log( wp_json_encode( $output->result, JSON_PRETTY_PRINT ) );

						$response = array(
							'resource' => array(
								'batch_header' => array(
									'payout_batch_id'     => $output->result->batch_header->payout_batch_id,
									'batch_status'        => strtolower( $output->result->batch_header->batch_status ),
									'sender_batch_header' => array(
										'sender_batch_id' => $sender_batch_id,
									),
								),
							),
						);

						$this->check_payout_status( $output->result->batch_header->payout_batch_id );
					}
				} catch ( Exception $ex ) {

					$this->maybe_write_log( 'ERROR : ' . $ex->getMessage() . ' for #' . $sender_batch_id );
					$ex_json = json_decode( $ex->getMessage() );

					return isset( $ex_json->message ) ? $ex_json->message : __( 'Can\'t pay the payouts.', 'yith-paypal-payouts-for-woocommerce' );
				}
			}

			return true;
		}

		/**
		 * Register an entry when the order is processed
		 *
		 * @param int $order_id The order id.
		 *
		 * @since 1.0.0
		 */
		public function register_payouts_on_checkout( $order_id ) {

			if ( apply_filters( 'yith_payouts_register_new_payout', false, $order_id ) ) {

				$order    = wc_get_order( $order_id );
				$has_meta = $order->get_meta( 'yith_payouts_sender_id' );
				if ( empty( $has_meta ) ) {
					$payment_mode    = get_option( 'yith_payouts_mode', 'instant' );
					$sender_batch_id = uniqid();
					$items           = $this->get_payout_items( $order_id );
					$args            = array(
						'order_id'        => $order_id,
						'payout_mode'     => $payment_mode,
						'sender_batch_id' => $sender_batch_id,
						'items'           => $items,
					);

					$this->register_payouts( $args );

					$order->add_meta_data( 'yith_payouts_sender_id', $sender_batch_id );
					$order->save();
				}
			}
		}

		/**
		 * Register an entry when the order is processed via api
		 *
		 * @param WC_Order $order The order.
		 *
		 * @return void
		 */
		public function register_payouts_on_api_checkout( $order ) {
			$this->register_payouts_on_checkout( $order->get_id() );
		}

		/**
		 * Add a new entry into db
		 *
		 * @param array $payouts_info The payouts info.
		 *
		 * @since 1.0.0
		 */
		public function register_payouts( $payouts_info ) {

			$payout_args = array(
				'order_id'        => $payouts_info['order_id'],
				'payout_mode'     => $payouts_info['payout_mode'],
				'sender_batch_id' => $payouts_info['sender_batch_id'],
				'last_edit'       => date( 'Y-m-d H:i:s' ),
				// phpcs:ignore  WordPress.DateTime.RestrictedFunctions.date_date
			);

			YITH_Payout()->add( $payout_args );

			$payout_items = $payouts_info['items'];

			foreach ( $payout_items as $item ) {

				$args = array(
					'sender_batch_id' => $payouts_info['sender_batch_id'],
					'sender_item_id'  => isset( $item['sender_item_id'] ) ? $item['sender_item_id'] : '',
					'amount'          => $item['amount']['value'],
					'currency'        => $item['amount']['currency'],
					'receiver'        => $item['receiver'],
				);

				YITH_Payout_Items::add_payout_item( $args );

			}
		}

		/**
		 * Manage the webhooks Payouts
		 *
		 * @since 1.0.0
		 */
		public function trigger_webhook_response() {

			$body     = @file_get_contents( 'php://input' ); //phpcs:ignore
			$response = json_decode( $body, true );

			$event_name = explode( '.', $response['event_type'] );
			$event_name = isset( $event_name[1] ) ? $event_name[1] : '';

			if ( 'PAYOUTSBATCH' === $event_name ) {
				$this->process_payoutsbatch_event( $response );
			} elseif ( 'PAYOUTS-ITEM' === $event_name ) {

				$this->process_payouts_item_event( $response );
			}

		}

		/**
		 * Process Payout Batch event
		 *
		 * @param array $response The response.
		 *
		 * @since 1.0.0
		 */
		public function process_payoutsbatch_event( $response ) {

			$batch_header    = isset( $response['resource']['batch_header'] ) ? $response['resource']['batch_header'] : array();
			$payout_batch_id = $batch_header['payout_batch_id'];
			$sender_batch_id = $batch_header['sender_batch_header']['sender_batch_id'];
			$status          = strtolower( $batch_header['batch_status'] );

			do_action( 'yith_paypal_payout_batch_before_change_status', $payout_batch_id, $status, $batch_header );

			$this->update_payouts_info( $sender_batch_id, $payout_batch_id, $status );

			do_action( 'yith_paypal_payout_batch_change_status', $payout_batch_id, $status, $batch_header );

		}

		/**
		 * Update the payouts information
		 *
		 * @param string $sender_batch_id The sender batch id.
		 * @param string $payout_batch_id The batch id.
		 * @param string $payout_status The status.
		 *
		 * @since 2.0.0
		 */
		public function update_payouts_info( $sender_batch_id, $payout_batch_id = '', $payout_status = 'unprocessed' ) {
			$payout = YITH_Payout( $sender_batch_id );

			if ( ! empty( $payout_batch_id ) && empty( $payout->payout_batch_id ) ) {
				$this->maybe_write_log( 'Set the batch id ' . $payout_batch_id );
				$payout->payout_batch_id = $payout_batch_id;
			}

			if ( $payout->payout_status !== $payout_status ) {
				$this->maybe_write_log( 'Change the status from ' . $payout->payout_status . ' to ' . $payout_status );
				$payout->payout_status = $payout_status;
			}
			if ( '0000-00-00 00:00:00' === $payout->last_edit ) {
				$payout->last_edit = date( 'Y-m-d H:i:s' ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
			}

		}

		/**
		 * Check and update the payout items status
		 *
		 * @param string $payout_batch_id The batch id.
		 *
		 * @since 2.0.0
		 */
		public function check_payout_status( $payout_batch_id ) {
			try {
				$request = new PayoutsGetRequest( $payout_batch_id );
				$client  = $this->get_client();
				if ( $client ) {
					$response = $client->execute( $request );

					$batch_header   = $response->result->batch_header;
					$response_array = array(
						'resource' => array(
							'batch_header' => array(
								'payout_batch_id'     => $response->result->batch_header->payout_batch_id,
								'batch_status'        => strtolower( $response->result->batch_header->batch_status ),
								'sender_batch_header' => array(
									'sender_batch_id' => $batch_header->sender_batch_header->sender_batch_id,
								),
							),
						),
					);
					$this->process_payoutsbatch_event( $response_array );
					$items = $response->result->items;
					$this->maybe_write_log( 'Update payout items of ' . $payout_batch_id );

					foreach ( $items as $item ) {
						$args = array(
							'resource' => array(
								'payout_batch_id'    => $payout_batch_id,
								'sender_batch_id'    => $batch_header->sender_batch_header->sender_batch_id,
								'transaction_id'     => isset( $item->transaction_id ) ? $item->transaction_id : '' ,
								'transaction_status' => $item->transaction_status,
								'payout_item_id'     => $item->payout_item_id,
								'payout_item'        => array(
									'sender_item_id' => isset( $item->payout_item->sender_item_id ) ? $item->payout_item->sender_item_id : '',
									'amount'         => array(
										'value'    => $item->payout_item->amount->value,
										'currency' => $item->payout_item->amount->currency,
									),
									'receiver'       => $item->payout_item->receiver,
								),
								'payout_item_fee'    => array(
									'value' => $item->payout_item_fee->value,
								),
							),
						);

						$this->maybe_write_log( 'Update item : ' . print_r( $args, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
						$this->process_payouts_item_event( $args );
					}
				}
			} catch ( Exception $e ) {
				$this->maybe_write_log( 'ERROR : ' . $e->getMessage() . ' for #' . $payout_batch_id );
			}

		}

		/**
		 * Check the item status
		 *
		 * @param string $payout_item_id The  item id.
		 *
		 * @since 2.0.0
		 */
		public function check_payout_item_status( $payout_item_id ) {
			try {
				$request = new PayoutsItemGetRequest( $payout_item_id );
				$client  = $this->get_client();
				if ( $client ) {
					$response = $client->execute( $request );

					$args = array(
						'resource' => array(
							'payout_batch_id'    => $response->result->payout_batch_id,
							'sender_batch_id'    => $response->result->sender_batch_id,
							'transaction_id'     => $response->result->transaction_id,
							'transaction_status' => $response->result->transaction_status,
							'payout_item_id'     => $response->result->payout_item_id,
							'payout_item'        => array(
								'sender_item_id' => isset( $response->result->payout_item->sender_item_id ) ? $response->result->payout_item->sender_item_id : '',
								'amount'         => array(
									'value'    => $response->result->payout_item->amount->value,
									'currency' => $response->result->payout_item->amount->currency,
								),
								'receiver'       => $response->result->payout_item->receiver,
							),
							'payout_item_fee'    => array(
								'value' => $response->result->payout_item_fee->value,
							),
						),
					);
					$this->maybe_write_log( 'Update item : ' . print_r( $args, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
					$this->process_payouts_item_event( $args );
				}
			} catch ( Exception $e ) {
				$this->maybe_write_log( 'ERROR : ' . $e->getMessage() . ' for #' . $payout_item_id );
			}
		}

		/**
		 * Process Payout Item
		 *
		 * @param array $response The response.
		 *
		 * @since 1.0.0
		 *
		 */
		public function process_payouts_item_event( $response ) {

			$resource           = isset( $response['resource'] ) ? $response['resource'] : array();
			$payout_item_id     = $resource['payout_item_id'];
			$transaction_id     = isset( $resource['transaction_id'] ) ? $resource['transaction_id'] : '';
			$transaction_status = strtolower( $resource['transaction_status'] );
			$payout_batch_id    = $resource['payout_batch_id'];
			$payout_item_fee    = $resource['payout_item_fee'];
			$payout_item        = $resource['payout_item'];
			$sender_batch_id    = $resource['sender_batch_id'];
			$sender_item_id     = isset( $resource['payout_item']['sender_item_id'] ) ? $resource['payout_item']['sender_item_id'] : '';

			$receiver = $payout_item['receiver'];

			$args = array(
				'transaction_id'     => $transaction_id,
				'transaction_status' => $transaction_status,
				'payout_batch_id'    => $payout_batch_id,
				'payout_item_id'     => $payout_item_id,
				'amount'             => $payout_item['amount']['value'],
				'fee'                => $payout_item_fee['value'],
				'currency'           => $payout_item['amount']['currency'],
			);

			do_action( 'yith_paypal_payout_item_before_change_status', $payout_item_id, $transaction_status, $resource );

			YITH_Payout_Items::update_payout_item( $sender_batch_id, $receiver, $args );
			if ( '' === $sender_item_id ) {
				$item                                      = YITH_Payout_Items::get_payout_items(
					array(
						'payout_item_id' => $payout_item_id,
						'fields'         => 'sender_item_id',
					)
				);
				$resource['payout_item']['sender_item_id'] = $item[0];
			}

			do_action( 'yith_paypal_payout_item_change_status', $payout_item_id, $transaction_status, $resource );

		}
	}
}
