<?php
if (!defined('ABSPATH')) {
	exit;
}

class WC_Gateway_Verifone extends WC_Payment_Gateway {
    const HTTP = 'http://';
    const HTTPS = 'https://';
    const WC_API = 'wc-api';
    const GATEWAY_NAME = 'WC_Gateway_Verifone';

    const ONE_CLICK_MAX_AMOUNT = '100';

    /** @var WC_Verifone_Config $_verifoneConfig */
    protected $_verifoneConfig = null;

    protected $_instance = null;

	public $is_checkout_block = false;

    public function __construct() {

        $this->version = WC_VERIFONE_VERSION;
        $this->id = WC_VERIFONE_GATEWAY_ID;
        $this->method_title =esc_html__( 'Verifone eCommerce', 'verifone-bluecommerce-for-woocommerce') ;
        /* translators: 1: a href link 2: closing href */
        $this->method_description = esc_html__( 'Secure, flexible, and easy-to-use payment methods supporting businesses in local and global markets.', 'verifone-bluecommerce-for-woocommerce' );
        $this->has_fields = true;
		$this->is_checkout_block = false;

        if (file_exists(WC_VERIFONE_PLUGIN_PATH . '/assets/img/verifonepayment-logo.png')) {
            $this->icon = plugins_url('assets/img/verifonepayment-logo.png', WC_VERIFONE_MAIN_FILE);
        }

        $this->init_form_fields();
        $this->init_settings();

        $this->_verifoneConfig = WC_Verifone_Config::getInstance();

        $verifoneSettings = new WC_Verifone_Settings();

        if (empty($this->settings['delayed_url'])) {
            $this->settings['delayed_url'] = WC_Verifone_Urls::getPaymentDelayedLink();
        }

        $this->title = $this->get_option('title');

        $this->supports = [
            'products',
            'refunds',
        ];

        if ($this->_verifoneConfig->isAllowToSaveCC()) {
            $this->supports[] = 'add_payment_method';
			$this->supports[] = 'tokenization';

			/**
			 * As we using token based billing and relay on WooCommerce Subscriptions scheduled paymet hook to charge
			 * each recurring payment, we can declare support for all subscription related features.
			 */
			$this->supports[] = 'subscriptions';
			$this->supports[] = 'subscription_cancellation';
			$this->supports[] = 'subscription_suspension';
			$this->supports[] = 'subscription_reactivation';
			$this->supports[] = 'subscription_amount_changes';
			$this->supports[] = 'subscription_date_changes';
			$this->supports[] = 'subscription_payment_method_change';
			$this->supports[] = 'subscription_payment_method_change_customer';
			$this->supports[] = 'multiple_subscriptions';
        }

        // Actions
        add_action('woocommerce_update_options_payment_gateways_' . $this->id, [$this, 'process_admin_options']);
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [$this, 'redirect_after_save'] );

        // Payment listener/API hook
        add_action('woocommerce_api_wc_gateway_verifone', [$this, 'gateway_communication']);

        add_action('verifone_refresh_saved_methods', [WC_Verifone_Observer::class, 'refreshSavedCards'], 10, 1);
        add_action('verifone_add_masked_pan_number', [WC_Verifone_Observer::class, 'addMaskedPanNumber'], 10, 2);

        add_filter('payment_fields', [$this, 'payment_fields']);
    }

    public function get_icon() {
        $icon = $this->icon ? '<img src="' . WC_HTTPS::force_https_url($this->icon) . '" class="verifonepayment-logo" alt="' . esc_attr($this->get_title()) . '" />' : ''; // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions -- this is a static image URL and should be safe to use as is
        return apply_filters('woocommerce_gateway_icon', $icon, $this->id);
    }

    public function init_form_fields() {
        $verifoneSettings = new WC_Verifone_Settings();
        $this->form_fields = $verifoneSettings->getSettings();
    }

    public function payment_fields() {
        if (is_checkout() || $this->is_checkout_block ) {

            $saved = [];
			$paymentMethods = WC_Verifone_PaymentMethods::getAvailablePaymentMethods();
			$usePaymentMethodLogos = $this->_verifoneConfig->displayMethodLogos();

            if (get_current_user_id() ) {
                $saved = WC_Verifone_PaymentMethods::getSavedPaymentMethods(get_current_user_id());
            }

			/**
			 * On block based checkout, saved methods are shown as separate payment option.
			 * On legacy checkout when logos are used, saved methods are listed above other methods.
			 */
			if ( $usePaymentMethodLogos ) {
				$paymentMethods = array(
					'methods' => $paymentMethods,
					'saved' => $this->is_checkout_block ? [] : $saved,
				);
			} else {
				$paymentMethods = array_merge($paymentMethods, $saved);
			}


            $messages = [
                'selectMethod' => esc_html__('Select payment method', 'verifone-bluecommerce-for-woocommerce'),
                'rememberMethod' => esc_html__('Remember payment method.', 'verifone-bluecommerce-for-woocommerce'),
                'rememberMeInfo' => $this->_verifoneConfig->getRememberCCInfo(),
				'rememberCCSubscriptionInfo' => $this->_verifoneConfig->getRememberCCSubscriptionInfo(),
				'subscriptionFreeTrialReservationInfo' => $this->_verifoneConfig->getSubscriptionFreeTrialReservationInfo(),
                'redirectMessage' => esc_html__('You will be redirected to Verifone Payment service to complete your order.', 'verifone-bluecommerce-for-woocommerce')
            ];

            if(!empty($this->_verifoneConfig->getPaymentMessage())) {
                $messages['redirectMessage'] = $this->_verifoneConfig->getPaymentMessage();
            }

            $context = [
                'allowCC' => $this->_verifoneConfig->isAllowToSaveCC(),
				'cartContainsSubscription' => WC_Verifone_Woocommerce_Subscriptions::cart_contains_subscription(),
				'cartTotalIsZero' => WC()->cart && WC()->cart->total == 0,
                'messages' => $messages,
                'paymentMethods' => $paymentMethods
            ];

			if ( $usePaymentMethodLogos ) {
				WC_Verifone_Tpl::render($context, WC_Verifone_Tpl::PAYMENT_METHOD_LOGOS_FORM);
			} else {
				WC_Verifone_Tpl::render($context, WC_Verifone_Tpl::PAYMENT_METHODS_FORM);
			}
        } elseif (is_add_payment_method_page()) {
            echo esc_html__('You will be redirect to Verifone Payment service to process adding payment card.', 'verifone-bluecommerce-for-woocommerce');
        }

    }

    /**
     * Sends and receives data to/from verifone server
     */
    public function gateway_communication() {
        $action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : null; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce

        if ( isset( $_GET['order_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
			$order_id = sanitize_text_field( wp_unslash( $_GET['order_id'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
			$order = wc_get_order( $order_id );

			// Check that is a valid order
			if ( ! $order ) {
				WC_Verifone::log( wp_sprintf( esc_html__( 'Tried to process payment for invalid order #%d', 'verifone-bluecommerce-for-woocommerce' ), $order_id ) );
				header("HTTP/1.1 200 OK");
            	die('<html><head><meta http-equiv="refresh" content="0;url=' . WC_Verifone_Urls::getCheckoutUrl() . '"></head></html>'); // phpcs:ignore WordPress.Security.EscapeOutput -- no user input
			}

			// If order is already paid, do not allow further processing
			if ( $order && $order->is_paid() ) {
				WC_Verifone::log( wp_sprintf( esc_html__( 'Tried to process payment for already paid order #%d', 'verifone-bluecommerce-for-woocommerce' ), $order_id ) );
				header("HTTP/1.1 200 OK");
            	die('<html><head><meta http-equiv="refresh" content="0;url=' . WC_Verifone_Urls::getCheckoutUrl() . '"></head></html>'); // phpcs:ignore WordPress.Security.EscapeOutput -- no user input
			}

			// Check order key
			if ( ! isset( $_GET['key'] ) || $order->get_order_key() !== sanitize_text_field( wp_unslash( $_GET['key'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
				WC_Verifone::log( wp_sprintf( esc_html__( 'Tried to process payment for order #%d with invalid key', 'verifone-bluecommerce-for-woocommerce' ), $order_id ) );
				header("HTTP/1.1 200 OK");
				die('<html><head><meta http-equiv="refresh" content="0;url=' . WC_Verifone_Urls::getCheckoutUrl() . '"></head></html>'); // phpcs:ignore WordPress.Security.EscapeOutput -- no user input
			}

            $data = $this->collectData();
            WC_Verifone_Core_Payment::makePaymentRequest($data);
        } elseif ($action == WC_Verifone_Urls::ACTION_DELAYED_SUCCESS) {
            $this->verifyPaymentResponse(true);
        } elseif ($action == WC_Verifone_Urls::ACTION_ADD_NEW_CARD) {
            $this->verifyCardResponse();
        } else {
            $this->verifyPaymentResponse();
        }

        //exit must be present in this function!
        exit;
    }

    public function collectData() {
        $orderId = isset($_GET['order_id']) ? sanitize_text_field(wp_unslash($_GET['order_id'])) : null; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
        $paymentMethod = isset($_GET['payment_method']) ? sanitize_text_field(wp_unslash($_GET['payment_method'])) : null; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
        $savePaymentMethod = isset($_GET['save_payment_method']) ? sanitize_text_field(wp_unslash($_GET['save_payment_method'])) : null; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce

        $orderData = WC_Verifone_Data::collectData($orderId, $paymentMethod, $savePaymentMethod);

        return $orderData;
    }

    public function verifyPaymentResponse($isDelayed = false) {
        $status = isset($_GET['status']) ? sanitize_text_field(wp_unslash($_GET['status'])) : null; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce

        if ($isDelayed) {
            $response = WC_Verifone_Payment::processSuccess('success', true);
            if (!$response['error']) {
                do_action('verifone_refresh_saved_methods', $response['order']);
            }
            // no session, i.e. late POST from the payment system. We must signal 200 OK.
            header("HTTP/1.1 200 OK");
            die('<html><head><meta http-equiv="refresh" content="0;url=' . WC_Verifone_Urls::getCheckoutUrl() . '"></head></html>'); // phpcs:ignore WordPress.Security.EscapeOutput -- no user input
        } elseif (is_null($status)) {
            $response = WC_Verifone_Payment::processFail('error');
        } elseif ($status == 'success') {
            $response = WC_Verifone_Payment::processSuccess($status);
            if (!$response['error']) {
                do_action('verifone_refresh_saved_methods', $response['order']);
            }
        } else {
            $response = WC_Verifone_Payment::processFail($status);
        }

        if (isset($response['return_url'])) {
            wp_safe_redirect(apply_filters('woocommerce_checkout_no_payment_needed_redirect', $response['return_url'], $response['order']));
        } else {
            wp_safe_redirect(WC_Verifone_Urls::getCheckoutUrl());
        }
    }

    public function process_payment($orderId) {
		$order = wc_get_order($orderId);

        // Reduce stock levels
        wc_maybe_reduce_stock_levels($order->get_id());

        // Clear cart
        /** @var WooCommerce $woocommerce */
        global $woocommerce;
        $woocommerce->cart->empty_cart();

		// Backward compatibility for old field name
        $paymentMethod = isset($_POST['verifone-payment-method']) ? sanitize_text_field(wp_unslash($_POST['verifone-payment-method'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
		if ( empty( $paymentMethod ) && isset( $_POST['verifonepaymentmethod'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
			$paymentMethod = sanitize_text_field( wp_unslash( $_POST['verifonepaymentmethod'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
		}

		// Backward compatibility for old field name
        $savePaymentMethod = isset($_POST['verifone-save-payment-method']) ? sanitize_text_field(wp_unslash($_POST['verifone-save-payment-method'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
		if ( empty( $savePaymentMethod ) && isset( $_POST['verifonepaymentremembermethod'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
			$savePaymentMethod = sanitize_text_field( wp_unslash( $_POST['verifonepaymentremembermethod'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
		}

		$changePaymentMethod = isset($_POST['woocommerce_change_payment']) ? sanitize_text_field(wp_unslash($_POST['woocommerce_change_payment'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
		$changePaymentMethod = absint( $orderId ) === absint( $changePaymentMethod ) ? true : false;

        $params = array(
            'order_id' => $orderId,
			'key' => $order->get_order_key(),
            'payment_method' => $paymentMethod,
            'save_payment_method' => $savePaymentMethod,
			'change_payment_method' => $changePaymentMethod,
        );

        return array(
            'result' => 'success',
            'redirect' => add_query_arg($params, WC_Verifone_Urls::getPaymentLink())
        );
    }

    /**
     * Refund a charge
     * @param  int $order_id
     * @param  float $amount
     * @param string $reason
     * @return bool|WP_Error
     */
    public function process_refund($order_id, $amount = null, $reason = '') {
        $order = wc_get_order($order_id);

        if (!$order || !$order->get_transaction_id()) {
            return false;
        }

        if (is_null($amount)) {
            return true;
        }

        $error = esc_html__('Attempted refund, but rejected by Verifone. Possible reasons: payment not committed, payment has already been refunded or similar. Please try again later, or check status from Verifones TCS portal.', 'verifone-bluecommerce-for-woocommerce');

        try {
            $result = WC_Verifone_Core_Refund::refund($order, $amount);
            if ($result) {
                /** @var Verifone\Core\DependencyInjection\Service\TransactionImpl $body */
                $body = $result->getFull();

				/** translators: %1$s is the amount, %2$s is the refund ID, %3$s is the reason */
                $refundMessage = sprintf(esc_html__('Refunded %1$s - Refund ID: %2$s - Reason: %3$s', 'verifone-bluecommerce-for-woocommerce'), wc_price($amount), $body->getNumber(), $reason);
                $order->add_order_note($refundMessage);

                return true;
            }
        } catch (Exception $e) {
            $error = $e->getMessage();
        }

        return new WP_Error('error', $error);
    }

    public function add_payment_method() {
        if (!is_user_logged_in()) {
            wc_add_notice(esc_html__('There was a problem during adding the card.', 'verifone-bluecommerce-for-woocommerce'), 'error');
            return false;
        }

        $formData = WC_Verifone_Core_SavedPaymentMethods::prepareNewCardRequestFormData();
        WC_Verifone_Core_SavedPaymentMethods::renderRequestForm($formData);

        //exit must be present in this function!
        exit;
    }

    public function verifyCardResponse() {
        $status = isset($_GET['status']) ? sanitize_text_field(wp_unslash($_GET['status'])) : null; // phpcs:ignore WordPress.Security.NonceVerification -- not coming from user input, but from Verifone payment system thus cannot be verified with nonce
        $result = WC_Verifone_Core_SavedPaymentMethods::verifyAddNewCardResponse($status);

        if ($result['success']) {
            do_action('verifone_refresh_saved_methods', null);
            wc_add_notice( $result['message'], 'success');
        } else {
            wc_add_notice( $result['message'], 'error');
        }

        wp_safe_redirect(wc_get_account_endpoint_url('payment-methods'));
    }

	/**
	 * After settings have been saved for our gateway, redirect back to our settings page and show a notice.
	 * This is needed so that our notices and onboardig have the new settings available. Without this redirect,
	 * some old settings might be still in use as save is done later than some of our hooks.
	 */
	public function redirect_after_save() {
		if ( isset($_GET['page'], $_GET['tab'], $_GET['section']) && $_GET['page'] === 'wc-settings' && $_GET['section'] === $this->id ) {
			WC_Verifone_Notice::addSuccess( '<b>' . esc_html__( 'Your settings have been saved.', 'verifone-bluecommerce-for-woocommerce' ) . '</b>', false, true );
			wp_safe_redirect( WC_Verifone_Urls::getSettingsUrl() );
			exit;
		}
	}

    public function admin_options() {
		WC_Verifone_Onboarding::render();

		// Render admin page main content, options and save button
        parent::admin_options();
    }

	public function is_available() {
		$payment_methods = WC_Verifone_PaymentMethods::getAvailablePaymentMethods();
		if ( empty( $payment_methods ) ) {
			return false;
		}

		return parent::is_available();
	}
}

