<?php
/**
 * Read-only adapter over GiveWP's connected PayPal Commerce account.
 *
 * @since   1.1.0
 * @package DonationRouter
 */

declare( strict_types=1 );
namespace DonationRouter;

use Give\PaymentGateways\PayPalCommerce\Repositories\MerchantDetails;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Exposes GiveWP's already-connected PayPal merchant so the plugin can adopt it
 * into its own managed accounts without a second OAuth connection.
 *
 * All GiveWP access goes through the `give()` service container only — never a
 * direct instantiation of GiveWP internals — per the project's architecture
 * rules. Every accessor fails safe (returns empty/false) when GiveWP is
 * inactive or nothing is connected, so callers never need to guard for a
 * missing gateway.
 *
 * @since 1.1.0
 */
class GiveWP_Merchant_Reader {

	/**
	 * Whether GiveWP currently has a PayPal Commerce account connected.
	 *
	 * @since 1.1.0
	 *
	 * @return bool
	 */
	public function is_connected(): bool {
		$repo = $this->merchant_details();

		if ( null === $repo ) {
			return false;
		}

		try {
			return (bool) $repo->accountIsConnected();
		} catch ( \Throwable $e ) {
			return false;
		}
	}

	/**
	 * The connected merchant's PayPal ID (prefers the in-PayPal merchant ID).
	 *
	 * @since 1.1.0
	 *
	 * @return string  Empty when nothing is connected.
	 */
	public function merchant_id(): string {
		$details = $this->details_array();

		$id = (string) ( $details['merchantIdInPayPal'] ?? '' );
		if ( '' === $id ) {
			$id = (string) ( $details['merchantId'] ?? '' );
		}

		return $id;
	}

	/**
	 * The connected merchant's ISO 3166-1 alpha-2 country, or '' when absent or
	 * malformed.
	 *
	 * @since 1.1.0
	 *
	 * @return string
	 */
	public function account_country(): string {
		$details = $this->details_array();
		$country = strtoupper( (string) ( $details['accountCountry'] ?? '' ) );

		return 1 === preg_match( '/^[A-Z]{2}$/', $country ) ? $country : '';
	}

	/**
	 * The PayPal environment the connected account belongs to.
	 *
	 * @since 1.1.0
	 *
	 * @return string  'sandbox' or 'live'.
	 */
	public function current_mode(): string {
		return give_is_test_mode() ? 'sandbox' : 'live';
	}

	/**
	 * The connected account's credentials in the shape PayPal_Account stores
	 * (decrypted, in-memory) — the inverse of
	 * PayPal_Credential_Router::build_merchant_detail().
	 *
	 * @since 1.1.0
	 *
	 * @return array<string,mixed>  Empty when nothing is connected.
	 */
	public function to_credentials(): array {
		$details = $this->details_array();

		if ( array() === $details ) {
			return array();
		}

		$token = ( isset( $details['token'] ) && is_array( $details['token'] ) )
			? $details['token']
			: array(
				'accessToken' => '',
				'expiresIn'   => 0,
				'tokenType'   => 'Bearer',
			);

		return array(
			'clientId'               => (string) ( $details['clientId'] ?? '' ),
			'clientSecret'           => (string) ( $details['clientSecret'] ?? '' ),
			'merchantIdInPayPal'     => $this->merchant_id(),
			'token'                  => $token,
			'accountIsReady'         => (bool) ( $details['accountIsReady'] ?? true ),
			'supportsCustomPayments' => (bool) ( $details['supportsCustomPayments'] ?? true ),
		);
	}

	/**
	 * Resolve GiveWP's connected MerchantDetail as an array, or [] when nothing
	 * is connected or the gateway is unavailable.
	 *
	 * @since 1.1.0
	 *
	 * @return array<string,mixed>
	 */
	private function details_array(): array {
		$repo = $this->merchant_details();

		if ( null === $repo ) {
			return array();
		}

		try {
			if ( ! $repo->accountIsConnected() ) {
				return array();
			}

			$detail = $repo->getDetails();

			return $detail->toArray();
		} catch ( \Throwable $e ) {
			return array();
		}
	}

	/**
	 * Resolve GiveWP's MerchantDetails repository from the service container.
	 *
	 * @since 1.1.0
	 *
	 * @return MerchantDetails|null  Null when GiveWP or its PayPal gateway is unavailable.
	 */
	private function merchant_details(): ?MerchantDetails {
		// Resolving through the container; a \Throwable (including a missing
		// give() when GiveWP is inactive) means "nothing to read".
		try {
			return give( MerchantDetails::class );
		} catch ( \Throwable $e ) {
			return null;
		}
	}
}
