<?php
namespace Svea_Webpay_For_Woocommerce\Gateways;

use Svea\WebPay\Constant\PaymentMethod;
use Svea\WebPay\WebPay;
use Svea\WebPay\WebPayItem;
use Svea\WebPay\WebPayAdmin;
use Svea_Webpay_For_Woocommerce\Helper;
use Svea_Webpay_For_Woocommerce\Gateways\WC_Gateway_Abstract;
use Svea_Webpay_For_Woocommerce\Traits\Country_Currency_Availability;
use Svea_Webpay_For_Woocommerce\Traits\Default_Payment_Callback;
use Svea_Webpay_For_Woocommerce\Traits\Phone_Format;
use Svea_Webpay_For_Woocommerce\Traits\Singleton;
use WC_Order;
use WP_Error;

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

/**
 * Class to handle Swish payments through Svea
 */
class WC_Gateway_Svea_Swish extends WC_Gateway_Abstract {
	use Singleton, Phone_Format, Default_Payment_Callback, Country_Currency_Availability;

	/**
	 * Id of this gateway
	 *
	 * @var string
	 */
	const GATEWAY_ID = 'sveawebpay_swish';

	/**
	 * Is the log enabled
	 *
	 * @var bool
	 */
	public static $log_enabled = false;

	/**
	 * @inheritDoc
	 */
	public $id = self::GATEWAY_ID;

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

	/**
	 * @inheritDoc
	 */
	public $enabled_country_currencies = [
		'SE' => 'SEK',
	];

	/**
	 * Allowed countries and their phone country codes
	 *
	 * @var string[]
	 */
	public $phone_country_codes = [
		'SE' => '46',
	];

	/**
	 * Constructor for the class and setup of hooks and object variables
	 */
	public function __construct() {
		$this->supports = [
			'products',
		];

		$this->method_title = __( 'Svea Swish', 'svea-webpay-for-woocommerce' );
		$this->icon = apply_filters( 'woocommerce_sveawebpay_swish_icon', WC_SVEAWEBPAY_GATEWAY_PLUGIN_URL . 'assets/img/swish_logo_secondary_light_bg.png' );

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

		$this->title = __( $this->get_option( 'title' ), 'svea-webpay-for-woocommerce' ); // phpcs:ignore

		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, [ $this, 'process_admin_options' ] );
		add_action( 'woocommerce_api_wc_gateway_svea_swish', [ $this, 'handle_callback_request' ] );

		// Merchant set fields
		$this->enabled = $this->get_option( 'enabled' );

		if ( ! isset( WC()->customer ) ) {
			return;
		}
		$customer_country = strtolower( WC()->customer->get_billing_country() );
		$this->swish_payment_method = $this->get_option( 'swish_payment_method_' . $customer_country );
		static::$log_enabled = $this->get_option( 'debug' ) === 'yes';

		$this->description = __( $this->get_option( 'description' ), 'svea-webpay-for-woocommerce' ); // phpcs:ignore

		if ( $this->enabled !== 'yes' ) {
			return;
		}

		$this->set_config();
	}

	/**
	 * Display payment fields at checkout
	 *
	 * @return void
	 */
	public function payment_fields() {
		echo wp_kses_post( $this->description );

		woocommerce_form_field(
			'svea_swish_phone_number',
			[
				'type'        => 'text',
				'placeholder' => '07X-XXX XX XX',
				'required'    => true,
				'class'       => [ 'form-row-wide' ],
				'label'       => __( 'Phone number', 'svea-webpay-for-woocommerce' ),
			],
			isset( $post_data['svea_swish_phone_number'] ) ? $post_data['svea_swish_phone_number'] : false
		);
	}

	/**
	 * Enables much more complex descriptions
	 *
	 * @param string $description the description for the provided gateway
	 * @param int $id the id of the provided gateway
	 * @return string the gateway description
	 */
	public function get_gateway_description( $description, $id ) {
		if ( $this->id !== $id ) {
			return $description;
		}

		if ( ! isset( $this->description[0] ) ) {
			$this->description = '<div>' . __( $this->get_option( 'description' ), 'svea-webpay-for-woocommerce' ) . '</div>'; // phpcs:ignore
		}

		return $this->description;
	}

	/**
	 * Form fields for Settings
	 *
	 * @return void
	 */
	public function init_form_fields() {
		$this->form_fields = [
			'enabled'                 => [
				'title'   => __( 'Enable/disable', 'svea-webpay-for-woocommerce' ),
				'type'    => 'checkbox',
				'label'   => __( 'Enable Svea Swish', 'svea-webpay-for-woocommerce' ),
				'default' => 'no',
			],
			'title'                   => [
				'title'       => __( 'Title', 'svea-webpay-for-woocommerce' ),
				'type'        => 'text',
				'description' => __( 'This controls the title which the user sees during checkout', 'svea-webpay-for-woocommerce' ),
				'default'     => __( 'Swish', 'svea-webpay-for-woocommerce' ),
			],
			'description'             => [
				'title'       => __( 'Description', 'svea-webpay-for-woocommerce' ),
				'type'        => 'textarea',
				'description' => __( 'This controls the description the user sees during checkout', 'svea-webpay-for-woocommerce' ),
				'default'     => __( 'Pay with Swish through Svea', 'svea-webpay-for-woocommerce' ),
			],
			// Sweden specific settings
			'merchant_sweden_hr'      => [
				'title' => '<hr>',
				'type'  => 'title',
			],
			'merchant_sweden'         => [
				'title' => __( 'Sweden', 'svea-checkout-for-woocommerce' ),
				'type'  => 'title',
			],
			'merchant_id_se'          => [
				'title'       => __( 'Merchant id', 'svea-webpay-for-woocommerce' ),
				'type'        => 'text',
				'description' => __( 'Your Svea Stand Alone merchant id', 'svea-webpay-for-woocommerce' ),
				'default'     => '',
			],
			'secret_word_se'          => [
				'title'       => __( 'Secret word', 'svea-webpay-for-woocommerce' ),
				'type'        => 'password',
				'description' => __( 'Your Svea Stand Alone secret word', 'svea-webpay-for-woocommerce' ),
				'default'     => '',
			],
			'testmode_se'             => [
				'title'       => __( 'Test mode', 'svea-webpay-for-woocommerce' ),
				'type'        => 'checkbox',
				'description' => __( 'Enable test mode for Svea Swish', 'svea-webpay-for-woocommerce' ),
				'default'     => 'no',
			],
			'swish_payment_method_se' => [
				'title'       => __( 'Swish payment method', 'svea-webpay-for-woocommerce' ),
				'type'        => 'select',
				'options'     => [
					PaymentMethod::SWISH    => __( 'Swish', 'svea-webpay-for-woocommerce' ),
					PaymentMethod::SWISH_PF => __( 'Swish (via payment facilitator)', 'svea-webpay-for-woocommerce' ),
				],
				'description' => __( 'Set the Swish payment method you use for processing payments', 'svea-webpay-for-woocommerce' ),
				'default'     => PaymentMethod::SWISH,
			],

			// Other settings
			'merchant_other_hr'       => [
				'title' => '<hr>',
				'type'  => 'title',
			],
			'merchant_other'          => [
				'title' => __( 'Other', 'svea-checkout-for-woocommerce' ),
				'type'  => 'title',
			],
			'debug'                   => [
				'title'       => __( 'Debug log', 'svea-webpay-for-woocommerce' ),
				'type'        => 'checkbox',
				'label'       => __( 'Enable logging', 'svea-webpay-for-woocommerce' ),
				'default'     => 'no',
				// translators: %s is the log file path
				'description' => __( 'Log Svea events, such as payment requests', 'svea-webpay-for-woocommerce' ),
			],
			'disable_order_sync'      => [
				'title'       => __( 'Disable automatic order sync', 'svea-webpay-for-woocommerce' ),
				'type'        => 'checkbox',
				'description' => __(
					"Disable automatic syncing of orders in WooCommerce to Svea. <br />
					If you enable this option, your refunded orders will not be refunded in Svea. <br />
					Your delivered orders will not be delivered in Svea and your cancelled orders will not be cancelled in Svea. <br />
					<strong>Don't touch this if you don't know what you're doing</strong>.",
					'svea-webpay-for-woocommerce'
				),
				'default'     => 'no',
			],
		];
	}

	/**
	 * Process refunds
	 *
	 * @param   string  $order_id   id of the order being refunded
	 * @param   float   $amount     amount being refunded
	 * @param   string  $reason     reason of the refund
	 *
	 * @return  boolean
	 */
	public function process_refund( $order_id, $amount = null, $reason = '' ) {
		$wc_order = new WC_Order( $order_id );

		$svea_order_id = $wc_order->get_meta( '_svea_order_id' );

		if ( strlen( (string) $svea_order_id ) <= 0 ) {
			return false;
		}

		$credit_name = __( 'Refund', 'svea-webpay-for-woocommerce' );

		if ( strlen( $reason ) > 0 )
			$credit_name .= ': ' . $reason;

		$response = WebPayAdmin::creditOrderRows( $this->config )
			->setOrderId( $svea_order_id )
			->setCountryCode( $wc_order->get_billing_country() )
			->setPayerAlias( $wc_order->get_meta( '_svea_swish_number' ) )
			->addCreditOrderRow(
				WebPayItem::orderRow()
					->setAmountExVat( (float) $amount )
					->setVatPercent( 0 )
					->setDiscountPercent( 0 )
					->setName( $credit_name )
					->setQuantity( 1 )
			)
			->creditCardOrderRows()
			->doRequest();

		if ( ! $response || ! isset( $response->accepted ) || ! $response->accepted ) {
			return new WP_Error( 'error', $response->errormessage ?? __( 'Unknown error', 'svea-webpay-for-woocommerce' ) );
		}

		return true;
	}

	/**
	 * Process payment and redirect ot the checkout payment url
	 *
	 * @param int $order_id The id of the order being processed
	 * @return array
	 */
	public function process_payment( $order_id ) {
		$wc_order = wc_get_order( $order_id );

		$order_number = $wc_order->get_meta( '_svea_order_number' );

		if ( $order_number === false || strlen( (string) $order_number ) <= 0 ) {
			$order_number = 1;
		} else {
			$order_number = (int) $order_number;
			$order_number += 1;
		}

		$wc_order->update_meta_data( '_svea_order_number', $order_number );
		$wc_order->update_meta_data( '_svea_swish_number', $this->get_payment_phone_number( 'swish', $wc_order->get_billing_country() ) );

		$wc_order->save();
		$this->set_config( $wc_order->get_billing_country() );
		$config = $this->get_config();

		$customer_country = $wc_order->get_billing_country();

		$customer_first_name = $wc_order->get_billing_first_name();
		$customer_last_name = $wc_order->get_billing_last_name();
		$customer_address_1 = $wc_order->get_billing_address_1();
		$customer_address_2 = $wc_order->get_billing_address_2();
		$customer_zip_code = $wc_order->get_billing_postcode();
		$customer_city = $wc_order->get_billing_city();
		$customer_country = $wc_order->get_billing_country();
		$customer_email = $wc_order->get_billing_email();
		$customer_phone = $wc_order->get_billing_phone();

		$customer_information = WebPayItem::individualCustomer()
			->setName( $customer_first_name, $customer_last_name )
			->setStreetAddress( $customer_address_1 )
			->setZipCode( $customer_zip_code )
			->setLocality( $customer_city )
			->setIpAddress( isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '' )
			->setEmail( $customer_email )
			->setPhoneNumber( $customer_phone )
			->setCoAddress( $customer_address_2 );

		self::log( sprintf( 'Creating order by id %s', $order_id ) );

		$svea_order = Helper::create_svea_order( $wc_order, $config );

		$wc_return_url = esc_url_raw(
			add_query_arg(
				[
					'wc-api'       => 'wc_gateway_svea_swish',
					'order-id'     => $wc_order->get_id(),
					'key'          => $wc_order->get_order_key(),
					'svea-country' => $customer_country,
				],
				$this->get_return_url( $wc_order )
			)
		);

		$wc_callback_url = esc_url_raw(
			add_query_arg(
				[
					'wc-api'        => 'wc_gateway_svea_swish',
					'order-id'      => $wc_order->get_id(),
					'key'           => $wc_order->get_order_key(),
					'svea-country'  => $customer_country,
					'svea-callback' => true,
				],
				$this->get_return_url( $wc_order )
			)
		);

		/**
		 * If the payment was issued in the checkout page,
		 * send the user back if they cancel the order in Svea.
		 */
		if ( is_checkout() ) {
			$wc_cancel_url = esc_url_raw( wc_get_checkout_url() );
		} else {
			$wc_cancel_url = esc_url_raw( $wc_order->get_cancel_order_url() );
		}

		try {
			$request = $svea_order->addCustomerDetails( $customer_information )
				->setClientOrderNumber( apply_filters( 'woocommerce_sveawebpay_swish_client_order_number', $wc_order->get_order_number() ) . '_' . $order_number )
				->setCurrency( $wc_order->get_currency() )
				->setPayerAlias( $wc_order->get_meta( '_svea_swish_number' ) )
				->setCountryCode( $customer_country )
				->usePaymentMethod( $this->swish_payment_method )
				->setReturnUrl( $wc_return_url )
				->setCallbackUrl( $wc_callback_url )
				->setCancelUrl( $wc_cancel_url )
				->setPayPageLanguage( 'sv' );

			$response = $request->getPaymentUrl();

		} catch ( \Exception $e ) {
			wc_add_notice( __( 'An error occurred whilst connecting to Svea. Please contact the store owner and display this message.', 'svea-webpay-for-woocommerce' ), 'error' );
			self::log( 'Received error when creating order: ' . $e->getMessage() );
			return [
				'result' => 'failure',
			];
		}

		if ( ! $response || ! isset( $response->accepted ) || ! $response->accepted ) {
			if ( isset( $response->resultcode ) ) {
				wc_add_notice( Helper::get_svea_error_message( (int) $response->resultcode ), 'error' );
				self::log( 'Received error message from Svea: ' . $response->errormessage );
			} else if ( isset( $response->errormessage ) ) {
				wc_add_notice( $response->errormessage, 'error' );
				self::log( 'Received error message from Svea: ' . $response->errormessage );
			} else {
				wc_add_notice( __( 'An unknown error occurred. Please contact the store owner about this issue.', 'svea-webpay-for-woocommerce' ), 'error' );
				self::log( 'Error: Response error message was empty' );
			}

			return [
				'result' => 'failure',
			];
		}

		$payment_url = $this->testmode ? $response->testurl : $response->url;

		if ( ! is_string( $payment_url ) || ! strlen( $payment_url ) > 0 ) {
			wc_add_notice( __( 'An unknown error occurred. Please contact the store owner about this issue.', 'svea-webpay-for-woocommerce' ), 'error' );
			self::log( 'Error: Payment URL was empty' );
			return [
				'result' => 'failure',
			];
		}

		self::log( 'User redirected to payment URL' );

		return [
			'result'   => 'success',
			'redirect' => $payment_url,
		];
	}

	/**
	 * Cancels the order in svea
	 *
	 * @param WC_Order $order the order being cancelled
	 * @param string $svea_order_id id of the svea order
	 *
	 * @return array an array containing result and message
	 */
	public function cancel_order( $wc_order, $svea_order_id ) {
		$this->set_config( $wc_order->get_billing_country() );
		$config = $this->get_config();

		$response = WebPayAdmin::cancelOrder( $config )
					->setCountryCode( $wc_order->get_billing_country() )
					->setOrderId( $svea_order_id )
					->cancelCardOrder()
					->doRequest();

		if ( ! $response || ! isset( $response->accepted ) || ! $response->accepted ) {
			return [
				'success' => false,
				'message' => $response->errormessage ?? '',
			];
		}

		/**
		 * The request was successful
		 */
		$wc_order->add_order_note(
			__( 'The order has been cancelled in Svea.', 'svea-webpay-for-woocommerce' )
		);

		$wc_order->update_status( 'cancelled' );

		return [
			'success' => true,
			'message' => __( 'The order has been cancelled in Svea.', 'svea-webpay-for-woocommerce' ),
		];
	}

	/**
	 * Credits the order in svea
	 *
	 * @param   WC_Order    $order  the order being credited
	 * @param   string      $svea_order_id  id of the svea order
	 * @return  array       an array containing result and message
	 */
	public function credit_order( $order, $svea_order_id ) {
		$this->set_config( $order->get_billing_country() );
		$config = $this->get_config();

		$credit_order_request = WebPayAdmin::creditOrderRows( $config )
				->setCountryCode( $order->get_billing_country() )
				->setOrderId( $svea_order_id );

		$order_tax_percentage = 0;

		if ( $order->get_total_tax() > 0 ) {
			$order_tax_percentage = ( $order->get_total_tax() / ( $order->get_total() - $order->get_total_tax() ) ) * 100;
		}

		$credit_order_request->addCreditOrderRow(
			WebPayItem::orderRow()
			->setAmountExVat( $order->get_total() - $order->get_total_tax() )
				->setVatPercent( $order_tax_percentage )
				->setQuantity( 1 )
				->setDescription(
					// translators: %s is the Svea order ID
					sprintf( __( 'Credited order #%s' ), $svea_order_id )
				)
		);

		$response = $credit_order_request->creditCardOrderRows()
			->doRequest();

		if ( ! $response || ! isset( $response->accepted ) || ! $response->accepted ) {
			return [
				'success' => false,
				'message' => $response->errormessage ?? '',
			];
		}

		foreach ( array_keys( $order->get_items( [ 'line_item', 'fee', 'shipping' ] ) ) as $order_item_id ) {
			if ( wc_get_order_item_meta( $order_item_id, 'svea_credited' ) ) {
				continue;
			}

			wc_update_order_item_meta( $order_item_id, 'svea_credited', date( 'Y-m-d H:i:s' ) );
		}

		$order->update_status( 'refunded' );

		$order->add_order_note(
			__( 'All items have been credited in Svea.', 'svea-webpay-for-woocommerce' )
		);

		return [
			'success' => true,
			'message' => __( 'All items have been credited in Svea.', 'svea-webpay-for-woocommerce' ),
		];
	}

	/**
	 * Delivers the order in svea
	 *
	 * @param   WC_Order    $order  the order being delivered
	 * @param   string      $svea_order_id  id of the svea order
	 * @return  array       an array containing result and message
	 */
	public function deliver_order( $order, $svea_order_id ) {
		return [
			'success' => true,
			'message' => __( 'Order has already been delivered', 'svea-webpay-for-woocommerce' ),
		];
	}
}
