<?php
/**
 * Stripe PSP Connector
 *
 * Implements the PspConnectorInterface so AmbassadorHub can use Stripe as
 * the active payment service provider.  Uses the Stripe Checkout Sessions API
 * (hosted checkout) — no client-side JS SDK required.
 *
 * @package AmbassadorHub\Connectors\Stripe
 */

declare( strict_types=1 );

namespace AmbassadorHub\Connectors\Stripe;

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

use AmbassadorHub\Admin\InlineAssets;
use AmbassadorHub\Connectors\AbstractPspConnector;
use AmbassadorHub\Connectors\ConnectorResult;

/**
 * Stripe PSP connector.
 *
 * Registered once with the ConnectorRegistry under the 'psp' slot.
 * Payment initiation (phase 1) creates a Stripe Checkout Session and
 * redirects the donor to Stripe's hosted checkout page.
 * Webhook callbacks (phase 2) are handled by WebhookHandler.
 */
final class StripePspConnector extends AbstractPspConnector {

	// -------------------------------------------------------------------------
	// ConnectorInterface — core identity
	// -------------------------------------------------------------------------

	public const ID   = 'stripe_psp';
	public const TYPE = 'psp';
	public const LOGO = AMBASSADOR_HUB_URL . 'src/Connectors/Stripe/assets/logo.svg';

	// -------------------------------------------------------------------------
	// Option key constants
	// -------------------------------------------------------------------------

	/** API mode: 'test' or 'live'. */
	public const OPTION_MODE = 'ambh_stripe_mode';

	/** Stripe secret key for test mode (sk_test_…). */
	public const OPTION_TEST_SECRET_KEY = 'ambh_stripe_test_secret_key';

	/** Stripe secret key for live mode (sk_live_…). */
	public const OPTION_LIVE_SECRET_KEY = 'ambh_stripe_live_secret_key';

	/**
	 * Stripe webhook signing secret (whsec_…).
	 *
	 * Found in the Stripe Dashboard under Developers → Webhooks after adding
	 * the endpoint URL.  Required for webhook signature verification.
	 */
	public const OPTION_WEBHOOK_SECRET = 'ambh_stripe_webhook_secret';

	/** Transient key for the cached connection-test result. */
	private const CONN_RESULT_TRANSIENT = 'ambh_stripe_psp_conn_result';

	// -------------------------------------------------------------------------
	// API helpers
	// -------------------------------------------------------------------------

	/**
	 * Return the Stripe secret key for the currently configured mode.
	 *
	 * @return string Secret key, or empty string when not configured.
	 */
	public static function get_secret_key(): string {
		$mode = get_option( self::OPTION_MODE, 'test' );

		return 'live' === $mode
			? (string) get_option( self::OPTION_LIVE_SECRET_KEY, '' )
			: (string) get_option( self::OPTION_TEST_SECRET_KEY, '' );
	}

	// -------------------------------------------------------------------------
	// ConnectorInterface — core identity
	// -------------------------------------------------------------------------

	/**
	 * {@inheritdoc}
	 */
	public static function get_label(): string {
		return esc_html__( 'Stripe', 'ambassadorhub' );
	}

	/**
	 * {@inheritdoc}
	 */
	public static function get_description(): string {
		return esc_html__( 'Accept card payments, iDEAL, SEPA, and more via Stripe Checkout.', 'ambassadorhub' );
	}

	// -------------------------------------------------------------------------
	// ConnectorInterface — availability
	// -------------------------------------------------------------------------

	/**
	 * {@inheritdoc}
	 *
	 * Available when the secret key for the active mode is configured.
	 */
	public static function is_available(): bool {
		if ( empty( self::get_secret_key() ) ) {
			return false;
		}

		// If a connection test has been cached, use its result.
		$result = get_transient( self::CONN_RESULT_TRANSIENT );
		if ( false !== $result ) {
			return (bool) ( $result['success'] ?? false );
		}

		return true; // Key present but not yet tested.
	}

	// -------------------------------------------------------------------------
	// ConnectorInterface — settings fields
	// -------------------------------------------------------------------------

	/**
	 * {@inheritdoc}
	 */
	public static function get_settings_fields(): array {
		$webhook_url = rest_url( 'ambassadorhub/v1/webhook/stripe' );

		return array(
			array(
				'type' => 'description',
				'text' => sprintf(
					/* translators: %s: Stripe Dashboard link */
					__( 'Enter your Stripe API keys. You can find them in the <a href="%s" target="_blank" rel="noopener noreferrer">Stripe Dashboard</a> under Developers → API keys.', 'ambassadorhub' ),
					'https://dashboard.stripe.com/apikeys'
				),
			),
			array(
				'id'      => self::OPTION_MODE,
				'type'    => 'select',
				'title'   => esc_html__( 'Mode', 'ambassadorhub' ),
				'options' => array(
					'test' => esc_html__( 'Test', 'ambassadorhub' ),
					'live' => esc_html__( 'Live', 'ambassadorhub' ),
				),
				'default' => 'test',
			),
			array(
				'id'          => self::OPTION_TEST_SECRET_KEY,
				'type'        => 'password',
				'title'       => esc_html__( 'Test secret key', 'ambassadorhub' ),
				'description' => esc_html__( 'Starts with sk_test_. Used when Mode is set to Test.', 'ambassadorhub' ),
			),
			array(
				'id'          => self::OPTION_LIVE_SECRET_KEY,
				'type'        => 'password',
				'title'       => esc_html__( 'Live secret key', 'ambassadorhub' ),
				'description' => esc_html__( 'Starts with sk_live_. Used when Mode is set to Live.', 'ambassadorhub' ),
			),
			array(
				'id'          => self::OPTION_WEBHOOK_SECRET,
				'type'        => 'password',
				'title'       => esc_html__( 'Webhook signing secret', 'ambassadorhub' ),
				'description' => sprintf(
					/* translators: %s: webhook endpoint URL */
					__( 'Starts with whsec_. Add <code>%s</code> as a webhook endpoint in the Stripe Dashboard (Developers → Webhooks), then paste the signing secret here.', 'ambassadorhub' ),
					esc_html( $webhook_url )
				),
			),
		);
	}

	// -------------------------------------------------------------------------
	// ConnectorInterface — registration callback
	// -------------------------------------------------------------------------

	/**
	 * {@inheritdoc}
	 *
	 * Registers the webhook REST route so it can receive payment status updates
	 * from Stripe.
	 */
	public static function on_registration(): void {

		// Load webhook endpoint handler so it can register its REST route.
		add_action( 'ambh_register_rest_routes', array( WebhookHandler::class, 'register_route' ) );

		// Flush the connection-test cache whenever credentials or mode change.
		add_action( 'update_option_' . self::OPTION_MODE, array( self::class, 'on_creds_updated' ) );
		add_action( 'update_option_' . self::OPTION_TEST_SECRET_KEY, array( self::class, 'on_creds_updated' ) );
		add_action( 'update_option_' . self::OPTION_LIVE_SECRET_KEY, array( self::class, 'on_creds_updated' ) );
	}

	/**
	 * Called when Stripe credentials or mode are updated.
	 *
	 * Clears the cached connection-test result so the next page load retests
	 * with the freshly-saved credentials.
	 *
	 * @return void
	 */
	public static function on_creds_updated(): void {
		delete_transient( self::CONN_RESULT_TRANSIENT );
	}

	// -------------------------------------------------------------------------
	// Settings rendering
	// -------------------------------------------------------------------------

	/**
	 * {@inheritdoc}
	 *
	 * Renders the settings form and, below it, the live connection status.
	 */
	public static function render_settings(): void {
		parent::render_settings();

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only flag set by our own redirect.
		$just_saved = isset( $_GET['updated'] ) && '1' === $_GET['updated'];

		// Clear stale cache before is_available() reads it, so saved credentials are always re-tested.
		if ( $just_saved ) {
			delete_transient( self::CONN_RESULT_TRANSIENT );
		}

		if ( ! self::is_available() ) {
			return;
		}

		$result = get_transient( self::CONN_RESULT_TRANSIENT );
		if ( false === $result ) {
			$result = Actions::test_connection();
			set_transient( self::CONN_RESULT_TRANSIENT, $result, DAY_IN_SECONDS );
		}

		$success  = (bool) ( $result['success'] ?? false );
		$name     = (string) ( $result['name'] ?? '' );
		$email    = (string) ( $result['email'] ?? '' );
		$mode     = (string) ( $result['mode'] ?? '' );
		$key_hint = (string) ( $result['key_hint'] ?? '' );
		$message  = (string) ( $result['message'] ?? '' );

		$mode_label = 'live' === $mode
			? esc_html__( 'Live', 'ambassadorhub' )
			: esc_html__( 'Test', 'ambassadorhub' );

		?>
		<div class="ambh-conn-status ambh-conn-status--<?php echo esc_attr( $success ? 'ok' : 'error' ); ?>">
			<?php if ( $success ) : ?>
				<span class="ambh-conn-status__icon" aria-hidden="true">&#10003;</span>
				<span class="ambh-conn-status__text">
					<?php
					echo esc_html(
						sprintf(
							/* translators: %s: Test or Live */
							__( 'Connected · %s', 'ambassadorhub' ),
							$mode_label
						)
					);
					if ( '' !== $name ) {
						echo ' <span class="ambh-conn-status__sub">(' . esc_html( $name );
						if ( '' !== $email ) {
							echo ' · ' . esc_html( $email );
						}
						echo ')</span>';
					} elseif ( '' !== $key_hint ) {
						echo ' <span class="ambh-conn-status__sub">(' . esc_html( $key_hint ) . ')</span>';
					}
					?>
				</span>
			<?php else : ?>
				<span class="ambh-conn-status__icon" aria-hidden="true">&#10007;</span>
				<span class="ambh-conn-status__text">
					<?php echo esc_html( $message ? $message : __( 'Connection failed.', 'ambassadorhub' ) ); ?>
				</span>
			<?php endif; ?>
		</div>
		<?php

		// Auto-expand this settings panel when the credentials were just saved.
		if ( $just_saved ) {
			$panel_id = 'ambh-psp-' . sanitize_html_class( self::ID ) . '-settings';

			InlineAssets::script(
				'(function () {
					var panelId = ' . wp_json_encode( $panel_id ) . ';
					var panel   = document.getElementById( panelId );
					var btn     = document.querySelector( \'[aria-controls="\' + panelId + \'"]\' );
					if ( panel ) { panel.removeAttribute( \'hidden\' ); }
					if ( btn )   { btn.setAttribute( \'aria-expanded\', \'true\' ); }
				})();'
			);
		}
	}

	// -------------------------------------------------------------------------
	// PspConnectorInterface
	// -------------------------------------------------------------------------

	/**
	 * {@inheritdoc}
	 *
	 * Creates a Stripe Checkout Session and returns the hosted-checkout redirect URL.
	 *
	 * @param array<string, mixed> $donation_data Normalised donation payload.
	 * @return ConnectorResult
	 */
	public static function initiate_payment( array $donation_data ): ConnectorResult {
		return Actions::execute_payment_initiation( $donation_data );
	}
}
