<?php
namespace RadiusTheme\RadiusBooking\PaymentGateway;

use RadiusTheme\RadiusBooking\Abstracts\AbstractPaymentGateway;
if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}
/**
 * PayPal Payment Gateway
 *
 * Handles PayPal payment processing using PayPal REST API
 */
class PayPalPaymentGateway extends AbstractPaymentGateway {

	/**
	 * PayPal API base URL
	 *
	 * @var  string
	 */
	private $baseUrl;
	/**
	 * PayPal access token
	 *
	 * @var  string
	 */
	private $accessToken;


	/**
	 * Constructor
	 *
	 * @param array $config Configuration options for the PayPal gateway.
	 */
	public function __construct( array $config = array() ) {
		parent::__construct( $config );
		$this->initializePayPal();
	}

	/**
	 * Initialize PayPal client
	 */
	private function initializePayPal() {
		if ( ! $this->isConfigured() ) {
			return;
		}

		// Set base URL based on mode
		$mode          = $this->getConfig( 'mode', 'sandbox' );
		$this->baseUrl = $mode === 'live'
			? 'https://api-m.paypal.com'
			: 'https://api-m.sandbox.paypal.com';
	}

	/**
	 * Get PayPal access token
	 */
	private function getAccessToken(): ?string {
		if ( $this->accessToken ) {
			return $this->accessToken;
		}

		try {
			$clientId     = $this->getConfig( 'client_id' );
			$clientSecret = $this->getConfig( 'client_secret' );

			$response = wp_remote_post(
				$this->baseUrl . '/v1/oauth2/token',
				array(
					'headers' => array(
						'Authorization' => 'Basic ' . base64_encode( "$clientId:$clientSecret" ),
						'Content-Type'  => 'application/x-www-form-urlencoded',
					),
					'body'    => 'grant_type=client_credentials',
					'timeout' => 30,
				)
			);

			if ( is_wp_error( $response ) ) {
				$this->log(
					'error',
					'PayPal token request failed',
					array(
						'error' => $response->get_error_message(),
					)
				);
				return null;
			}

			$body = json_decode( wp_remote_retrieve_body( $response ), true );

			if ( isset( $body['access_token'] ) ) {
				$this->accessToken = $body['access_token'];
				return $this->accessToken;
			}

			return null;

		} catch ( \Exception $e ) {
			$this->log(
				'error',
				'PayPal token generation error',
				array(
					'error' => $e->getMessage(),
				)
			);
			return null;
		}
	}

	/**
	 * Creates a new PayPal order based on the provided order data.
	 *
	 * @param array $orderData The data to create the order. It should include:
	 *                         - 'amount' (float|string): The amount of the order.
	 *                         - 'currency' (string): The currency code, e.g., 'USD'.
	 *                         - 'description' (string, optional): A description of the order.
	 *                         - 'metadata' (array, optional): Additional custom metadata.
	 *                         - 'return_url' (string, optional): URL to redirect after payment.
	 *                         - 'cancel_url' (string, optional): URL to redirect if payment is canceled.
	 *
	 * @return array The response indicating the success or failure of the order creation. On success, it includes details like:
	 *               - 'orderId' (string): The PayPal order ID.
	 *               - 'status' (string): The status of the order (e.g., 'CREATED').
	 *               - 'amount' (string): The formatted order amount.
	 *               - 'currency' (string): The currency code.
	 *               On failure, it provides error details.
	 * @throws \Exception If there is an error during the order creation process.
	 */
	public function createOrder( array $orderData ): array {
		try {
			// Validate required fields
			$validation = $this->validatePaymentData(
				$orderData,
				array(
					'amount',
					'currency',
				)
			);

			if ( ! $validation['valid'] ) {
				return $this->createResponse(
					false,
					'Validation failed',
					array(
						'errors' => $validation['errors'],
					)
				);
			}
			if ( ! $this->isConfigured() ) {
				return $this->createResponse( false, 'PayPal is not configured' );
			}

			$accessToken = $this->getAccessToken();
			if ( ! $accessToken ) {
				return $this->createResponse( false, 'Failed to authenticate with PayPal' );
			}

			// Prepare order data
			$amount   = number_format( (float) $orderData['amount'], 2, '.', '' );
			$currency = strtoupper( $orderData['currency'] ?? 'USD' );

			$payload = array(
				'intent'         => 'CAPTURE',
				'purchase_units' => array(
					array(
						'amount'      => array(
							'currency_code' => $currency,
							'value'         => $amount,
						),
						'description' => $orderData['description'] ?? 'Radius Booking Payment',
					),
				),
			);

			// Add metadata if provided
			if ( ! empty( $orderData['metadata'] ) ) {
				$payload['purchase_units'][0]['custom_id'] = json_encode( $orderData['metadata'] );
			}

			$response = wp_remote_post(
				$this->baseUrl . '/v2/checkout/orders',
				array(
					'headers' => array(
						'Authorization' => 'Bearer ' . $accessToken,
						'Content-Type'  => 'application/json',
					),
					'body'    => json_encode( $payload ),
					'timeout' => 30,
				)
			);

			if ( is_wp_error( $response ) ) {
				throw new \Exception( $response->get_error_message() );
			}

			$body       = json_decode( wp_remote_retrieve_body( $response ), true );
			$statusCode = wp_remote_retrieve_response_code( $response );

			if ( $statusCode !== 201 || ! isset( $body['id'] ) ) {
				throw new \Exception( $body['message'] ?? 'Failed to create PayPal order' );
			}

			$this->log(
				'info',
				'PayPal order created',
				array(
					'order_id' => $body['id'],
					'amount'   => $amount,
				)
			);

			return $this->createResponse(
				true,
				'Order created successfully',
				array(
					'orderId'  => $body['id'],
					'status'   => $body['status'],
					'amount'   => $amount,
					'currency' => $currency,
				)
			);

		} catch ( \Exception $e ) {
			$this->log(
				'error',
				'PayPal order creation error',
				array(
					'error' => $e->getMessage(),
				)
			);

			return $this->createResponse( false, 'Order creation failed: ' . $e->getMessage() );
		}
	}

	/**
	 * Captures a PayPal order based on the provided order ID.
	 *
	 * @param string $orderId The ID of the PayPal order to be captured.
	 *
	 * @return array Returns an array containing the result of the capture operation, including success status, message, and possibly additional data on success.
	 * @throws \Exception If there is an error during the capture process.
	 */
	public function captureOrder( string $orderId ): array {
		try {
			if ( ! $this->isConfigured() ) {
				return $this->createResponse( false, 'PayPal is not configured' );
			}

			$accessToken = $this->getAccessToken();
			if ( ! $accessToken ) {
				return $this->createResponse( false, 'Failed to authenticate with PayPal' );
			}

			$response = wp_remote_post(
				$this->baseUrl . "/v2/checkout/orders/$orderId/capture",
				array(
					'headers' => array(
						'Authorization' => 'Bearer ' . $accessToken,
						'Content-Type'  => 'application/json',
					),
					'timeout' => 30,
				)
			);

			if ( is_wp_error( $response ) ) {
				throw new \Exception( $response->get_error_message() );
			}

			$body       = json_decode( wp_remote_retrieve_body( $response ), true );
			$statusCode = wp_remote_retrieve_response_code( $response );

			if ( $statusCode !== 201 || ! isset( $body['id'] ) ) {
				throw new \Exception( $body['message'] ?? 'Failed to capture PayPal order' );
			}

			// Extract capture details
			$capture = $body['purchase_units'][0]['payments']['captures'][0] ?? null;

			if ( ! $capture ) {
				throw new \Exception( 'No capture information found' );
			}
			$this->log(
				'info',
				'PayPal order captured',
				array(
					'order_id'   => $orderId,
					'capture_id' => $capture['id'],
					'status'     => $body['status'],
				)
			);

			return $this->createResponse(
				true,
				'Payment captured successfully',
				array(
					'orderId'   => $body['id'],
					'status'    => $body['status'],
					'captureId' => $capture['id'],
					'payerId'   => $body['payer']['payer_id'] ?? '',
					'amount'    => $capture['amount']['value'],
					'currency'  => $capture['amount']['currency_code'],
				)
			);

		} catch ( \Exception $e ) {
			$this->log(
				'error',
				'PayPal capture error',
				array(
					'error'    => $e->getMessage(),
					'order_id' => $orderId,
				)
			);

			return $this->createResponse( false, 'Payment capture failed: ' . $e->getMessage() );
		}
	}

	/**
	 * Processes the payment and creates an order.
	 *
	 * @param array $paymentData Array containing the payment details required to create the order.
	 *
	 * @return array Returns an array with the result of the process, including success status and related messages.
	 * @throws \Exception If there is an error during the capture process.
	 */
	public function processPayment( array $paymentData ): array {
		// Create order
		$orderResult = $this->createOrder( $paymentData );

		if ( ! $orderResult['success'] ) {
			return $orderResult;
		}

		// For backend processing, we'd need the order to be approved first
		// This is typically handled by the frontend flow
		return $this->createResponse( false, 'PayPal requires user approval. Use createOrder and captureOrder instead.' );
	}

	/**
	 * Verifies the payment details for a given transaction ID with PayPal.
	 *
	 * @param string $transactionId The unique identifier of the transaction to verify.
	 *
	 * @return array An associative array containing the verification result, status, and additional details.
	 * @throws \Exception If there is an error during the capture process.
	 */
	public function verifyPayment( string $transactionId ): array {
		try {
			if ( ! $this->isConfigured() ) {
				return $this->createResponse( false, 'PayPal is not configured' );
			}

			$accessToken = $this->getAccessToken();
			if ( ! $accessToken ) {
				return $this->createResponse( false, 'Failed to authenticate with PayPal' );
			}

			$response = wp_remote_get(
				$this->baseUrl . "/v2/checkout/orders/$transactionId",
				array(
					'headers' => array(
						'Authorization' => 'Bearer ' . $accessToken,
						'Content-Type'  => 'application/json',
					),
					'timeout' => 30,
				)
			);

			if ( is_wp_error( $response ) ) {
				throw new \Exception( $response->get_error_message() );
			}

			$body = json_decode( wp_remote_retrieve_body( $response ), true );

			if ( ! isset( $body['id'] ) ) {
				throw new \Exception( 'Order not found' );
			}

			$status = $this->mapPayPalStatus( $body['status'] );

			return $this->createResponse(
				true,
				'Order verified',
				array(
					'transaction_id' => $body['id'],
					'status'         => $status,
					'paypal_status'  => $body['status'],
				)
			);

		} catch ( \Exception $e ) {
			$this->log(
				'error',
				'PayPal verification error',
				array(
					'error'          => $e->getMessage(),
					'transaction_id' => $transactionId,
				)
			);

			return $this->createResponse( false, 'Payment verification failed: ' . $e->getMessage() );
		}
	}

	/**
	 * Process payment refund via PayPal.
	 *
	 * @param string $transactionId The unique identifier of the PayPal transaction to be refunded.
	 * @param float $amount The amount to refund. If 0, the entire transaction amount will be refunded.
	 *
	 * @return array An associative array containing the refund status, message, and additional data such as refund ID and refunded amount.
	 * @throws \Exception If there is an error during the capture process.
	 */
	public function refundPayment( string $transactionId, float $amount ): array {
		try {
			if ( ! $this->isConfigured() ) {
				return $this->createResponse( false, 'PayPal is not configured' );
			}

			$accessToken = $this->getAccessToken();
			if ( ! $accessToken ) {
				return $this->createResponse( false, 'Failed to authenticate with PayPal' );
			}

			// First, get the capture ID from the order
			$orderResponse = wp_remote_get(
				$this->baseUrl . "/v2/checkout/orders/$transactionId",
				array(
					'headers' => array(
						'Authorization' => 'Bearer ' . $accessToken,
						'Content-Type'  => 'application/json',
					),
					'timeout' => 30,
				)
			);

			if ( is_wp_error( $orderResponse ) ) {
				throw new \Exception( $orderResponse->get_error_message() );
			}

			$orderBody = json_decode( wp_remote_retrieve_body( $orderResponse ), true );
			$captureId = $orderBody['purchase_units'][0]['payments']['captures'][0]['id'] ?? null;

			if ( ! $captureId ) {
				throw new \Exception( 'Capture ID not found for this order' );
			}

			// Prepare refund data
			$refundData = array();
			if ( $amount > 0 ) {
				$refundData['amount'] = array(
					'value'         => number_format( $amount, 2, '.', '' ),
					'currency_code' => $orderBody['purchase_units'][0]['payments']['captures'][0]['amount']['currency_code'] ?? 'USD',
				);
			}

			// Process refund
			$response = wp_remote_post(
				$this->baseUrl . "/v2/payments/captures/$captureId/refund",
				array(
					'headers' => array(
						'Authorization' => 'Bearer ' . $accessToken,
						'Content-Type'  => 'application/json',
					),
					'body'    => json_encode( $refundData ),
					'timeout' => 30,
				)
			);

			if ( is_wp_error( $response ) ) {
				throw new \Exception( $response->get_error_message() );
			}

			$body       = json_decode( wp_remote_retrieve_body( $response ), true );
			$statusCode = wp_remote_retrieve_response_code( $response );

			if ( $statusCode !== 201 || ! isset( $body['id'] ) ) {
				throw new \Exception( $body['message'] ?? 'Failed to process refund' );
			}

			$this->log(
				'info',
				'PayPal refund processed',
				array(
					'refund_id' => $body['id'],
					'order_id'  => $transactionId,
					'amount'    => $amount,
				)
			);

			return $this->createResponse(
				true,
				'Refund processed successfully',
				array(
					'refund_id'      => $body['id'],
					'transaction_id' => $transactionId,
					'amount'         => $body['amount']['value'] ?? $amount,
					'status'         => $body['status'],
				)
			);

		} catch ( \Exception $e ) {
			$this->log(
				'error',
				'PayPal refund error',
				array(
					'error'          => $e->getMessage(),
					'transaction_id' => $transactionId,
				)
			);

			return $this->createResponse( false, 'Refund failed: ' . $e->getMessage() );
		}
	}

	/**
	 * Maps a PayPal status to a corresponding internal status.
	 *
	 * @param string $paypalStatus The PayPal status to be mapped.
	 *
	 * @return string The corresponding internal status.
	 * @throws \Exception If there is an error during the capture process.
	 */
	private function mapPayPalStatus( string $paypalStatus ): string {
		$statusMap = array(
			'CREATED'               => 'pending',
			'SAVED'                 => 'pending',
			'APPROVED'              => 'authorized',
			'VOIDED'                => 'canceled',
			'COMPLETED'             => 'completed',
			'PAYER_ACTION_REQUIRED' => 'pending',
		);

		return $statusMap[ $paypalStatus ] ?? 'pending';
	}

	/**
	 * Retrieves the name of the gateway.
	 *
	 * @return string The name of the gateway.
	 */
	public function getGatewayName(): string {
		return 'paypal';
	}

	/**
	 * Checks if the system is properly configured with the required credentials.
	 *
	 * @return bool True if both 'client_id' and 'client_secret' are set, false otherwise.
	 */
	public function isConfigured(): bool {
		return ! empty( $this->getConfig( 'client_id' ) ) &&
				! empty( $this->getConfig( 'client_secret' ) );
	}
}
