<?php
/**
 * Stripe Connector — Actions
 *
 * Implements the Stripe payment-initiation path called directly by
 * StripePspConnector::initiate_payment().
 *
 * Uses the Stripe Checkout Sessions API (hosted checkout page) via WordPress
 * HTTP functions — no external SDK or client-side JS is required.
 *
 * Stripe API overview:
 *  Base URL  : https://api.stripe.com
 *  Auth      : HTTP Basic with the secret key as the username (password blank).
 *              Alternatively: Authorization: Bearer sk_xxx header.
 *  Create    : POST /v1/checkout/sessions  — returns session ID + hosted URL
 *  Webhook   : POST events signed with HMAC-SHA256 (whsec_…) via Stripe-Signature header
 *
 * @package AmbassadorHub\Connectors\Stripe
 */

declare( strict_types=1 );

namespace AmbassadorHub\Connectors\Stripe;

use AmbassadorHub\Connectors\ConnectorResult;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Executes Stripe payment-initiation and connection-test requests.
 */
final class Actions {

	/** Stripe API base URL. */
	private const API_BASE = 'https://api.stripe.com';

	// -------------------------------------------------------------------------
	// Payment initiation
	// -------------------------------------------------------------------------

	/**
	 * Create a Stripe Checkout Session.
	 *
	 * Called directly by StripePspConnector::initiate_payment().
	 *
	 * @param array<string, mixed> $donation_data Normalised donation payload.
	 * @return ConnectorResult
	 */
	public static function execute_payment_initiation( array $donation_data ): ConnectorResult {
		$secret_key = StripePspConnector::get_secret_key();
		if ( empty( $secret_key ) ) {
			return ConnectorResult::fail(
				esc_html__( 'Stripe secret key is not configured.', 'ambassadorhub' )
			);
		}

		$amount      = (float) ( $donation_data['amount'] ?? 0 );
		$currency    = strtolower( (string) ( $donation_data['currency'] ?? 'eur' ) );
		$donation_id = (int) ( $donation_data['donation_id'] ?? 0 );
		$description = (string) ( $donation_data['description'] ?? esc_html__( 'Donation', 'ambassadorhub' ) );

		// Stripe expects amounts in the smallest currency unit (e.g. cents).
		$unit_amount = (int) round( $amount * 100 );

		$success_url = $donation_data['redirect_url'] ?? home_url( '/' );
		$cancel_url  = $donation_data['cancel_url'] ?? home_url( '/' );

		$body_array = array(
			'mode'                => 'payment',
			'success_url'         => $success_url,
			'cancel_url'          => $cancel_url,
			'client_reference_id' => (string) $donation_id,
			'line_items'          => array(
				array(
					'quantity'   => 1,
					'price_data' => array(
						'currency'     => $currency,
						'unit_amount'  => $unit_amount,
						'product_data' => array(
							'name' => $description,
						),
					),
				),
			),
			'metadata'            => array(
				'donation_id' => (string) $donation_id,
			),
		);

		/**
		 * Allow other plugins to alter the Stripe Checkout Session payload before dispatch.
		 *
		 * @param array $body_array   Assembled Checkout Session parameters.
		 * @param array $donation_data Original donation data.
		 */
		$body_array = apply_filters( 'ambh_stripe_checkout_session_payload', $body_array, $donation_data );

		// External service call — see readme.txt's "External Services" section
		// (Stripe). Checkout session creation: donation amount, currency, and
		// success/cancel URLs, sent when a donor pays via Stripe.
		$response = wp_remote_post(
			self::API_BASE . '/v1/checkout/sessions',
			array(
				'headers' => array(
					'Authorization' => 'Bearer ' . $secret_key,
					'Content-Type'  => 'application/x-www-form-urlencoded',
				),
				'body'    => self::encode_body( $body_array ),
				'timeout' => 15,
			)
		);

		if ( is_wp_error( $response ) ) {
			return ConnectorResult::fail( $response->get_error_message() );
		}

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

		if ( $code < 200 || $code >= 300 ) {
			$error = $body['error']['message'] ?? esc_html__( 'Unknown Stripe API error.', 'ambassadorhub' );
			return ConnectorResult::fail( (string) $error );
		}

		$session_id   = $body['id'] ?? null;
		$checkout_url = $body['url'] ?? null;

		if ( ! $session_id || ! $checkout_url ) {
			return ConnectorResult::fail(
				esc_html__( 'Stripe did not return a checkout URL.', 'ambassadorhub' )
			);
		}

		return ConnectorResult::ok( $session_id, $checkout_url );
	}

	// -------------------------------------------------------------------------
	// Connection test
	// -------------------------------------------------------------------------

	/**
	 * Test the Stripe connection by fetching the current account details.
	 *
	 * Calls GET /v1/account to verify the secret key is valid and returns
	 * the connected account's display name and email.
	 *
	 * @return array{success: bool, name: string, email: string, mode: string, key_hint: string, message: string}
	 */
	public static function test_connection(): array {
		$secret_key = StripePspConnector::get_secret_key();

		if ( empty( $secret_key ) ) {
			return array(
				'success'  => false,
				'name'     => '',
				'email'    => '',
				'mode'     => '',
				'key_hint' => '',
				'message'  => esc_html__( 'Secret key is required.', 'ambassadorhub' ),
			);
		}

		// External service call — see readme.txt's "External Services" section
		// (Stripe). Triggered only when an admin clicks "Test connection" in
		// Settings → Payments; sends the site's own configured secret key,
		// no donor data.
		$response = wp_remote_get(
			self::API_BASE . '/v1/account',
			array(
				'headers'   => array(
					'Authorization' => 'Bearer ' . $secret_key,
				),
				'timeout'   => 10,
				'sslverify' => ! ( defined( 'WP_DEBUG' ) && WP_DEBUG ),
			)
		);

		$invalid = array(
			'success'  => false,
			'name'     => '',
			'email'    => '',
			'mode'     => '',
			'key_hint' => '',
			'message'  => esc_html__( 'Incorrect secret key. Please check your Stripe API key.', 'ambassadorhub' ),
		);

		if ( is_wp_error( $response ) ) {
			return $invalid;
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		$data = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( 200 !== $code || empty( $data ) ) {
			return $invalid;
		}

		$mode = get_option( StripePspConnector::OPTION_MODE, 'test' );

		return array(
			'success'  => true,
			'name'     => (string) ( $data['business_profile']['name'] ?? $data['display_name'] ?? '' ),
			'email'    => (string) ( $data['email'] ?? '' ),
			'mode'     => $mode,
			'key_hint' => substr( $secret_key, 0, 8 ) . '…',
			'message'  => '',
		);
	}

	// -------------------------------------------------------------------------
	// Encoding helper
	// -------------------------------------------------------------------------

	/**
	 * Encode a nested PHP array as an application/x-www-form-urlencoded string
	 * compatible with the Stripe API's bracket notation (param[key]=value).
	 *
	 * PHP's http_build_query handles nested arrays with the [key] syntax that
	 * Stripe expects (e.g. line_items[0][price_data][currency]).
	 *
	 * @param array<string, mixed> $params Nested parameters array.
	 * @return string URL-encoded string.
	 */
	public static function encode_body( array $params ): string {
		return http_build_query( $params, '', '&', PHP_QUERY_RFC3986 );
	}
}
