<?php
/**
 * PostGate Fee Manager Class
 *
 * This file contains the centralized fee management logic for platform
 * fees and commission calculations. Provides hard-enforced fee rates
 * and validation mechanisms.
 *
 * @package PostGate
 * @subpackage FeeManagement
 * @since 1.0.0
 * @author QodeSphere
 * @license GPL-2.0+
 */

defined( 'ABSPATH' ) || exit;

/**
 * PostGate Fee Manager Class
 *
 * Centralized, hard-enforced platform fee logic with the following rules:
 * - Default: 5% platform fee for all users
 * - Fees are NOT read from user-editable options for security
 * - Filter hooks are provided but never allow lowering below default rate
 *
 * This class ensures consistent fee calculation across all payment types
 * and provides validation mechanisms to prevent fee manipulation.
 *
 * @since 1.0.0
 */
class PostGate_Fee_Manager {

	/**
	 * Default platform fee rate (5%).
	 *
	 * @since 1.0.0
	 * @var float
	 */
	private const DEFAULT_RATE = 0.05;

	/**
	 * Tolerance for float comparison operations.
	 *
	 * @since 1.0.0
	 * @var float
	 */
	private const FLOAT_TOLERANCE = 0.0001;

	/**
	 * HTTP status code for fee mismatch errors.
	 *
	 * @since 1.0.0
	 * @var int
	 */
	private const HTTP_BAD_REQUEST = 400;

	/**
	 * Gets the current platform fee rate.
	 *
	 * Returns the platform fee rate with optional filtering.
	 * The filter allows customization but never permits setting
	 * the rate below the minimum required rate for security.
	 *
	 * @since 1.0.0
	 * @return float The platform fee rate as a decimal (e.g., 0.05 for 5%).
	 */
	public static function get_platform_fee_rate(): float {
		/**
		 * Filter the platform fee rate.
		 *
		 * Allows customization of the fee rate but never permits
		 * setting it below the default minimum rate for security.
		 *
		 * @since 1.0.0
		 * @param float $rate The default fee rate (0.05 for 5%).
		 */
		$filtered_rate = apply_filters( 'postgate_fee_rate', self::DEFAULT_RATE );

		// Validate: must be numeric and not less than DEFAULT_RATE
		if ( ! is_numeric( $filtered_rate ) || (float) $filtered_rate < self::DEFAULT_RATE ) {
			$filtered_rate = self::DEFAULT_RATE;
		}

		return $filtered_rate;
	}

	/**
	 * Calculate the platform fee amount in cents for one-time payments.
	 *
	 * @since 1.0.0
	 * @param int $amount_cents The payment amount in cents.
	 * @return int The calculated fee amount in cents.
	 */
	public static function calculate_fee_amount_cents( int $amount_cents ): int {
		if ( $amount_cents < 0 ) {
			return 0;
		}

		$rate = self::get_platform_fee_rate();
		return (int) round( $amount_cents * $rate );
	}

	/**
	 * Calculate the platform fee percentage for subscription payments.
	 *
	 * @since 1.0.0
	 * @return float The fee percentage (e.g., 5.0 for 5%).
	 */
	public static function calculate_subscription_fee_percentage(): float {
		return self::get_platform_fee_rate() * 100;
	}

	/**
	 * Validate fee amounts and return validation result.
	 *
	 * Ensures the fee being applied matches exactly what we expect
	 * to prevent fee manipulation or calculation errors. Returns
	 * WP_Error if validation fails to abort the payment process.
	 *
	 * @since 1.0.0
	 * @param int|float $expected_fee The expected fee amount/percentage.
	 * @param int|float $applied_fee  The fee amount/percentage being applied.
	 * @param string    $fee_type     The type of fee ('amount' for cents, 'percent' for percentage).
	 * @return bool|WP_Error True if validation passes, WP_Error if fee mismatch detected.
	 */
	public static function validate_fee_match( int|float $expected_fee, int|float $applied_fee, string $fee_type = 'amount' ) {
		$is_valid = false;

		if ( 'percent' === $fee_type ) {
			// Compare percent values with float tolerance
			$expected_rounded = round( (float) $expected_fee, 2 );
			$applied_rounded = round( (float) $applied_fee, 2 );
			$is_valid = abs( $expected_rounded - $applied_rounded ) < self::FLOAT_TOLERANCE;
		} else {
			// Default: integer cents, must match exactly
			$is_valid = ( (int) $expected_fee === (int) $applied_fee );
		}

		if ( ! $is_valid ) {
			return new WP_Error(
				'postgate_fee_mismatch',
				__( 'Platform fee mismatch detected. Aborting checkout.', 'postgate' ),
				array(
					'status' => self::HTTP_BAD_REQUEST,
					'type' => sanitize_text_field( $fee_type ),
					'expected' => is_numeric( $expected_fee ) ? (float) $expected_fee : 0,
					'applied' => is_numeric( $applied_fee ) ? (float) $applied_fee : 0,
				)
			);
		}

		return true;
	}

	/**
	 * Validate platform fee for one-time payment intents.
	 *
	 * Retrieves the payment intent from Stripe and compares the actual
	 * application fee amount with the expected fee based on our platform
	 * fee calculation.
	 *
	 * @since 1.0.0
	 * @param string $payment_intent_id Stripe PaymentIntent ID.
	 * @param string $stripe_account_id Connected account ID for API calls.
	 * @return bool|WP_Error True if validation passes, WP_Error if fee mismatch detected.
	 */
	public static function validate_payment_intent_fee( string $payment_intent_id, string $stripe_account_id ) {
		try {
			// Retrieve payment intent with charge data from Stripe
			$payment_intent = \Stripe\PaymentIntent::retrieve(
				array( 'id' => $payment_intent_id, 'expand' => array( 'latest_charge' ) ),
				array( 'stripe_account' => $stripe_account_id )
			);

			// Get actual amounts and fees
			$amount_cents = (int) ( $payment_intent->amount_received ?? $payment_intent->amount ?? 0 );
			$actual_fee_cents = (int) ( $payment_intent->application_fee_amount ?? 0 );

			// Calculate expected fee based on current platform settings
			$expected_fee_cents = self::calculate_fee_amount_cents( $amount_cents );

			// Validate fee matches expected amount
			return self::validate_fee_match( $expected_fee_cents, $actual_fee_cents, 'amount' );

		} catch (\Throwable $exception) {
			return new WP_Error(
				'postgate_fee_validation_error',
				/* translators: %s: Error message */
				sprintf( __( 'Fee validation failed: %s', 'postgate' ), $exception->getMessage() ),
				array( 'status' => self::HTTP_BAD_REQUEST )
			);
		}
	}

	/**
	 * Validate platform fee percentage for subscriptions.
	 *
	 * Retrieves the subscription from Stripe and validates that the
	 * application_fee_percent matches our expected percentage.
	 *
	 * @since 1.0.0
	 * @param string $subscription_id Stripe Subscription ID.
	 * @param string $stripe_account_id Connected account ID for API calls.
	 * @return bool|WP_Error True if validation passes, WP_Error if fee mismatch detected.
	 */
	public static function validate_subscription_fee( string $subscription_id, string $stripe_account_id ) {
		try {
			// Retrieve subscription object from Stripe
			$stripe_subscription = \Stripe\Subscription::retrieve(
				$subscription_id,
				array( 'stripe_account' => $stripe_account_id )
			);

			// Get expected and actual fee percentages
			$expected_percent = self::calculate_subscription_fee_percentage(); // Returns 0-100 range
			$actual_percent = (float) ( $stripe_subscription->application_fee_percent ?? 0.0 );

			// Validate percentage matches expected value
			return self::validate_fee_match( $expected_percent, $actual_percent, 'percent' );

		} catch (\Throwable $exception) {
			return new WP_Error(
				'postgate_subscription_validation_error',
				/* translators: %s: Error message */
				sprintf( __( 'Subscription fee validation failed: %s', 'postgate' ), $exception->getMessage() ),
				array( 'status' => self::HTTP_BAD_REQUEST )
			);
		}
	}
}