<?php
/**
 * PeachPay Stripe Credentials Manager.
 *
 * Handles secure storage and retrieval of merchant's Stripe API keys.
 *
 * @package PeachPay
 */

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

/**
 * Manages Stripe API credentials for client-side integration.
 */
final class PeachPay_Stripe_Credentials {

	/**
	 * Option key for storing credentials.
	 */
	const OPTION_KEY = 'peachpay_stripe_credentials';

	/**
	 * Option key for storing account info (connect_id, country, etc.).
	 */
	const ACCOUNT_OPTION_KEY = 'peachpay_stripe_account';

	/**
	 * Encryption method.
	 */
	const ENCRYPTION_METHOD = 'aes-256-cbc';

	/**
	 * Gets the encryption key derived from WordPress salts.
	 *
	 * @return string The encryption key.
	 */
	private static function get_encryption_key() {
		$salt = defined( 'LOGGED_IN_SALT' ) ? LOGGED_IN_SALT : 'peachpay-stripe-default-salt';
		return hash( 'sha256', $salt, true );
	}

	/**
	 * Encrypts a value.
	 *
	 * @param string $value The value to encrypt.
	 * @return string The encrypted value (base64 encoded).
	 */
	private static function encrypt( $value ) {
		if ( empty( $value ) ) {
			return '';
		}

		$iv        = openssl_random_pseudo_bytes( openssl_cipher_iv_length( self::ENCRYPTION_METHOD ) );
		$encrypted = openssl_encrypt( $value, self::ENCRYPTION_METHOD, self::get_encryption_key(), 0, $iv );

		return base64_encode( $iv . $encrypted ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
	}

	/**
	 * Decrypts a value.
	 *
	 * @param string $value The encrypted value (base64 encoded).
	 * @return string The decrypted value.
	 */
	private static function decrypt( $value ) {
		if ( empty( $value ) ) {
			return '';
		}

		$data      = base64_decode( $value ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
		$iv_length = openssl_cipher_iv_length( self::ENCRYPTION_METHOD );
		$iv        = substr( $data, 0, $iv_length );
		$encrypted = substr( $data, $iv_length );

		return openssl_decrypt( $encrypted, self::ENCRYPTION_METHOD, self::get_encryption_key(), 0, $iv );
	}

	/**
	 * Gets all stored credentials.
	 *
	 * @return array The credentials array.
	 */
	private static function get_credentials() {
		return get_option( self::OPTION_KEY, array() );
	}

	/**
	 * Checks if Stripe credentials are configured for the current mode.
	 *
	 * @return bool True if configured, false otherwise.
	 */
	public static function is_configured() {
		$mode = peachpay_is_test_mode() ? 'test' : 'live';
		return ! empty( self::get_secret_key( $mode ) ) && ! empty( self::get_publishable_key( $mode ) );
	}

	/**
	 * Gets the secret key for the specified mode.
	 *
	 * @param string $mode The mode ('test' or 'live'). Defaults to current mode.
	 * @return string The secret key.
	 */
	public static function get_secret_key( $mode = null ) {
		if ( null === $mode ) {
			$mode = peachpay_is_test_mode() ? 'test' : 'live';
		}

		$credentials = self::get_credentials();
		$key         = $mode . '_secret_key';

		if ( ! isset( $credentials[ $key ] ) ) {
			return '';
		}

		return self::decrypt( $credentials[ $key ] );
	}

	/**
	 * Gets the publishable key for the specified mode.
	 *
	 * @param string $mode The mode ('test' or 'live'). Defaults to current mode.
	 * @return string The publishable key.
	 */
	public static function get_publishable_key( $mode = null ) {
		if ( null === $mode ) {
			$mode = peachpay_is_test_mode() ? 'test' : 'live';
		}

		$credentials = self::get_credentials();
		$key         = $mode . '_publishable_key';

		return isset( $credentials[ $key ] ) ? $credentials[ $key ] : '';
	}

	/**
	 * Gets the webhook signing secret.
	 *
	 * @return string The webhook secret.
	 */
	public static function get_webhook_secret() {
		$credentials = self::get_credentials();

		if ( ! isset( $credentials['webhook_secret'] ) ) {
			return '';
		}

		return self::decrypt( $credentials['webhook_secret'] );
	}

	/**
	 * Saves Stripe credentials for the current mode.
	 *
	 * @param array $keys The credentials to save. Expected keys based on mode:
	 *                    For test mode: test_secret_key, test_publishable_key
	 *                    For live mode: live_secret_key, live_publishable_key
	 *                    Optional: webhook_secret.
	 * @return array Result with 'success' and 'message' keys.
	 */
	public static function save_credentials( $keys ) {
		$is_test_mode = peachpay_is_test_mode();
		$mode         = $is_test_mode ? 'test' : 'live';
		$mode_label   = $is_test_mode ? __( 'test', 'peachpay-for-woocommerce' ) : __( 'live', 'peachpay-for-woocommerce' );

		// Get existing credentials to preserve keys for the other mode.
		$credentials = get_option( self::OPTION_KEY, array() );

		// Validate and prepare current mode keys.
		$secret_key_name      = $mode . '_secret_key';
		$publishable_key_name = $mode . '_publishable_key';

		if ( empty( $keys[ $secret_key_name ] ) || empty( $keys[ $publishable_key_name ] ) ) {
			return array(
				'success' => false,
				// translators: %s mode name.
				'message' => sprintf( __( 'Both %s publishable key and secret key are required.', 'peachpay-for-woocommerce' ), $mode_label ),
			);
		}

		// Validate secret key format.
		if ( ! self::validate_secret_key( $keys[ $secret_key_name ], $mode ) ) {
			$expected = $is_test_mode ? 'sk_test_' : 'sk_live_';
			return array(
				'success' => false,
				// translators: %s expected prefix.
				'message' => sprintf( __( 'Invalid secret key format. Expected key starting with %s', 'peachpay-for-woocommerce' ), $expected ),
			);
		}

		// Validate publishable key format.
		if ( ! self::validate_publishable_key( $keys[ $publishable_key_name ], $mode ) ) {
			$expected = $is_test_mode ? 'pk_test_' : 'pk_live_';
			return array(
				'success' => false,
				// translators: %s expected prefix.
				'message' => sprintf( __( 'Invalid publishable key format. Expected key starting with %s', 'peachpay-for-woocommerce' ), $expected ),
			);
		}

		// Validate webhook secret if provided.
		if ( ! empty( $keys['webhook_secret'] ) ) {
			if ( ! self::validate_webhook_secret( $keys['webhook_secret'] ) ) {
				return array(
					'success' => false,
					'message' => __( 'Invalid webhook secret format. Expected key starting with whsec_', 'peachpay-for-woocommerce' ),
				);
			}
		}

		// Verify the keys work by connecting to Stripe.
		$verification = self::verify_and_fetch_account( $keys, $is_test_mode );
		if ( ! $verification['success'] ) {
			return $verification;
		}

		// Save the current mode keys.
		$credentials[ $secret_key_name ]      = self::encrypt( $keys[ $secret_key_name ] );
		$credentials[ $publishable_key_name ] = $keys[ $publishable_key_name ];

		// Save webhook secret if provided.
		if ( ! empty( $keys['webhook_secret'] ) ) {
			$credentials['webhook_secret'] = self::encrypt( $keys['webhook_secret'] );
		}

		// Save credentials.
		update_option( self::OPTION_KEY, $credentials );

		// Save account info.
		update_option( self::ACCOUNT_OPTION_KEY, $verification['account'] );

		return array(
			'success' => true,
			// translators: %s mode name.
			'message' => sprintf( __( 'Stripe %s credentials saved and verified successfully.', 'peachpay-for-woocommerce' ), $mode_label ),
			'account' => $verification['account'],
		);
	}

	/**
	 * Validates a secret key format.
	 *
	 * @param string $key The key to validate.
	 * @param string $mode Expected mode ('test' or 'live').
	 * @return bool True if valid.
	 */
	private static function validate_secret_key( $key, $mode ) {
		$prefix = 'live' === $mode ? 'sk_live_' : 'sk_test_';
		return strpos( $key, $prefix ) === 0;
	}

	/**
	 * Validates a publishable key format.
	 *
	 * @param string $key The key to validate.
	 * @param string $mode Expected mode ('test' or 'live').
	 * @return bool True if valid.
	 */
	private static function validate_publishable_key( $key, $mode ) {
		$prefix = 'live' === $mode ? 'pk_live_' : 'pk_test_';
		return strpos( $key, $prefix ) === 0;
	}

	/**
	 * Validates a webhook secret format.
	 *
	 * @param string $secret The secret to validate.
	 * @return bool True if valid.
	 */
	private static function validate_webhook_secret( $secret ) {
		return strpos( $secret, 'whsec_' ) === 0;
	}

	/**
	 * Verifies credentials by connecting to Stripe and fetching account info.
	 * Uses WordPress HTTP API instead of Stripe SDK for portability.
	 *
	 * @param array $keys The credentials to verify.
	 * @param bool  $use_test_mode Whether to verify test mode keys. If null, will use the first available key.
	 * @return array Result with 'success', 'message', and 'account' keys.
	 */
	private static function verify_and_fetch_account( $keys, $use_test_mode = null ) {
		// Determine which key to use for verification based on mode.
		$secret_key = null;
		$mode       = null;

		if ( null !== $use_test_mode ) {
			// Use specific mode.
			if ( $use_test_mode ) {
				$secret_key = isset( $keys['test_secret_key'] ) ? $keys['test_secret_key'] : null;
				$mode       = 'test';
			} else {
				$secret_key = isset( $keys['live_secret_key'] ) ? $keys['live_secret_key'] : null;
				$mode       = 'live';
			}
		} else {
			// Auto-detect: prefer live if available, else test.
			if ( ! empty( $keys['live_secret_key'] ) ) {
				$secret_key = $keys['live_secret_key'];
				$mode       = 'live';
			} elseif ( ! empty( $keys['test_secret_key'] ) ) {
				$secret_key = $keys['test_secret_key'];
				$mode       = 'test';
			}
		}

		if ( ! $secret_key ) {
			$mode_name = $use_test_mode ? __( 'test', 'peachpay-for-woocommerce' ) : __( 'live', 'peachpay-for-woocommerce' );
			return array(
				'success' => false,
				// translators: %s mode name.
				'message' => sprintf( __( 'No %s secret key provided.', 'peachpay-for-woocommerce' ), $mode_name ),
			);
		}

		// Verify the supplied key against the Stripe API. We use get_with_key() rather than get() because the credentials have not been saved to options yet at this point.
		$result = PeachPay_Stripe_HTTP::get_with_key( '/v1/account', $secret_key );

		if ( is_wp_error( $result ) ) {
			$error_data = $result->get_error_data();
			$http_code  = isset( $error_data['code'] ) ? $error_data['code'] : 0;

			if ( 401 === $http_code ) {
				$mode_name = 'test' === $mode ? __( 'test', 'peachpay-for-woocommerce' ) : __( 'live', 'peachpay-for-woocommerce' );
				return array(
					'success' => false,
					// translators: %s mode name.
					'message' => sprintf( __( 'Invalid %s API key. Please check your Stripe credentials.', 'peachpay-for-woocommerce' ), $mode_name ),
				);
			}

			return array(
				'success' => false,
				'message' => sprintf(
					// translators: %s: Error message from Stripe.
					__( 'Stripe API error: %s', 'peachpay-for-woocommerce' ),
					$result->get_error_message()
				),
			);
		}

		if ( ! isset( $result['id'] ) ) {
			return array(
				'success' => false,
				'message' => __( 'Invalid response from Stripe API.', 'peachpay-for-woocommerce' ),
			);
		}

		return array(
			'success' => true,
			'account' => array(
				'id'            => $result['id'],
				'country'       => isset( $result['country'] ) ? $result['country'] : '',
				'email'         => isset( $result['email'] ) ? $result['email'] : '',
				'business_name' => isset( $result['business_profile']['name'] ) ? $result['business_profile']['name'] : '',
				'capabilities'  => isset( $result['capabilities'] ) ? $result['capabilities'] : array(),
				'mode'          => $mode,
			),
		);
	}

	/**
	 * Gets the Stripe account ID.
	 *
	 * @return string The account ID or empty string if not configured.
	 */
	public static function get_account_id() {
		$account = get_option( self::ACCOUNT_OPTION_KEY, array() );
		return isset( $account['id'] ) ? $account['id'] : '';
	}

	/**
	 * Gets the Stripe account country.
	 *
	 * @return string The country code or empty string if not configured.
	 */
	public static function get_account_country() {
		$account = get_option( self::ACCOUNT_OPTION_KEY, array() );
		return isset( $account['country'] ) ? $account['country'] : '';
	}

	/**
	 * Gets the Stripe account email.
	 *
	 * @return string The email or empty string if not configured.
	 */
	public static function get_account_email() {
		$account = get_option( self::ACCOUNT_OPTION_KEY, array() );
		return isset( $account['email'] ) ? $account['email'] : '';
	}

	/**
	 * Gets the full account info array.
	 *
	 * @return array The account info.
	 */
	public static function get_account() {
		return get_option( self::ACCOUNT_OPTION_KEY, array() );
	}

	/**
	 * Deletes all stored credentials and account info.
	 *
	 * @return bool True on success.
	 */
	public static function delete_credentials() {
		delete_option( self::OPTION_KEY );
		delete_option( self::ACCOUNT_OPTION_KEY );
		delete_option( 'peachpay_stripe_applepay_config' );

		return true;
	}
}

