<?php
/**
 * PostGate Stripe Payment Class
 *
 * Registers REST API endpoints used to create Stripe Checkout sessions
 * for one-time post purchases and subscriptions. These public endpoints handle
 * authentication checks, access validation, and delegate session creation
 * to the PostGate_Stripe_Redirector class.
 *
 * This class serves as the entry point for all payment-initiated actions in the plugin,
 * managing the flow from user intent to Stripe Checkout or login redirect.
 *
 * @package PostGate
 * @subpackage Payments
 * @since 1.0.0
 * @author QodeSphere
 * @license GPL-2.0+
 */

defined( 'ABSPATH' ) || exit;

/**
 * PostGate Stripe Payment Class
 *
 * Provides REST API endpoints for initiating Stripe Checkout sessions.
 * This class implements the payment initiation layer with the following features:
 *
 * - Two public REST endpoints for payment initiation
 * - Authentication flow with login redirects and intent preservation
 * - Access validation to prevent duplicate purchases
 * - Parameter validation and sanitization
 * - Session creation delegation to PostGate_Stripe_Redirector
 * - Origin post tracking for subscriptions and one-time purchases
 * - Standardized error responses using HTTP status codes
 *
 * REST API Endpoints:
 * - POST /postgate/v1/create-one-time-checkout: One-time content purchase
 * - POST /postgate/v1/create-subscription-checkout: Recurring subscription
 *
 * Response Patterns:
 * - 200: Success with Stripe Checkout URL or access confirmation
 * - 400: Validation error (missing/invalid parameters)
 * - 401: Unauthorized with login redirect URL and encoded intent
 *
 * Authentication Flow:
 * 1. Unauthenticated users receive login redirect with encoded purchase intent
 * 2. After login, PostGate_Login_Handler processes intent and creates checkout
 * 3. Users complete purchase in Stripe Checkout
 * 4. Webhook processes payment and grants access
 *
 * The class uses constants for all HTTP status codes and route definitions
 * to maintain consistency and enable easy maintenance.
 *
 * @since 1.0.0
 */
class PostGate_Stripe_Payment {
	/**
	 * REST API namespace used for Stripe payment endpoints.
	 *
	 * @since 1.0.0
	 */
	private const REST_NS = 'postgate/v1';

	/**
	 * Route for one-time checkout creation.
	 *
	 * @since 1.0.0
	 */
	private const ROUTE_ONE_TIME = '/create-one-time-checkout';

	/**
	 * Route for subscription checkout creation.
	 *
	 * @since 1.0.0
	 */
	private const ROUTE_SUBSCRIPTION = '/create-subscription-checkout';

	/**
	 * HTTP 200 - Success response.
	 *
	 * @since 1.0.0
	 */
	private const HTTP_OK = 200;

	/**
	 * HTTP 400 - Bad request (validation errors).
	 *
	 * @since 1.0.0
	 */
	private const HTTP_BAD_REQUEST = 400;

	/**
	 * HTTP 401 - Unauthorized (login required).
	 *
	 * @since 1.0.0
	 */
	private const HTTP_UNAUTHORIZED = 401;

	/**
	 * Initialize the Stripe payment REST API registration.
	 *
	 * Registers the rest_api_init action hook to set up payment endpoints
	 * during WordPress REST API initialization. This method should be called
	 * during plugin bootstrap.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public static function init(): void {
		add_action( 'rest_api_init', array( __CLASS__, 'register_routes' ) );
	}

	/**
	 * Register REST API routes for Stripe Checkout session creation.
	 *
	 * Registers two public POST endpoints:
	 * - /postgate/v1/create-one-time-checkout: For single content purchases
	 * - /postgate/v1/create-subscription-checkout: For recurring subscriptions
	 *
	 * Both routes are publicly accessible to allow unauthenticated purchase attempts.
	 * The permission_callback validates the request structure (post_id validity)
	 * before processing. Authentication and access checks are handled within each
	 * endpoint method:
	 * - Unauthenticated users receive login redirects with encoded purchase intent
	 * - Authenticated users proceed to checkout session creation
	 *
	 * This approach allows seamless handling of both authenticated and
	 * unauthenticated purchase attempts with appropriate redirects.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public static function register_routes(): void {
		register_rest_route( self::REST_NS, self::ROUTE_ONE_TIME, [
			'methods' => 'POST',
			'callback' => array( __CLASS__, 'create_one_time_checkout_session' ),
			'permission_callback' => array( __CLASS__, 'validate_checkout_request' ),
			'args' => [
				'post_id' => [
					'required' => true,
					'type' => 'integer',
					'validate_callback' => function ( $param ) {
						return is_numeric( $param ) && $param > 0;
					},
				],
				'price_id' => [
					'required' => true,
					'type' => 'string',
				],
				'product_id' => [
					'required' => false,
					'type' => 'string',
					'default' => '',
				],
			],
		] );

		register_rest_route( self::REST_NS, self::ROUTE_SUBSCRIPTION, [
			'methods' => 'POST',
			'callback' => array( __CLASS__, 'create_subscription_checkout_session' ),
			'permission_callback' => array( __CLASS__, 'validate_checkout_request' ),
			'args' => [
				'post_id' => [
					'required' => true,
					'type' => 'integer',
					'validate_callback' => function ( $param ) {
						return is_numeric( $param ) && $param > 0;
					},
				],
				'price_id' => [
					'required' => true,
					'type' => 'string',
				],
				'product_id' => [
					'required' => false,
					'type' => 'string',
					'default' => '',
				],
			],
		] );
	}

	/**
	 * Validate checkout session request parameters.
	 *
	 * This permission callback performs early validation before the main checkout
	 * handlers execute. It ensures that:
	 * 1. The post_id parameter is a valid positive integer
	 * 2. The referenced post exists and is published
	 *
	 * This validation prevents processing invalid requests and provides clear
	 * error messages for malformed requests. Authentication checks happen in the
	 * main callback methods.
	 *
	 * Note: Paywall configuration is not validated here because paywalls can be
	 * configured globally (site-wide settings, post type settings) or via blocks,
	 * not just post meta. The checkout flow can only be initiated from posts with
	 * active paywalls, so additional validation is unnecessary.
	 *
	 * @since 1.0.0
	 *
	 * @param WP_REST_Request $request The REST request object.
	 *
	 * @return true|WP_Error True if request is valid, WP_Error otherwise.
	 */
	public static function validate_checkout_request( WP_REST_Request $request ) {
		$post_id = absint( $request->get_param( 'post_id' ) );

		if ( $post_id <= 0 ) {
			return new WP_Error(
				'invalid_post_id',
				__( 'Payment Failed: Invalid post ID. The content you\'re trying to purchase could not be identified. Please refresh the page and try again.', 'postgate' ),
				[ 'status' => 400 ]
			);
		}

		$post = get_post( $post_id );
		if ( ! $post || $post->post_status !== 'publish' ) {
			return new WP_Error(
				'invalid_post',
				__( 'Payment Failed: The requested content is not available or has been unpublished. Please contact support if this issue persists.', 'postgate' ),
				[ 'status' => 404 ]
			);
		}

		return true;
	}

	/**
	 * Create a Stripe Checkout session for a one-time content purchase.
	 *
	 * Handles the complete flow for one-time content purchases:
	 * 1. Extract and sanitize required parameters (price_id, post_id)
	 * 2. Validate price_id is provided (required for Stripe)
	 * 3. Validate post_id is a positive integer (required for access grant)
	 * 4. Check authentication status:
	 *    - If not logged in: Return login redirect with encoded purchase intent
	 *    - If logged in: Continue to step 5
	 * 5. Check if user already has access to prevent duplicate purchases
	 *    - If has access: Return success message
	 *    - If no access: Continue to step 6
	 * 6. Delegate to PostGate_Stripe_Redirector for session creation
	 * 7. Return Stripe Checkout URL or error
	 *
	 * The post_id parameter serves dual purposes:
	 * - origin_post_id: Tracks where the purchase was initiated for analytics and redirects
	 * - unlocked_content calculation: Pre-calculates which posts this purchase will unlock
	 *
	 * Login Intent Flow:
	 * When unauthenticated, the endpoint encodes purchase details as JSON and embeds
	 * them in a 'postgate_intent' URL parameter. After successful login,
	 * PostGate_Login_Handler processes this intent and creates the checkout session.
	 *
	 * @since 1.0.0
	 *
	 * @param WP_REST_Request $request REST request object containing:
	 *                                 - price_id (required): Stripe price ID for the content
	 *                                 - post_id (required): WordPress post ID being purchased
	 *
	 * @return WP_REST_Response|WP_Error Response object:
	 *                                   - 200: {url: "checkout.stripe.com/..."} or {message: "Already purchased"}
	 *                                   - 400: WP_Error for validation failures
	 *                                   - 401: {redirect: "wp-login.php?..."} for unauthenticated users
	 *                                   - 500: WP_Error from Stripe API failures
	 */
	public static function create_one_time_checkout_session( WP_REST_Request $request ) {
		// Extract and sanitize input parameters
		$raw_price_id = $request->get_param( 'price_id' );
		$raw_post_id = $request->get_param( 'post_id' );
		$raw_product_id = $request->get_param( 'product_id' );

		// Sanitize inputs - price_id is a Stripe identifier, post_id must be positive integer
		$price_id = $raw_price_id ? sanitize_text_field( (string) $raw_price_id ) : '';
		$post_id = $raw_post_id ? absint( $raw_post_id ) : 0;
		$product_id = $raw_product_id ? sanitize_text_field( (string) $raw_product_id ) : '';

		// Validate required price_id parameter
		if ( '' === $price_id ) {
			return new WP_Error( 'postgate_missing_price_id', __( 'Price ID is required.', 'postgate' ), array( 'status' => self::HTTP_BAD_REQUEST ) );
		}

		// Validate required post_id parameter - must be positive integer
		if ( $post_id <= 0 ) {
			return new WP_Error( 'postgate_invalid_post_id', __( 'A valid post ID is required.', 'postgate' ), array( 'status' => self::HTTP_BAD_REQUEST ) );
		}

		// Handle unauthenticated users - redirect to login
		// After login, user will return to the post and need to click purchase button again
		// This ensures all checkout requests are protected by WordPress REST API nonces
		if ( ! is_user_logged_in() ) {
			// Simple redirect to login page, redirecting back to post after login
			// Note: Purchase intent is NOT preserved to satisfy WordPress.org security requirements.
			// User will need to click the purchase button again after logging in.
			// Future enhancement tracked in GitHub Issue #32
			$redirect_url = get_permalink( $post_id );
			$login_url = wp_login_url( $redirect_url );

			return new WP_REST_Response( array( 'redirect' => esc_url_raw( $login_url ) ), self::HTTP_UNAUTHORIZED );
		}

		$user_id = get_current_user_id();

		// Check if user already has access to avoid duplicate purchases
		if ( PostGate_Access_Manager::user_has_access( $user_id, $post_id ) ) {
			return new WP_REST_Response( array( 'message' => __( 'Already purchased.', 'postgate' ) ), self::HTTP_OK );
		}

		// Delegate actual Stripe session creation to the redirector class
		// This handles Stripe API calls, fee calculation, and session configuration
		$session_url = PostGate_Stripe_Redirector::create_one_time_checkout_session_url( $user_id, $post_id, $price_id, $product_id );

		// Handle errors from Stripe session creation
		if ( is_wp_error( $session_url ) ) {
			return $session_url;
		}

		// Return successful response with Stripe checkout URL
		return new WP_REST_Response( array( 'url' => esc_url_raw( $session_url ) ), self::HTTP_OK );
	}

	/**
	 * Create a Stripe Checkout session for a recurring subscription.
	 *
	 * Handles the complete flow for subscription purchases:
	 * 1. Extract and sanitize required price_id parameter
	 * 2. Extract and sanitize optional post_id parameter (for origin tracking)
	 * 3. Validate price_id is provided (required for Stripe)
	 * 4. Check authentication status:
	 *    - If not logged in: Return login redirect with encoded subscription intent
	 *    - If logged in: Continue to step 5
	 * 5. Delegate to PostGate_Stripe_Redirector for subscription session creation
	 * 6. Return Stripe Checkout URL or error
	 *
	 * Key Differences from One-Time Purchases:
	 * - No duplicate access check: Users can change plans or reactivate subscriptions
	 * - post_id is optional: Subscriptions may not be tied to specific posts
	 * - Origin tracking only: post_id serves as origin_post_id for redirects/analytics
	 *
	 * The post_id parameter (when provided):
	 * - origin_post_id: Tracks where subscription was initiated
	 * - Redirect target: Where to send user after successful subscription
	 * - Not stored as dedicated column: Only used for origin tracking
	 *
	 * Subscription Behavior:
	 * Unlike one-time purchases, subscriptions grant site-wide or category-wide access
	 * based on subscription configuration rather than individual post access. The
	 * actual access is determined by webhook processing after successful payment.
	 *
	 * @since 1.0.0
	 *
	 * @param WP_REST_Request $request REST request object containing:
	 *                                 - price_id (required): Stripe subscription price ID
	 *                                 - post_id (optional): WordPress post ID for origin tracking
	 *
	 * @return WP_REST_Response|WP_Error Response object:
	 *                                   - 200: {url: "checkout.stripe.com/..."}
	 *                                   - 400: WP_Error for validation failures
	 *                                   - 401: {redirect: "wp-login.php?..."} for unauthenticated users
	 *                                   - 500: WP_Error from Stripe API failures
	 */
	public static function create_subscription_checkout_session( WP_REST_Request $request ) {
		// Extract and sanitize price_id parameter
		$raw_price_id = $request->get_param( 'price_id' );
		$price_id = $raw_price_id ? sanitize_text_field( (string) $raw_price_id ) : '';

		// Extract and sanitize post_id parameter for origin tracking
		$raw_post_id = $request->get_param( 'post_id' );
		$post_id = $raw_post_id ? absint( $raw_post_id ) : 0;

		// Extract and sanitize product_id parameter (optional, for product-based architecture)
		$raw_product_id = $request->get_param( 'product_id' );
		$product_id = $raw_product_id ? sanitize_text_field( (string) $raw_product_id ) : '';

		// Validate required price_id parameter
		if ( '' === $price_id ) {
			return new WP_Error( 'postgate_missing_price_id', __( 'Price ID is required.', 'postgate' ), array( 'status' => self::HTTP_BAD_REQUEST ) );
		}

		// Handle unauthenticated users - redirect to login
		// After login, user will return to the post and need to click purchase button again
		// This ensures all checkout requests are protected by WordPress REST API nonces
		if ( ! is_user_logged_in() ) {
			// Simple redirect to login page, redirecting back to post/home after login
			// Note: Purchase intent is NOT preserved to satisfy WordPress.org security requirements.
			// User will need to click the purchase button again after logging in.
			// Future enhancement tracked in GitHub Issue #32
			$base_url = ! empty( $post_id ) ? get_permalink( $post_id ) : home_url();
			$login_url = wp_login_url( $base_url );

			return new WP_REST_Response( array( 'redirect' => esc_url_raw( $login_url ) ), self::HTTP_UNAUTHORIZED );
		}

		$user_id = get_current_user_id();

		// Delegate Stripe subscription session creation to the redirector class
		// This handles subscription-specific configuration and fee calculation
		// Pass post_id for origin tracking (0 if not provided for backward compatibility)
		$session_url = PostGate_Stripe_Redirector::create_subscription_checkout_session_url( $user_id, $price_id, $post_id, $product_id );

		// Handle errors from Stripe session creation
		if ( is_wp_error( $session_url ) ) {
			return $session_url;
		}

		// Return successful response with Stripe checkout URL
		return new WP_REST_Response( array( 'url' => esc_url_raw( $session_url ) ), self::HTTP_OK );
	}
}