<?php

namespace StoreEngine\Addons\Paddle;

use StoreEngine;
use StoreEngine\Classes\Exceptions\StoreEngineException;
use StoreEngine\Classes\Order;
use StoreEngine\Classes\OrderContext;
use StoreEngine\Payment\Gateways\PaymentGateway;
use StoreEngine\Utils\ArrayUtil;
use StoreEngine\Utils\Formatting;
use StoreEngine\Utils\Helper;

class GatewayPaddle extends PaymentGateway {

	public function __construct() {
		$this->setup();

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

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

	protected function setup() {
		$this->id                 = 'paddle';
		$this->icon               = apply_filters( 'storeengine/paddle_icon', Helper::get_assets_url( 'images/payment-methods/paddle.svg' ) );
		$this->method_title       = __( 'Paddle', 'storeengine' );
		$this->method_description = __( 'Paddle’s checkout securely processes your payment.', 'storeengine' );
		$this->has_fields         = true;
		$this->verify_config      = true;
		$this->supports           = [
			'products',
			// 'refunds',
			'subscriptions',
			'multiple_subscriptions',
			'subscription_cancellation',
			'subscription_reactivation',
			'subscription_suspension',
			'subscription_amount_changes',
			'subscription_date_changes',
			'subscription_payment_method_change_admin',
			'subscription_payment_method_change_customer',
			'subscription_payment_method_change',
		];
	}

	protected function init_admin_fields() {
		$this->admin_fields = [
			'title'                => [
				'label'    => __( 'Title', 'storeengine' ),
				'type'     => 'safe_text',
				'tooltip'  => __( 'Payment method title that the customer will see on your checkout.', 'storeengine' ),
				'default'  => __( 'Paddle', 'storeengine' ),
				'priority' => 0,
			],
			'description'          => [
				'label'    => __( 'Description', 'storeengine' ),
				'type'     => 'textarea',
				'tooltip'  => __( 'Payment method description that the customer will see on your website.', 'storeengine' ),
				'default'  => __( 'Process payments securely with Paddle.', 'storeengine' ),
				'priority' => 0,
			],
			'is_production'        => [
				'label'    => __( 'Is Live Mode?', 'storeengine' ),
				'tooltip'  => __( 'Enable Stripe Live (Production) Mode.', 'storeengine' ),
				'type'     => 'checkbox',
				'default'  => true,
				'priority' => 0,
			],
			'client_token'         => [
				'label'        => __( 'Client Side Token', 'storeengine' ),
				'type'         => 'text',
				'priority'     => 0,
				'dependency'   => [ 'is_production' => true ],
				'description'  => __( 'This can\'t be verify through saving, Verification only happen through checkout process.', 'storeengine' ),
				'autocomplete' => 'none',
				'required'     => true,
			],
			'api_key'              => [
				'label'        => __( 'API Key', 'storeengine' ),
				'type'         => 'password',
				'priority'     => 0,
				'dependency'   => [ 'is_production' => true ],
				'autocomplete' => 'none',
				'required'     => true,
			],
			'test_client_token'    => [
				'label'        => __( 'Client Side Token (Sandbox)', 'storeengine' ),
				'type'         => 'text',
				'priority'     => 0,
				'dependency'   => [ 'is_production' => false ],
				'description'  => __( 'This can\'t be verify through saving, Verification only happen through checkout process.', 'storeengine' ),
				'autocomplete' => 'none',
				'required'     => true,
			],
			'test_api_key'         => [
				'label'        => __( 'API Key (Sandbox)', 'storeengine' ),
				'type'         => 'password',
				'priority'     => 0,
				'dependency'   => [ 'is_production' => false ],
				'autocomplete' => 'none',
				'required'     => true,
			],
			'default_tax_category' => [
				'label'        => __( 'Default Tax Category', 'storeengine' ),
				'type'         => 'select',
				'priority'     => 0,
				'description'  => __( 'Select the default tax category for products(if tax category not selected) sold through this payment method. Selected tax category must be enabled on your Paddle account.', 'storeengine' ),
				'options'      => [
					[
						'label' => __( 'Digital Goods', 'storeengine' ),
						'value' => 'digital-goods',
					],
					[
						'label' => __( 'eBooks', 'storeengine' ),
						'value' => 'ebooks',
					],
					[
						'label' => __( 'Implementation Services', 'storeengine' ),
						'value' => 'implementation-services',
					],
					[
						'label' => __( 'Professional Services', 'storeengine' ),
						'value' => 'professional-services',
					],
					[
						'label' => __( 'SaaS', 'storeengine' ),
						'value' => 'saas',
					],
					[
						'label' => __( 'Software Programming Services', 'storeengine' ),
						'value' => 'software-programming-services',
					],
					[
						'label' => __( 'Standard', 'storeengine' ),
						'value' => 'standard',
					],
					[
						'label' => __( 'Training Services', 'storeengine' ),
						'value' => 'training-services',
					],
					[
						'label' => __( 'Website Hosting', 'storeengine' ),
						'value' => 'website-hosting',
					],
				],
				'default'      => 'digital-goods',
				'autocomplete' => 'none',
				'required'     => true,
			],
			'tax_mode'             => [
				'label'    => __( 'Prices Tax Mode', 'storeengine' ),
				'type'     => 'select',
				'priority' => 0,
				'options'  => [
					[
						'label' => __( 'Internal', 'storeengine' ),
						'value' => 'internal',
					],
					[
						'label' => __( 'External', 'storeengine' ),
						'value' => 'external',
					],
					[
						'label' => __( 'Account Setting', 'storeengine' ),
						'value' => 'account_setting',
					],
				],
				'default'  => 'internal',
				'required' => true,
			],
			'checkout_theme'       => [
				'label'    => __( 'Paddle Theme', 'storeengine' ),
				'type'     => 'select',
				'priority' => 0,
				'options'  => [
					[
						'label' => __( 'Light', 'storeengine' ),
						'value' => 'light',
					],
					[
						'label' => __( 'Dark', 'storeengine' ),
						'value' => 'dark',
					],
				],
				'default'  => 'light',
				'required' => true,
			],
			'checkout_variant'     => [
				'label'    => __( 'Checkout Variant', 'storeengine' ),
				'type'     => 'select',
				'priority' => 0,
				'options'  => [
					[
						'label' => __( 'One page', 'storeengine' ),
						'value' => 'one-page',
					],
					[
						'label' => __( 'Multi page', 'storeengine' ),
						'value' => 'multi-page',
					],
				],
				'default'  => 'one-page',
				'required' => true,
			],
		];
	}

	/**
	 * Verify Config.
	 *
	 * @param array $config
	 *
	 * @return void
	 * @throws StoreEngineException
	 */
	public function verify_config( array $config ) {
		$is_production = $config['is_production'] ?? true;
		if ( $is_production ) {
			$client_token = $config['client_token'] ?? '';
			$api_key      = $config['api_key'] ?? '';
		} else {
			$client_token = $config['test_client_token'] ?? '';
			$api_key      = $config['test_api_key'] ?? '';
		}

		if ( ! $client_token ) {
			throw new StoreEngineException( esc_html__( 'Paddle Client Side Token is required.', 'storeengine' ), 'client-token-is-required', 400 );
		}

		if ( ! $api_key ) {
			throw new StoreEngineException( esc_html__( 'Paddle API Key is required.', 'storeengine' ), 'api-key-is-required', 400 );
		}

		if ( ! $this->is_currency_supported() ) {
			throw new StoreEngineException(
				sprintf(
				/* translators: %1$s the shop currency, %2$s the Paddle currency support page link opening HTML tag, %3$s the link ending HTML tag. */
					esc_html__(
						'Attention: Your current StoreEngine store currency (%1$s) is not supported by Paddle. Please update your store currency to one that is supported by Paddle to ensure smooth transactions. Visit the %2$sPaddle currency support page%3$s for more information on supported currencies.',
						'storeengine'
					),
					esc_html( Formatting::get_currency() ),
					'<a href="' . esc_url( 'https://developer.paddle.com/concepts/sell/supported-currencies#supported-payments' ) . '" target="_blank">',
					'</a>'
				),
				'currency-not-supported',
				null,
				400
			);
		}

		$result = PaddleService::validate_api_key( $api_key, $is_production );

		if ( ! $result ) {
			throw new StoreEngineException( esc_html__( 'Paddle API Key Is Invalid! Please update API key.', 'storeengine' ), 'paddle-api-key-is-invalid', 400 );
		}
	}

	public function is_currency_supported( string $currency = null ): bool {
		if ( ! $currency ) {
			$currency = Formatting::get_currency();
		}

		return in_array( $currency, PaddleService::get_supported_currencies(), true );
	}

	public function is_available(): bool {
		if ( Helper::is_add_payment_method_page() && ! $this->saved_cards ) {
			return false;
		}

		if ( Helper::is_request( 'admin' ) && Helper::is_request( 'ajax' ) ) {
			return parent::is_available();
		}

		if ( Helper::is_request( 'ajax' ) && isset( $_REQUEST['payment_method'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return parent::is_available();
		}

		if ( ! Helper::is_dashboard() && StoreEngine::init()->cart ) {
			/**
			 * Paddle doesn't allow payment for physical products.
			 *
			 * @link https://paddle.com/help/start/intro-to-paddle/what-am-i-not-allowed-to-sell-on-paddle
			 */
			if ( ! $this->is_currency_supported() || StoreEngine::init()->cart->needs_shipping() ) {
				return false;
			}
		}

		return parent::is_available();
	}

	/**
	 * @throws StoreEngineException
	 */
	public function validate_minimum_order_amount( $order ) {
		if ( $order->get_total() * 100 < PaddleService::get_minimum_amount() ) {
			throw new StoreEngineException(
				wp_kses_post(
					sprintf(
					/* translators: %1$s. Minimum allowed amount (including currency symbol) by the gateway. */
						__( 'Sorry, the minimum allowed order total is %1$s to use this payment method.', 'storeengine' ),
						Formatting::price( PaddleService::get_minimum_amount() / 100 )
					)
				),
				'did-not-meet-minimum-amount'
			);
		}
	}

	public function process_payment( Order $order ) {
		$transaction_id = $order->get_transaction_id();

		if ( ! $transaction_id ) {
			return [];
		}

		$transaction = PaddleService::init()->get_transaction( $transaction_id );
		$transaction_statuses = [ 'completed', 'paid' ];

		if (
			is_wp_error( $transaction ) ||
			! in_array( $transaction->status, $transaction_statuses, true ) ||
			$transaction->custom_data->merchant_order_id !== $order->get_id()
		) {
			return [];
		}

		$order_context = new OrderContext( $order->get_status() );
		$order_items   = [];

		foreach ( $order->get_items() as $order_item ) {
			$order_items[ $order_item->get_price_id() ] = [
				'item_id'                  => $order_item->get_id(),
				'quantity'                 => $order_item->get_quantity(),
				'product_id'               => $order_item->get_product_id(),
				'variation_id'             => $order_item->get_variation_id() ?? null,
				'is_digital_auto_complete' => $order_item->get_digital_auto_complete(),
				'paid'                     => false,
			];
		}

		foreach ( $transaction->items as $transaction_item ) {
			$price_id = $transaction_item->price->custom_data->merchant_price_id ?? null;
			if ( ! $price_id || ! isset( $order_items[ $price_id ] ) ) {
				continue;
			}

			$selected_order_item              = $order_items[ $price_id ];
			$order_items[ $price_id ]['paid'] = true;

			if ( $transaction_item->quantity !== $selected_order_item['quantity'] ) {
				$order_item = $order->get_items()[ $selected_order_item['item_id'] ];
				$order_item->set_quantity( $transaction_item->quantity );
				$order_item->set_subtotal( $transaction_item->quantity * $order_item->get_total() );
				$order_item->set_total( $transaction_item->quantity * $order_item->get_total() );
				$order_item->save();
			}
		}

		foreach ( $order_items as $order_item ) {
			if ( $order_item['paid'] ) {
				continue;
			}
			$order->remove_item( $order_item['item_id'] );
			unset( $order_items[ $order_item['item_id'] ] );
		}

		// Set order as paid
		$order->set_paid_status( 'paid' );
		$order_context->proceed_to_next_status( 'process_order', $order, [ 'note' => _x( 'Payment already done.', 'Paddle payment method', 'storeengine' ) ] );
		$order->calculate();
		$order->save();

		return [
			'result'   => 'success',
			'redirect' => $order->get_checkout_order_received_url(),
		];
	}
}
