<?php

namespace PaymentPlugins\WooCommerce\PPCP\Rest\Routes;

use PaymentPlugins\PayPalSDK\Order;
use PaymentPlugins\PayPalSDK\OrderApplicationContext;
use PaymentPlugins\WooCommerce\PPCP\Admin\Settings\AdvancedSettings;
use PaymentPlugins\WooCommerce\PPCP\CheckoutValidator;
use PaymentPlugins\WooCommerce\PPCP\Constants;
use PaymentPlugins\WooCommerce\PPCP\Payments\PaymentGateways;
use PaymentPlugins\WooCommerce\PPCP\Traits\CheckoutRouteTrait;

/**
 * Route that is called when the PayPal integration requests an order ID.
 */
class CartOrder extends AbstractCart {

	use CheckoutRouteTrait;

	private $settings;

	/**
	 * @var \PaymentPlugins\WooCommerce\PPCP\CheckoutValidator
	 */
	private $checkout_validator;

	public function __construct( AdvancedSettings $settings, CheckoutValidator $checkout_validator, ...$args ) {
		parent::__construct( ...$args );
		$this->settings           = $settings;
		$this->checkout_validator = $checkout_validator;
	}

	public function get_path() {
		return 'cart/order';
	}

	public function get_routes() {
		return [
			[
				'methods'  => \WP_REST_Server::CREATABLE,
				'callback' => [ $this, 'handle_request' ],
				'args'     => [
					'payment_method' => [
						'required'          => true,
						'validate_callback' => [ $this->validator, 'validate_payment_method' ]
					]
				]
			]
		];
	}

	public function handle_post_request( \WP_REST_Request $request ) {
		wc_maybe_define_constant( 'WOOCOMMERCE_CHECKOUT', true );
		if ( $this->is_checkout_initiated( $request ) ) {
			// only update the customer data if this is the checkout page since the product and cart page
			// don't have any input fields that should update the customer
			$this->update_customer_data( WC()->customer, $request );

			if ( $this->settings->is_shipping_address_disabled() ) {
				add_action( 'wc_ppcp_get_order_from_cart', function ( Order $order ) {
					// If the application context is allowing the address to be changed, override that.
					if ( $order->getApplicationContext()->getShippingPreference() === OrderApplicationContext::GET_FROM_FILE ) {
						$order->getApplicationContext()->setShippingPreference( OrderApplicationContext::SET_PROVIDED_ADDRESS );
					}
				} );
			}
		}
		$this->populate_post_data( $request );
		$this->calculate_totals();
		$order = $this->get_order_from_cart( $request );
		try {
			$payment_method = $this->get_payment_method_from_request( $request );

			if ( ! $payment_method ) {
				throw new \Exception( __( 'Invalid payment method provided.', 'pymntpl-paypal-woocommerce' ) );
			}

			$this->checkout_validator->run(
				$request,
				$this->is_checkout_initiated( $request ) && $this->is_checkout_validation_enabled( $request ) && $request->get_param( 'payment_method' ) === 'ppcp'
			);

			$result = $this->client->orders->create( $order );
			if ( is_wp_error( $result ) ) {
				/**
				 * @var \WP_Error $result
				 */
				if ( $result->get_error_code() === 'MISSING_SHIPPING_ADDRESS' ) {
					throw new \Exception( __( 'Please enter a valid shipping address.', 'pymntpl-paypal-woocommerce' ) );
				}
				throw new \Exception( $result->get_error_message() );
			}
			$this->cache->set( sprintf( '%s_%s', $payment_method->id, Constants::PAYPAL_ORDER_ID ), $result->id );
			$this->cache->set( Constants::SHIPPING_PREFERENCE, $order->getApplicationContext()->getShippingPreference() );

			$this->logger->info(
				sprintf( 'PayPal order created via %s. Args: %s', __METHOD__, print_r( $result->toArray(), true ) ),
				'payment'
			);

			/**
			 * @param $result  PaymentPlugins\PayPalSDK\Order
			 * @param $request \WP_REST_Request
			 *
			 * @since 1.0.55
			 */
			do_action( 'wc_ppcp_cart_order_created', $result, $request );


			return $result->id;
		} catch ( \Exception $e ) {
			$this->logger->error( sprintf( 'Error creating PayPal order. Msg: %s Params: %s', $e->getMessage(), print_r( $order->toArray(), true ) ) );
			throw new \Exception( $e->getMessage(), $e->getCode() > 0 ? $e->getCode() : 400 );
		}
	}

	/**
	 * @param $error
	 *
	 * @return mixed|void|\WP_Error
	 */
	public function get_error_response( $error ) {
		if ( $error instanceof \Exception && $this->checkout_validator->has_errors() ) {
			return $this->checkout_validator->get_failure_response();
		}

		return parent::get_error_response( $error );
	}

}