<?php
/**
 * Endpoint to verify if an order has been approved. An approved order
 * will be stored in the current session.
 *
 * @package WooCommerce\PayPalCommerce\Button\Endpoint
 */

declare(strict_types=1);

namespace WooCommerce\PayPalCommerce\Button\Endpoint;

use Exception;
use Psr\Log\LoggerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Entity\OrderStatus;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderHelper;
use WooCommerce\PayPalCommerce\Button\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\Button\Helper\ContextTrait;
use WooCommerce\PayPalCommerce\Button\Helper\ThreeDSecure;
use WooCommerce\PayPalCommerce\Button\Helper\WooCommerceOrderCreator;
use WooCommerce\PayPalCommerce\Session\SessionHandler;
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
use WooCommerce\PayPalCommerce\WcSubscriptions\Helper\SubscriptionHelper;

/**
 * Class ApproveOrderEndpoint
 */
class ApproveOrderEndpoint implements EndpointInterface {

	use ContextTrait;

	const ENDPOINT = 'ppc-approve-order';

	/**
	 * The request data helper.
	 *
	 * @var RequestData
	 */
	private $request_data;

	/**
	 * The session handler.
	 *
	 * @var SessionHandler
	 */
	private $session_handler;

	/**
	 * The order endpoint.
	 *
	 * @var OrderEndpoint
	 */
	private $api_endpoint;

	/**
	 * The 3d secure helper object.
	 *
	 * @var ThreeDSecure
	 */
	private $threed_secure;

	/**
	 * The settings.
	 *
	 * @var Settings
	 */
	private $settings;

	/**
	 * The DCC applies object.
	 *
	 * @var DccApplies
	 */
	private $dcc_applies;

	/**
	 * The order helper.
	 *
	 * @var OrderHelper
	 */
	protected $order_helper;

	/**
	 * Whether the final review is enabled.
	 *
	 * @var bool
	 */
	protected $final_review_enabled;

	/**
	 * The WC gateway.
	 *
	 * @var PayPalGateway
	 */
	protected $gateway;

	/**
	 * The WooCommerce order creator.
	 *
	 * @var WooCommerceOrderCreator
	 */
	protected $wc_order_creator;

	/**
	 * The logger.
	 *
	 * @var LoggerInterface
	 */
	protected $logger;

	/**
	 * ApproveOrderEndpoint constructor.
	 *
	 * @param RequestData             $request_data The request data helper.
	 * @param OrderEndpoint           $order_endpoint The order endpoint.
	 * @param SessionHandler          $session_handler The session handler.
	 * @param ThreeDSecure            $three_d_secure The 3d secure helper object.
	 * @param Settings                $settings The settings.
	 * @param DccApplies              $dcc_applies The DCC applies object.
	 * @param OrderHelper             $order_helper The order helper.
	 * @param bool                    $final_review_enabled Whether the final review is enabled.
	 * @param PayPalGateway           $gateway The WC gateway.
	 * @param WooCommerceOrderCreator $wc_order_creator The WooCommerce order creator.
	 * @param LoggerInterface         $logger The logger.
	 */
	public function __construct(
		RequestData $request_data,
		OrderEndpoint $order_endpoint,
		SessionHandler $session_handler,
		ThreeDSecure $three_d_secure,
		Settings $settings,
		DccApplies $dcc_applies,
		OrderHelper $order_helper,
		bool $final_review_enabled,
		PayPalGateway $gateway,
		WooCommerceOrderCreator $wc_order_creator,
		LoggerInterface $logger
	) {

		$this->request_data         = $request_data;
		$this->api_endpoint         = $order_endpoint;
		$this->session_handler      = $session_handler;
		$this->threed_secure        = $three_d_secure;
		$this->settings             = $settings;
		$this->dcc_applies          = $dcc_applies;
		$this->order_helper         = $order_helper;
		$this->final_review_enabled = $final_review_enabled;
		$this->gateway              = $gateway;
		$this->wc_order_creator     = $wc_order_creator;
		$this->logger               = $logger;
	}

	/**
	 * The nonce.
	 *
	 * @return string
	 */
	public static function nonce(): string {
		return self::ENDPOINT;
	}

	/**
	 * Handles the request.
	 *
	 * @return bool
	 * @throws RuntimeException When order not found or handling failed.
	 */
	public function handle_request(): bool {
		try {
			$data = $this->request_data->read_request( $this->nonce() );
			if ( ! isset( $data['order_id'] ) ) {
				throw new RuntimeException(
					__( 'No order id given', 'woocommerce-paypal-payments' )
				);
			}

			$order = $this->api_endpoint->order( $data['order_id'] );

			$payment_source = $order->payment_source();
			if ( $payment_source && $payment_source->name() === 'card' ) {
				if ( $this->settings->has( 'disable_cards' ) ) {
					$disabled_cards = (array) $this->settings->get( 'disable_cards' );
					$card           = strtolower( $payment_source->properties()->brand ?? '' );
					if ( 'master_card' === $card ) {
						$card = 'mastercard';
					}

					if ( ! $this->dcc_applies->can_process_card( $card ) || in_array( $card, $disabled_cards, true ) ) {
						throw new RuntimeException(
							__(
								'Unfortunately, we do not accept this card.',
								'woocommerce-paypal-payments'
							),
							100
						);
					}
				}
				$proceed = $this->threed_secure->proceed_with_order( $order );
				if ( ThreeDSecure::RETRY === $proceed ) {
					throw new RuntimeException(
						__(
							'Something went wrong. Please try again.',
							'woocommerce-paypal-payments'
						)
					);
				}
				if ( ThreeDSecure::REJECT === $proceed ) {
					throw new RuntimeException(
						__(
							'Unfortunately, we can\'t accept your card. Please choose a different payment method.',
							'woocommerce-paypal-payments'
						)
					);
				}
				$this->session_handler->replace_order( $order );

				wp_send_json_success();
			}

			if ( $this->order_helper->contains_physical_goods( $order ) && ! $order->status()->is( OrderStatus::APPROVED ) && ! $order->status()->is( OrderStatus::CREATED ) ) {
				$message = sprintf(
				// translators: %s is the id of the order.
					__( 'Order %s is not ready for processing yet.', 'woocommerce-paypal-payments' ),
					$data['order_id']
				);

				$this->logger->log( 'error', $message );
				throw new RuntimeException( $message );
			}

			$funding_source = $data['funding_source'] ?? null;
			$this->session_handler->replace_funding_source( $funding_source );

			$this->session_handler->replace_order( $order );

			if ( apply_filters( 'woocommerce_paypal_payments_toggle_final_review_checkbox', false ) ) {
				$this->toggle_final_review_enabled_setting();
			}

			$should_create_wc_order = $data['should_create_wc_order'] ?? false;
			if ( ! $this->final_review_enabled && ! $this->is_checkout() && $should_create_wc_order ) {
				$wc_order = $this->wc_order_creator->create_from_paypal_order( $order, WC()->cart );
				$this->gateway->process_payment( $wc_order->get_id() );
				$order_received_url = $wc_order->get_checkout_order_received_url();

				wp_send_json_success( array( 'order_received_url' => $order_received_url ) );
			}
			wp_send_json_success();
			return true;
		} catch ( Exception $error ) {
			$this->logger->error( 'Order approve failed: ' . $error->getMessage() );

			wp_send_json_error(
				array(
					'name'    => is_a( $error, PayPalApiException::class ) ? $error->name() : '',
					'message' => $error->getMessage(),
					'code'    => $error->getCode(),
					'details' => is_a( $error, PayPalApiException::class ) ? $error->details() : array(),
				)
			);
			return false;
		}
	}

	/**
	 * Will toggle the "final confirmation" checkbox.
	 *
	 * @return void
	 */
	protected function toggle_final_review_enabled_setting(): void {
		$final_review_enabled_setting = $this->settings->has( 'blocks_final_review_enabled' ) && $this->settings->get( 'blocks_final_review_enabled' );
		$final_review_enabled_setting ? $this->settings->set( 'blocks_final_review_enabled', false ) : $this->settings->set( 'blocks_final_review_enabled', true );
		$this->settings->persist();
	}
}
