<?php

declare(strict_types=1);

namespace Australcode\Rewards\Frontend;

use Australcode\Rewards\Domain\Settings\SettingsRepository;
use Australcode\Rewards\Hooks\RedemptionCartHook;
use Australcode\Rewards\Service\RedemptionService;
use Australcode\Rewards\Service\WalletService;

defined( 'ABSPATH' ) || exit;

/**
 * Shows logged-in customers their points balance on the cart page together
 * with an apply/update/remove form that redeems points as a wallet discount
 * (negative fee, no coupon). The form posts to admin-post and the amount is
 * re-validated server-side by RedemptionCartHook on every cart calculation.
 * A richer Blocks-native redeem widget arrives in Phase 3.b.
 */
final class CartPointsNotice {

	public function __construct(
		private readonly WalletService $wallet,
		private readonly RedemptionService $redemption,
		private readonly SettingsRepository $settings,
		private readonly RedemptionCartHook $cart_hook,
	) {
	}

	public function register(): void {
		add_action( 'woocommerce_before_cart_totals', array( $this, 'render_cart_notice' ) );
		// Front-end POST handler (NOT admin-post.php: that runs in admin context,
		// where WC never boots its session handler — WC()->session is null and the
		// apply would silently no-op). On wp_loaded the WC session is live.
		add_action( 'wp_loaded', array( $this, 'handle_apply' ), 20 );
		add_action( 'template_redirect', array( $this, 'maybe_add_blocks_notice' ) );
	}

	/**
	 * Balance / redeemable max / discount amount / label for the current user,
	 * or null when nothing is redeemable (shared by classic and Blocks paths).
	 *
	 * @return array{balance:int,max:int,amount:float,label:string}|null
	 */
	private function redeemable_summary(): ?array {
		$settings = $this->settings->get();
		if ( ! $settings->redemption_enabled || ! $settings->show_cart_notice || ! is_user_logged_in() ) {
			return null;
		}

		$user_id = get_current_user_id();
		$balance = $this->wallet->get_balance( $user_id )->points_balance;
		if ( $balance <= 0 ) {
			return null;
		}

		$cart     = WC()->cart;
		$subtotal = null !== $cart ? (float) $cart->get_subtotal() : 0.0;
		$max      = $this->redemption->max_redeemable_points( $user_id, $subtotal );
		if ( $max <= 0 ) {
			return null;
		}

		return array(
			'balance' => $balance,
			'max'     => $max,
			'amount'  => $this->redemption->points_to_amount( $max ),
			'label'   => 1 === $balance ? $settings->points_label_singular : $settings->points_label_plural,
		);
	}

	/**
	 * The Blocks cart (default since WC 8.3) never fires the classic
	 * woocommerce_before_cart_totals hook; surface the balance as a store
	 * notice there so customers still see what they can redeem. Interim until
	 * the Blocks-native redeem widget (Phase 3.b).
	 */
	public function maybe_add_blocks_notice(): void {
		if ( ! function_exists( 'is_cart' ) || ! is_cart() || ! is_user_logged_in() ) {
			return;
		}
		$page = get_post();
		if ( ! $page instanceof \WP_Post || ! has_block( 'woocommerce/cart', $page ) ) {
			return; // Classic cart is covered by the woocommerce_before_cart_totals hook.
		}

		$summary = $this->redeemable_summary();
		if ( null === $summary ) {
			return;
		}

		$applied = $this->cart_hook->session_points();
		if ( $applied > 0 ) {
			// Re-run the quote so the copy matches the fee: the money path clamps
			// to balance/cap, and the notice must never overstate it (crafted POST
			// can carry more points than the balance).
			$subtotal = ( function_exists( 'WC' ) && null !== WC()->cart ) ? (float) WC()->cart->get_subtotal() : 0.0;
			$quote    = $this->redemption->quote( get_current_user_id(), $applied, $subtotal );
			$shown    = $quote->valid ? $quote->points : 0;
			$msg      = sprintf(
				/* translators: 1: applied points, 2: points label, 3: formatted discount amount. */
				__( '%1$s %2$s applied — %3$s off this order. Manage them from your account Rewards page.', 'australcode-points-rewards' ),
				number_format_i18n( $shown ),
				$summary['label'],
				wp_strip_all_tags( wc_price( $this->redemption->points_to_amount( $shown ) ) )
			);
		} else {
			$msg = sprintf(
				/* translators: 1: points balance, 2: points label, 3: formatted discount amount. */
				__( 'You have %1$s %2$s — worth up to %3$s off this order. Redeem them from your account Rewards page.', 'australcode-points-rewards' ),
				number_format_i18n( $summary['balance'] ),
				$summary['label'],
				wp_strip_all_tags( wc_price( $summary['amount'] ) )
			);
		}
		if ( ! wc_has_notice( $msg, 'notice' ) ) {
			wc_add_notice( $msg, 'notice' );
		}
	}

	public function render_cart_notice(): void {
		$summary = $this->redeemable_summary();
		if ( null === $summary ) {
			return;
		}

		$balance = $summary['balance'];
		$max     = $summary['max'];
		$amount  = $summary['amount'];
		$label   = $summary['label'];
		$applied = $this->cart_hook->session_points();

		/* translators: keep the {points}, {points_label} and {max_discount} placeholders. */
		$default  = __( 'You have <strong>{points} {points_label}</strong> — redeem up to {max_discount} off this order.', 'australcode-points-rewards' );
		$template = '' !== $this->settings->get()->msg_cart ? $this->settings->get()->msg_cart : $default;
		$message  = MessageTemplate::render(
			$template,
			array(
				'{points}'       => esc_html( number_format_i18n( $balance ) ),
				'{points_label}' => esc_html( $label ),
				'{max_discount}' => wc_price( $amount ),
			)
		);

		echo '<div class="acrwd-points-message">';
		echo wp_kses_post( $message );
		echo '<form method="post" class="acrwd-redeem-form">';
		wp_nonce_field( 'acrwd_apply_redemption' );
		echo '<input type="hidden" name="acrwd_action" value="apply_redemption" />';
		echo '<input type="number" name="acrwd_points" min="0" max="' . esc_attr( (string) $max ) . '" step="1" value="' . esc_attr( (string) ( $applied > 0 ? $applied : $max ) ) . '" aria-label="' . esc_attr__( 'Points to redeem', 'australcode-points-rewards' ) . '" /> ';
		echo '<button type="submit" class="button">' . ( $applied > 0 ? esc_html__( 'Update', 'australcode-points-rewards' ) : esc_html__( 'Apply points', 'australcode-points-rewards' ) ) . '</button>';
		if ( $applied > 0 ) {
			echo ' <button type="submit" name="acrwd_points" value="0" class="button">' . esc_html__( 'Remove', 'australcode-points-rewards' ) . '</button>';
		}
		echo '</form></div>';
	}

	/**
	 * POST-redirect-GET handler for the redeem forms (cart page + My Account
	 * Rewards). The form posts back to the page it lives on; this fires on
	 * wp_loaded for any front-end request carrying the marker.
	 */
	public function handle_apply(): void {
		if ( is_admin() || ! isset( $_POST['acrwd_action'] ) || 'apply_redemption' !== $_POST['acrwd_action'] ) {
			return;
		}
		if ( ! is_user_logged_in() ) {
			return;
		}
		check_admin_referer( 'acrwd_apply_redemption' );
		$points = isset( $_POST['acrwd_points'] ) ? absint( wp_unslash( $_POST['acrwd_points'] ) ) : 0;
		$this->cart_hook->set_session_points( $points );

		$referer = wp_get_referer();
		wp_safe_redirect( false !== $referer ? $referer : ( function_exists( 'wc_get_cart_url' ) ? wc_get_cart_url() : home_url() ) );
		exit;
	}
}
