<?php
/**
 * Admin notices for SPP transfer-percentage validation.
 *
 * 3.7.2: replaces the blocking JS confirm()/alert() popups that prior
 * versions used to warn customers when configured transfer percentages
 * crossed the 97% (warning) or 100% (error) thresholds. The old popups
 * could abort save when the user dismissed them, which blocked legitimate
 * 100%-routing workflows (e.g. account-per-product setups, customers who
 * charge a separate processing-fee line to cover Stripe fees).
 *
 * Flow:
 *   1. The product-edit form and the SPP dashboard form's JS submit
 *      handler (includes/admin/assets/js/bsd-spscwa.js) computes the
 *      relevant totals and, if a threshold is crossed, appends a hidden
 *      input named `bsd_spscwa_high_total_warning` carrying a JSON payload
 *      ({context, total, severity}).
 *   2. This module's admin_init handler picks up that field from any
 *      admin POST and stashes it in a short-lived per-user transient.
 *   3. The admin_notices handler renders it once as a dismissible
 *      `notice-warning` (or `notice-error` for the >100% case) and clears
 *      the transient.
 *
 * The save itself always proceeds — the notice is advisory, not blocking.
 * The underlying validator math (multi-account aggregate) is tracked for a
 * deeper rework in 3.7.3 — see Shortcut sc-7166.
 *
 * @since 3.7.2
 * @package BSD_Split_Pay_Stripe_Connect_Woo
 */

namespace BSD_Split_Pay_Stripe_Connect_Woo\Inc\Admin;

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

const SPP_VALIDATION_NOTICE_TRANSIENT_PREFIX = 'spp_validation_notice_';

add_action( 'admin_init', __NAMESPACE__ . '\\spp_capture_validation_notice', 99 );
add_action( 'admin_notices', __NAMESPACE__ . '\\spp_render_validation_notice' );

/**
 * Pick up the validation-warning hidden field from any admin POST and
 * stash a per-user transient. Intentionally permissive about which form
 * the POST came from: SPP's dashboard form posts to options.php, the WC
 * product editor posts to post.php, and both pass through admin_init
 * before any redirect — so a single capture hook here works for both.
 *
 * Nonce verification is owned by the parent request handler (WP Settings
 * API for the dashboard form, WC's woocommerce_save_data nonce for the
 * product editor). We do not introduce a separate nonce because the
 * captured payload is purely informational; it never drives a write.
 *
 * @return void
 */
function spp_capture_validation_notice() {
	// phpcs:disable WordPress.Security.NonceVerification.Missing
	if ( empty( $_POST['bsd_spscwa_high_total_warning'] ) ) {
		return;
	}
	$user_id = get_current_user_id();
	if ( ! $user_id ) {
		return;
	}

	$raw     = wp_unslash( $_POST['bsd_spscwa_high_total_warning'] );
	$payload = json_decode( is_string( $raw ) ? $raw : '', true );
	// phpcs:enable WordPress.Security.NonceVerification.Missing
	if ( ! is_array( $payload ) ) {
		return;
	}

	$total = isset( $payload['total'] ) ? (float) $payload['total'] : 0.0;
	if ( $total <= 0 ) {
		return;
	}

	$context  = isset( $payload['context'] ) ? sanitize_key( (string) $payload['context'] ) : 'unknown';
	$severity = isset( $payload['severity'] ) ? sanitize_key( (string) $payload['severity'] ) : 'warning';
	if ( ! in_array( $severity, array( 'warning', 'error' ), true ) ) {
		$severity = 'warning';
	}

	set_transient(
		SPP_VALIDATION_NOTICE_TRANSIENT_PREFIX . $user_id,
		array(
			'context'  => $context,
			'total'    => $total,
			'severity' => $severity,
		),
		MINUTE_IN_SECONDS
	);
}

/**
 * Render the captured validation notice once, then clear it. The notice
 * is dismissible — WordPress core handles the X button. Because the
 * transient is consumed on read, the notice naturally disappears after
 * the post-save redirect renders.
 *
 * @return void
 */
function spp_render_validation_notice() {
	$user_id = get_current_user_id();
	if ( ! $user_id ) {
		return;
	}
	$key     = SPP_VALIDATION_NOTICE_TRANSIENT_PREFIX . $user_id;
	$payload = get_transient( $key );
	if ( ! is_array( $payload ) ) {
		return;
	}
	delete_transient( $key );

	$total    = isset( $payload['total'] ) ? (float) $payload['total'] : 0.0;
	$context  = isset( $payload['context'] ) ? (string) $payload['context'] : '';
	$severity = isset( $payload['severity'] ) ? (string) $payload['severity'] : 'warning';

	switch ( $context ) {
		case 'product':
			$where_label = __( 'Product-level transfer percentages', 'bsd-split-pay-stripe-connect-woo' );
			break;
		case 'global':
			$where_label = __( 'Global transfer percentages', 'bsd-split-pay-stripe-connect-woo' );
			break;
		case 'global_shipping':
			$where_label = __( 'Global shipping transfer percentages', 'bsd-split-pay-stripe-connect-woo' );
			break;
		default:
			$where_label = __( 'Transfer percentages', 'bsd-split-pay-stripe-connect-woo' );
	}

	// Extensibility (3.7.5): whether this advisory notice carries WordPress's
	// native dismiss ("X") affordance. Default true preserves today's markup
	// exactly — the percentage-validation notice has always been dismissible —
	// so with no callback attached the rendered class string is byte-identical.
	// The notice context ('product' | 'global' | 'global_shipping' | …) is
	// passed so integrators can selectively force a notice to stay sticky.
	$dismissible = true;
	if ( function_exists( 'apply_filters' ) ) {
		$dismissible = (bool) apply_filters( 'spp_admin_notice_dismissible', $dismissible, $context );
	}
	$dismissible_class = $dismissible ? ' is-dismissible' : '';

	$class = ( 'error' === $severity )
		? 'notice notice-error' . $dismissible_class
		: 'notice notice-warning' . $dismissible_class;

	if ( 'error' === $severity ) {
		$message = sprintf(
			/* translators: 1: where label, 2: total percent */
			esc_html__( 'Split Pay: %1$s sum to %2$s%%, which exceeds 100%% of the eligible amount. Stripe transfers cannot move more than what was collected, so values above 100%% will be proportionally scaled at runtime. Review your configuration when convenient.', 'bsd-split-pay-stripe-connect-woo' ),
			esc_html( $where_label ),
			esc_html( number_format( $total, 2 ) )
		);
	} else {
		$message = sprintf(
			/* translators: 1: where label, 2: total percent */
			esc_html__( 'Split Pay: %1$s sum to %2$s%%. Stripe processing fees are deducted from the payment before transfers, so transfers above ~97%% may fail due to insufficient balance. Your settings were saved; this is just a heads-up.', 'bsd-split-pay-stripe-connect-woo' ),
			esc_html( $where_label ),
			esc_html( number_format( $total, 2 ) )
		);
	}

	echo '<div class="' . esc_attr( $class ) . '"><p>' . wp_kses_post( $message ) . '</p></div>';
}
