<?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 (!isset($this->settings['enabled']) || !$this->settings['enabled']) {
			return false;
		}

		// todo: delete this usage to avoid call to getAccount resource each checkout loading
		$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 (!isset($options['payment_methods']) || empty($options['payment_methods'])) {
			return false;
		}

		if (!$options['payment_methods']['configuration'][$this->id]['active']) {
			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)
	{
		$embedded_mode = $this->settings['payment_methods']['configuration']['payplug']['embedded_mode'];
		if (!is_wc_endpoint_url('order-pay') &&
			empty($_POST['payplug_non_blocks']) &&
			PayplugWoocommerceHelper::is_checkout_block() &&
			(
				($this->id === 'payplug' && in_array($embedded_mode, ['integrated', 'popup'])) ||
				($this->id === 'american_express' && 'popup' == $embedded_mode)
			) &&
			!empty($order->get_transaction_id())) {


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

			try {
				$payment = $this->payplug_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 (!empty($payment_data['billing']['landline_phone_number'])) {
				$payment_data['billing']['mobile_phone_number'] = $payment_data['billing']['landline_phone_number'];
			}

			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->payplug_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->payplug_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->payplug_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());
		}
	}

}
