<?php

namespace Payplug\PayplugWoocommerce\Controller;

use Payplug\PayplugWoocommerce\Gateway\PayplugAddressData;
use Payplug\PayplugWoocommerce\Gateway\PayplugGateway;
use Payplug\PayplugWoocommerce\Interfaces\PayplugGatewayBuilder;
use Payplug\PayplugWoocommerce\PayplugWoocommerceHelper;
use Automattic\WooCommerce\Utilities\OrderUtil;


use Payplug\Authentication;
use Payplug\Exception\ConfigurationException;
use Payplug\Exception\HttpException;
use Payplug\Exception\ForbiddenException;
use Payplug\Payplug;
use Payplug\Resource\Payment as PaymentResource;
use Payplug\Resource\Refund as RefundResource;

class PayplugGenericGateway extends PayplugGateway implements PayplugGatewayBuilder
{

	/**
	 * @var string
	 */
	public $image;

	/**
	 * @var array
	 */
	protected $allowed_country_codes;

	public function __construct()
	{
		parent::__construct();
		add_action('woocommerce_after_order_itemmeta', [$this, 'hide_wc_refund_button']);
	}

	/**
	 * Generic code to fetch payment gateway specific image
	 * @return string
	 */
	public function get_icon()
	{

		//get object $image
		$icons = apply_filters('payplug_payment_icons', [
			'payplug' => sprintf('<img src="%s" alt="%s" class="payplug-payment-icon" />', esc_url(PAYPLUG_GATEWAY_PLUGIN_URL . '/assets/images/checkout/' . $this->image), $this->id . " Icon"),
		]);

		$icons_str = '';
		foreach ($icons as $icon) {
			$icons_str .= $icon;
		}

		return $icons_str;
	}

	/**
	 * @return void
	 */
	public function display_notice()
	{
		$error_message = 'payplug_' . $this->id . '_unauthorized_message';
		?>
		<div class="notice notice-error is-dismissible">
			<p><?php echo __( $error_message, 'payplug' ); ?></p>
		</div>
		<?php
	}

	public function checkGateway()
	{
		//check if module is enabled
		if(!empty($this->settings['enabled']) && 'no' === $this->settings['enabled']){
			return false;
		}

		$account = PayplugWoocommerceHelper::generic_get_account_data_from_options( $this->id );
		$options = PayplugWoocommerceHelper::get_payplug_options();

		if( !$this->check_api_gateway_enable($account)){
			return false;
		}

		if (empty($options) || empty($options[$this->id]) || $options[$this->id] === 'no') {
			return false;
		}


		if(!is_admin() && !PayplugWoocommerceHelper::is_checkout_block()){
			if (empty(WC()->cart) && !is_admin()) {
				return false;
			}

			//for backend orders
			if (!empty(get_query_var('order-pay'))) {
				$order = wc_get_order( (int) get_query_var('order-pay'));
				$items = $order->get_items();
				$country_code_shipping = $order->get_shipping_country();
				$country_code_billing = $order->get_billing_country();
				$this->order_items_to_cart(WC()->cart, $items);
			}

			if ( empty( $country_code_billing ) || empty( $country_code_shipping ) ) {
				$country_code_shipping = method_exists(WC()->customer, "get_shipping_country") ? WC()->customer->get_shipping_country() : null;
				$country_code_billing  = method_exists(WC()->customer, "get_billing_country") ? WC()->customer->get_billing_country() : null;;
			}

			if( !$this->check_billing_country_permissions($account, $country_code_billing) ){
				return false;
			}
		}

		return true;
	}

	/**
	 * check if payment is enable and customer has permissions
	 * @param $account
	 * @return bool
	 */
	protected function check_api_gateway_enable($account){

		if (
			isset( $account["payment_methods"] )  &&
			empty( $account["payment_methods"][ $this->id ] )  &&
			!$account["payment_methods"][ $this->id ]['enabled']
		) {
			return false;
		}

		return true;
	}


	/**
	 * Check if billing country can use this payment method
	 * @param $account
	 * @param $billing_code
	 * @return bool
	 */
	public function check_billing_country_permissions($account, $billing_code){
		$this->allowed_country_codes = !empty($account["payment_methods"][ $this->id ]['allowed_countries']) ? $account["payment_methods"][ $this->id ]['allowed_countries'] : null;

		if (is_array($this->allowed_country_codes)) {
			if ( in_array( "ALL", $this->allowed_country_codes) || empty( $this->allowed_country_codes ) ) {
				return true;
			}

			//check if country is allowed
			if ( in_array( $billing_code, $this->allowed_country_codes ) ) {
				return true;

			} else {
				return false;
			}
		}

		return true;
	}


	/**
	 * if payment was generated by an intend, we shouldn't generate another one and try to pay it, this would generate duplications
	 * @param $order
	 * @return array|null
	 * @throws \Exception
	 */
	private function process_standard_intent_payment($order){

		if ( !is_wc_endpoint_url('order-pay') &&
			empty($_POST["payplug_non_blocks"]) &&
			PayplugWoocommerceHelper::is_checkout_block() &&
			(
				( $this->id === "payplug" && ($this->payment_method === 'integrated'|| $this->payment_method === 'popup') ) ||
				( $this->id === "american_express" && $this->payment_method === 'popup')
			) &&
			!empty($order->get_transaction_id()) ) {

			$order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();

			try {
				$payment = $this->api->payment_retrieve($order->get_transaction_id());
				if (ob_get_length() > 0) {
					ob_clean();
				}


				// Save transaction id for the order
				PayplugWoocommerceHelper::is_pre_30()
					? update_post_meta($order_id, '_transaction_id', $payment->id)
					: $order->set_transaction_id($payment->id);

				if (is_callable([$order, 'save'])) {
					$order->save();
				}

				/**
				 * Fires once a payment has been created.
				 *
				 * @param int $order_id Order ID
				 * @param PaymentResource $payment Payment resource
				 */
				\do_action('payplug_gateway_payment_created', $order_id, $payment);

				$metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
				PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);

				PayplugGateway::log(sprintf('Payment intent created for order #%s', $order_id));

				$return_url = esc_url_raw($order->get_checkout_order_received_url());

				wp_send_json_success(
					array(
						'payment_id' => $payment->id,
						'result' => 'success',
						'redirect' => !empty($payment->hosted_payment->payment_url) ? $payment->hosted_payment->payment_url : $return_url,
						'cancel' => !empty($payment->hosted_payment->cancel_url) ? $payment->hosted_payment->cancel_url : null
					)
				);

				return array("stt" => "OK");

			} catch (HttpException $e) {
				PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
				throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
			} catch (\Exception $e) {
				PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
				throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
			}
		}

		return null;
	}

	public function process_standard_payment($order, $amount, $customer_id)
	{

		$intent = $this->process_standard_intent_payment($order);
		if( !empty($intent) ){
			return $intent;
		}

		$order_id = PayplugWoocommerceHelper::is_pre_30() ? $order->id : $order->get_id();
		try {

			//if there's no auth to process payment
			if ( !$this->checkGateway() ){
				throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
			}

			$address_data = PayplugAddressData::from_order($order);

			$return_url = esc_url_raw($order->get_checkout_order_received_url());

			if (!(substr( $return_url, 0, 4 ) === "http")) {
				$return_url = get_site_url().$return_url;
			}

			$payment_data = [
				'amount'           => $amount,
				'currency'         => get_woocommerce_currency(),
				'payment_method'   => $this->id,
				'billing'          => $address_data->get_billing(),
				'shipping'         => $address_data->get_shipping(),
				'hosted_payment'   => [
					'return_url' => $return_url,
					'cancel_url' => esc_url_raw($order->get_cancel_order_url_raw()),
				],
				'notification_url' => esc_url_raw(WC()->api_request_url('PayplugGateway')),
				'metadata'         => [
					'order_id'    => $order_id,
					'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
					'domain'      => $this->limit_length(esc_url_raw(home_url()), 500),
				],
				"save_card"=> false,
				"force_3ds"=> false
			];

			if (PayplugWoocommerceHelper::is_checkout_block() && is_checkout()) {
				$payment_data['metadata']['woocommerce_block'] = "CHECKOUT";

			} elseif (PayplugWoocommerceHelper::is_cart_block() && is_cart()) {
				$payment_data['metadata']['woocommerce_block'] = "CART";
			}

			/**
			 * Filter the payment data before it's used
			 *
			 * @param array $payment_data
			 * @param int $order_id
			 * @param array $customer_details
			 * @param PayplugAddressData $address_data
			 */
			$payment_data = apply_filters('payplug_gateway_payment_data', $payment_data, $order_id, [], $address_data);
			$payment      = $this->api->payment_create($payment_data);

			// Save transaction id for the order
			PayplugWoocommerceHelper::is_pre_30()
				? update_post_meta($order_id, '_transaction_id', $payment->id)
				: $order->set_transaction_id($payment->id);

			$order->set_payment_method( $this->id );
			$order->set_payment_method_title($this->method_title);

			if (is_callable([$order, 'save'])) {
				$order->save();
			}

			/**
			 * Fires once a payment has been created.
			 *
			 * @param int $order_id Order ID
			 * @param PaymentResource $payment Payment resource
			 */
			\do_action('payplug_gateway_payment_created', $order_id, $payment);

			$metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
			PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);

			PayplugGateway::log(sprintf('Payment creation complete for order #%s', $order_id));

			return [
				'result'   => 'success',
				'redirect' => $payment->hosted_payment->payment_url,
				'cancel'   => $payment->hosted_payment->cancel_url,
			];

		} catch (HttpException $e) {
			PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');
			throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
		} catch (\Exception $e) {
			PayplugGateway::log(sprintf('Error while processing order #%s : %s', $order_id, $e->getMessage()), 'error');
			throw new \Exception(__('Payment processing failed. Please retry.', 'payplug'));
		}

	}

	/**
	 * Process refund for an order paid with PayPlug gateway.
	 *
	 * @param int $order_id
	 * @param null $amount
	 * @param string $reason
	 *
	 * @return bool|\WP_Error
	 */
	public function process_refund($order_id, $amount = null, $reason = ''){

		PayplugGateway::log(sprintf('Processing refund for order #%s', $order_id));

		if( !$this->user_logged_in()){
			PayplugGateway::log(__('You must be logged in with your PayPlug account.', 'payplug'), 'error');
			return new \WP_Error('process_refund_error', __('You must be logged in with your PayPlug account.', 'payplug'));
		}

		$order = wc_get_order($order_id);
		if (!$order instanceof \WC_Order) {
			PayplugGateway::log(sprintf('The order #%s does not exist.', $order_id), 'error');
			return new \WP_Error('process_refund_error', sprintf(__('The order %s does not exist.', 'payplug'), $order_id));
		}

		if ($order->get_status() === "cancelled") {
			PayplugGateway::log(sprintf('The order #%s cannot be refund.', $order_id), 'error');
			return new \WP_Error('process_refund_error', sprintf(__('The order %s cannot be refund.', 'payplug'), $order_id));
		}

		$transaction_id = PayplugWoocommerceHelper::is_pre_30() ? get_post_meta($order_id, '_transaction_id', true) : $order->get_transaction_id();
		if (empty($transaction_id)) {
			PayplugGateway::log(sprintf('The order #%s does not have PayPlug transaction ID associated with it.', $order_id), 'error');
			return new \WP_Error('process_refund_error', __('No PayPlug transaction was found for this order. The refund could not be processed.', 'payplug'));
		}

		/**
		* PPRO gateways feature!
		 */
		if( isset($this->enable_refund) && $this->enable_refund === false){
			add_action('admin_head', [$this, 'hide_wc_refund_button'] );
			PayplugGateway::log(__('payplug_refund_disabled_error', 'payplug'), 'error');
			return new \WP_Error('process_refund_error', __('payplug_refund_disabled_error', 'payplug'));
		}

		$customer_id = PayplugWoocommerceHelper::is_pre_30() ? $order->customer_user : $order->get_customer_id();
		$data = [
			'metadata' => [
				'order_id'    => $order_id,
				'customer_id' => ((int) $customer_id > 0) ? $customer_id : 'guest',
				'refund_from' => 'woocommerce',
			]
		];

		if (!is_null($amount)) {
			$data['amount'] = PayplugWoocommerceHelper::get_payplug_amount($amount);
		}

		if (!empty($reason)) {
			$data['metadata']['reason'] = $reason;
		}

		/**
		 * Filter the refund data before it's used.
		 *
		 * @param array $data
		 * @param int $order_id
		 * @param string $transaction_id
		 */
		$data = apply_filters('payplug_gateway_refund_data', $data, $order_id, $transaction_id);

		try {
			$refund = $this->api->refund_create($transaction_id, $data);

			if($refund->object === "error"){
				PayplugGateway::log(__('payplug_ppro_flag_error', 'payplug'), 'error');
				return new \WP_Error('process_refund_error', __('payplug_ppro_flag_error', 'payplug'));
			}

			/**
			 * Fires once a refund has been created.
			 *
			 * @param int $order_id Order ID
			 * @param RefundResource $refund Refund resource
			 * @param string $transaction_id Transaction id
			 */
			\do_action('payplug_gateway_refund_created', $order_id, $refund, $transaction_id);

			$refund_meta_key = sprintf('_pr_%s', wc_clean($refund->id));
			if (PayplugWoocommerceHelper::is_pre_30()) {
				update_post_meta($order_id, $refund_meta_key, $refund->id);
			} else {
				$order->add_meta_data($refund_meta_key, $refund->id, true);
				$order->save();
			}

			$note = sprintf(__('Refund %s : Refunded %s', 'payplug'), wc_clean($refund->id), wc_price(((int) $refund->amount) / 100));
			if (!empty($refund->metadata['reason'])) {
				$note .= sprintf(' (%s)', esc_html($refund->metadata['reason']));
			}
			$order->add_order_note($note);

			try {
				$payment  = $this->api->payment_retrieve($transaction_id);
				$metadata = PayplugWoocommerceHelper::extract_transaction_metadata($payment);
				PayplugWoocommerceHelper::save_transaction_metadata($order, $metadata);
			} catch (\Exception $e) {
			}

			PayplugGateway::log('Refund process complete for the order.');

			return true;
		} catch (HttpException $e) {
			PayplugGateway::log(sprintf('Refund request error for the order %s from PayPlug API : %s', $order_id, wc_print_r($e->getErrorObject(), true)), 'error');

			return new \WP_Error('process_refund_error', __('The transaction could not be refunded. Please try again.', 'payplug'));
		} catch (\Exception $e) {
			PayplugGateway::log(sprintf('Refund request error for the order %s : %s', $order_id, wc_clean($e->getMessage())), 'error');

			return new \WP_Error('process_refund_error', __('The transaction could not be refunded. Please try again.', 'payplug'));
		}



	}


	/**
	 * Avoid usage of button refund on the BO
	 * @return false|void
	 */
	public function hide_wc_refund_button(){
		global $post;

		$payment_methods = [];

		if ( class_exists("OrderUtil") && OrderUtil::custom_orders_table_usage_is_enabled() ) {
			$order_id = !empty($_GET["id"]) ? $_GET["id"] : null;

		}else{

			if(!empty($post->ID)){
				$order_id = $post->ID;

			}else if( !empty($_GET["id"]) ){
				$order_id = $_GET["id"];

			}else{
				$order_id = null;

			}
		}

		if(empty($order_id)){
			return false;
		}

		$order = new \WC_Order($order_id);
		if (in_array($order->get_payment_method(), $payment_methods)) {
		?>
			<script>
			jQuery(function () {
				jQuery('.refund-items').attr("disabled", true);
			});
		</script>
		<?php
		}
	}

	/**
	 * refund not possible for PPRO payments
	 *
	 * @return void
	 */
	public function refund_not_available($order)
	{
		if ($this->id === $order->get_payment_method() && isset($this->enable_refund) && $this->enable_refund === false) {
			echo "<p style='color: red;'>" . __('payplug_refund_disabled_error', 'payplug') . "</p>";
		}
	}

	/**
	 * Empty the cart and add all order_items
	 *
	 * @param $cart
	 * @param $items
	 * @return void
	 */
	private function order_items_to_cart($cart, $items){
		$cart->empty_cart();
		foreach ($items as $item){
			$cart->add_to_cart($item->get_product_id(), $item->get_quantity(), $item->get_variation_id());
		}
	}

}
