<?php

namespace SugarCalendarBookings\Payments\Gateways\Stripe;

use SugarCalendarBookings\Admin\Settings\PaymentsSettingsTab;
use SugarCalendarBookings\Options;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Support\DataProcessing\InputProcessor;
use WP_Error;

/**
 * Stripe Connect Handler.
 *
 * @since 1.0.0
 */
class StripeConnectHandler {

	/**
	 * Connect error query parameter.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private const CONNECT_ERROR_PARAMETER = 'scb_gateway_connect_error';

	/**
	 * Connect error description query parameter.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private const CONNECT_ERROR_DESCRIPTION_PARAMETER = 'scb_gateway_connect_error_description';

	/**
	 * Connect completion query parameter.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private const CONNECT_COMPLETION_PARAMETER = 'scb_gateway_connect_completion';

	/**
	 * Get base URL.
	 *
	 * @since 1.0.0
	 *
	 * @param array $params URL parameters.
	 *
	 * @return string
	 */
	private function get_base_url( $params = [] ) {

		$base_url = ! defined( 'SCBOOKINGS_STRIPE_CONNECT_BASE_URL' ) ?
			'https://sugarcalendar.com/' :
			SCBOOKINGS_STRIPE_CONNECT_BASE_URL;

		return add_query_arg( $params, $base_url );
	}

	/**
	 * Get site URL.
	 *
	 * @since 1.0.0
	 *
	 * @param bool $test_mode Whether test mode is activated.
	 *
	 * @return string
	 */
	private function get_site_url( $test_mode ) {

		return add_query_arg( 'test_mode', (int) $test_mode, PaymentsSettingsTab::get_url() );
	}

	/**
	 * Get connect URL.
	 *
	 * @since 1.0.0
	 *
	 * @param bool $test_mode Whether test mode is activated.
	 *
	 * @return string
	 */
	public function get_connect_url( $test_mode = false ) {

		return add_query_arg(
			[
				'live_mode'         => (int) ! $test_mode,
				'state'             => rawurlencode( str_pad( wp_rand( wp_rand(), PHP_INT_MAX ), 100, wp_rand(), STR_PAD_BOTH ) ),
				'customer_site_url' => rawurlencode( $this->get_site_url( $test_mode ) ),
			],
			$this->get_base_url(
				[
					'sc_gateway_connect_init'     => 'stripe_connect',
					'error_parameter'             => self::CONNECT_ERROR_PARAMETER,
					'error_description_parameter' => self::CONNECT_ERROR_DESCRIPTION_PARAMETER,
					'completion_parameter'        => self::CONNECT_COMPLETION_PARAMETER,
				]
			)
		);
	}

	/**
	 * Get disconnect URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	public function get_disconnect_url() {

		return add_query_arg(
			'oauth-disconnect-stripe',
			1,
			wp_nonce_url( PaymentsSettingsTab::get_url(), 'oauth-disconnect-stripe' )
		);
	}

	/**
	 * Return Stripe credentials URL.
	 *
	 * @since 1.0.0
	 *
	 * @param bool   $test_mode Whether Stripe is in test mode.
	 * @param string $state     Stripe state auth parameter.
	 *
	 * @return string
	 */
	private function get_credentials_url( $test_mode, $state ) {

		return add_query_arg(
			[
				'live_mode'         => (int) ! $test_mode,
				'state'             => rawurlencode( $state ),
				'customer_site_url' => rawurlencode( $this->get_site_url( $test_mode ) ),
			],
			$this->get_base_url(
				[
					'sc_gateway_connect_credentials' => 'stripe_connect',
				]
			)
		);
	}

	/**
	 * Handle connection requests.
	 *
	 * @since 1.0.0
	 *
	 * @param array $request Request data.
	 *
	 * @return WP_Error|bool
	 */
	public function handle_connection( $request ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		$request['test_mode'] = ! empty( $request['test_mode'] );

		$data = InputProcessor::process(
			$request,
			[
				self::CONNECT_ERROR_PARAMETER      => 'string',
				self::CONNECT_COMPLETION_PARAMETER => 'string',
				'state'                            => 'string|required_if:' . self::CONNECT_COMPLETION_PARAMETER . ',stripe_connect',
				'test_mode'                        => 'boolean|required_if:' . self::CONNECT_COMPLETION_PARAMETER . ',stripe_connect',
			]
		);

		// Bail on error.
		if ( ! empty( $data[ self::CONNECT_ERROR_PARAMETER ] ) ) {
			return new WP_Error( 'oauth-error-connect-stripe' );
		}

		// Bail if not authenticating.
		if (
			empty( $data[ self::CONNECT_COMPLETION_PARAMETER ] ) ||
			$data[ self::CONNECT_COMPLETION_PARAMETER ] !== 'stripe_connect'
		) {
			return false;
		}

		// Bail if validation failed.
		if ( is_wp_error( $data ) ) {
			return new WP_Error( 'oauth-error-connect-stripe' );
		}

		// Bail if unauthorized.
		if ( ! current_user_can( 'manage_options' ) ) {
			return false;
		}

		$state           = $data['state'];
		$test_mode       = (bool) $data['test_mode'];
		$credential_urls = $this->get_credentials_url( $test_mode, $state );
		$response        = wp_remote_get( esc_url_raw( $credential_urls ) );

		if (
			is_wp_error( $response ) ||
			wp_remote_retrieve_response_code( $response ) !== 200 ||
			! wp_remote_retrieve_body( $response )
		) {
			return new WP_Error( 'stripe_credentials_error' );
		}

		$response = json_decode( $response['body'], true );

		$data = InputProcessor::process(
			$response,
			[
				'data'                 => 'required|array',
				'data.publishable_key' => 'required|string',
				'data.secret_key'      => 'required|string',
				'data.stripe_user_id'  => 'required|string',
			]
		);

		if ( is_wp_error( $data ) ) {
			return new WP_Error( 'stripe_credentials_error' );
		}

		$response['data']['user_id'] = $response['data']['stripe_user_id'];

		unset( $response['data']['stripe_user_id'] );

		$this->save_credentials( $response['data'], $test_mode );

		return true;
	}

	/**
	 * Handle disconnection requests.
	 *
	 * @since 1.0.0
	 *
	 * @param array $request Request data.
	 *
	 * @return bool
	 */
	public function handle_disconnection( $request ) {

		// Bail if not disconnecting.
		if (
			! isset( $request['oauth-disconnect-stripe'] ) ||
			! wp_verify_nonce( sanitize_key( $request['_wpnonce'] ), 'oauth-disconnect-stripe' )
		) {
			return false;
		}

		// Bail if unauthorized.
		if ( ! current_user_can( 'manage_options' ) ) {
			return false;
		}

		$this->save_credentials(
			[],
			Plugin::get( Options::class )->get( 'stripe.test_mode' )
		);

		return true;
	}

	/**
	 * Save credentials to options.
	 *
	 * @since 1.0.0
	 *
	 * @param array $credentials Credentials.
	 * @param bool  $test_mode   Whether in test mode.
	 *
	 * @return void
	 */
	private function save_credentials( $credentials, $test_mode ) {

		$credentials = wp_parse_args(
			$credentials,
			[
				'publishable_key' => '',
				'secret_key'      => '',
				'user_id'         => '',
			]
		);

		if ( $test_mode === true ) {
			$data = [
				'test_publishable_key' => $credentials['publishable_key'],
				'test_secret_key'      => $credentials['secret_key'],
				'test_user_id'         => $credentials['user_id'],
			];
		} else {
			$data = [
				'live_publishable_key' => $credentials['publishable_key'],
				'live_secret_key'      => $credentials['secret_key'],
				'live_user_id'         => $credentials['user_id'],
			];
		}

		$data['test_mode'] = $test_mode;
		$options           = Plugin::get( Options::class );

		$options->update(
			[
				'stripe' => $data,
			]
		);

		// Enable Stripe payment method while preserving existing payment settings.
		$existing_payments = $options->get( 'payments', [] );
		$existing_gateways = $existing_payments['gateways'] ?? [];

		// Ensure onsite is enabled as the default payment method.
		$gateway_settings = array_merge(
			$existing_gateways,
			[
				'stripe' => [
					'enabled' => true,
				],
				'onsite' => [
					'enabled' => true,
				],
			]
		);

		$options->update(
			[
				'payments' => array_merge(
					$existing_payments,
					[
						'gateways' => $gateway_settings,
					]
				),
			]
		);
	}
}
