<?php
/**
 * Review Reminder Email module.
 *
 * @package Kramar
 * @since   1.0.0
 */

declare( strict_types=1 );

namespace Kramar\Modules\Marketing;

// Prevent direct access.
defined( 'ABSPATH' ) || exit;

/**
 * Review Reminder Email — sends a post-purchase review request via Action Scheduler.
 *
 * KEY DESIGN DECISIONS:
 * - One email per order (not per product). Less annoying.
 * - Action Scheduler for reliable delayed delivery (not WP-Cron).
 * - Idempotent scheduling: checks for existing AS action before scheduling.
 * - Cancel/refund/return → unschedule the pending reminder automatically.
 * - Re-checks order status before sending (order may have been refunded in window).
 * - Opt-out: HMAC-signed one-click unsubscribe link + user meta `_kramar_no_review_reminder`.
 * - WooCommerce native reviews: only offered when `woocommerce_enable_reviews` is ON.
 * - Disabled by default.
 *
 * @since 1.0.0
 */
final class Review_Reminder extends \Kramar\Module {

	/** HMAC secret option key. */
	const HMAC_OPTION = 'kramar_rrr_hmac_secret';

	/** User meta key for opt-out. */
	const OPT_OUT_META = '_kramar_no_review_reminder';

	/** Order meta key: set after email is sent. */
	const SENT_META = '_kramar_review_reminder_sent';

	/** Action Scheduler hook for sending the reminder. */
	const AS_HOOK = 'kramar_send_review_reminder';

	/** AS group. */
	const AS_GROUP = 'kramar-rrr';

	// -----------------------------------------------------------------
	// Module interface
	// -----------------------------------------------------------------

	/**
	 * {@inheritDoc}
	 */
	public function get_slug(): string {
		return 'review-reminder';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_category(): string {
		return 'marketing';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_name(): string {
		return __( 'Review Reminder Email', 'kramar' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_description(): string {
		return __( 'Automatically emails customers a configurable number of days after their order completes, inviting them to leave a review on WooCommerce, Trustpilot, Google, or all of the above. Includes one-click unsubscribe and automatic cancellation on refunds or returns.', 'kramar' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_default_settings(): array {
		return [
			'enabled'        => false,
			'delay_days'     => 7,
			'targets'        => [ 'woocommerce' ],
			'trustpilot_url' => '',
			'google_url'     => '',
		];
	}

	/**
	 * {@inheritDoc}
	 */
	public function sanitize_settings( array $input ): array {
		$delay = absint( $input['delay_days'] ?? 7 );
		if ( $delay < 1 ) {
			$delay = 7;
		}

		$allowed = [ 'woocommerce', 'google', 'trustpilot' ];

		$targets = [];
		if ( isset( $input['targets'] ) && is_array( $input['targets'] ) ) {
			foreach ( $input['targets'] as $t ) {
				$t = sanitize_key( (string) $t );
				if ( in_array( $t, $allowed, true ) && ! in_array( $t, $targets, true ) ) {
					$targets[] = $t;
				}
			}
		}

		// Legacy single 'target' fallback (pre-multi-select saves).
		if ( empty( $targets ) && ! empty( $input['target'] ) ) {
			$legacy = sanitize_key( (string) $input['target'] );
			if ( 'both' === $legacy ) {
				$targets = [ 'woocommerce', 'google' ];
			} elseif ( in_array( $legacy, $allowed, true ) ) {
				$targets = [ $legacy ];
			}
		}

		if ( empty( $targets ) ) {
			$targets = [ 'woocommerce' ];
		}

		return [
			'enabled'        => ! empty( $input['enabled'] ),
			'delay_days'     => $delay,
			'targets'        => $targets,
			'trustpilot_url' => esc_url_raw( (string) ( $input['trustpilot_url'] ?? '' ) ),
			'google_url'     => esc_url_raw( (string) ( $input['google_url'] ?? '' ) ),
		];
	}

	/**
	 * {@inheritDoc}
	 */
	public function render_extra_settings( string $option_name ): void {
		$slug           = $this->get_slug();
		$cat            = $this->get_category();
		$delay          = (int) $this->get_setting( 'delay_days' );
		$targets        = (array) $this->get_setting( 'targets' );
		$trustpilot_url = (string) $this->get_setting( 'trustpilot_url' );
		$google_url     = (string) $this->get_setting( 'google_url' );
		$wc_reviews_on  = 'yes' === get_option( 'woocommerce_enable_reviews' );

		$field_base = esc_attr( $option_name ) . '[' . esc_attr( $cat ) . '][' . esc_attr( $slug ) . ']';
		?>
		<div class="kramar-module__extra">
			<div class="kramar-settings-stack">
				<p>
					<label>
						<?php esc_html_e( 'Send reminder after (days)', 'kramar' ); ?><br>
						<input
							type="number"
							name="<?php echo esc_attr( $field_base ); ?>[delay_days]"
							value="<?php echo esc_attr( (string) $delay ); ?>"
							min="1"
							step="1"
							class="small-text"
						>
					</label>
					<span class="description"><?php esc_html_e( 'Number of days after order completion to send the reminder. Minimum 1.', 'kramar' ); ?></span>
				</p>

				<fieldset style="border:1px solid #dcdcde;border-radius:4px;padding:8px 12px;margin:0;">
					<legend style="font-weight:600;padding:0 6px;"><?php esc_html_e( 'Review platforms', 'kramar' ); ?></legend>
					<p class="description" style="margin:0 0 8px;"><?php esc_html_e( 'Tick every platform you want a review button for. The email shows a button for each selected (and configured) platform.', 'kramar' ); ?></p>
					<label style="display:block;margin:4px 0;">
						<input
							type="checkbox"
							name="<?php echo esc_attr( $field_base ); ?>[targets][]"
							value="woocommerce"
							<?php checked( in_array( 'woocommerce', $targets, true ) ); ?>
							<?php disabled( ! $wc_reviews_on ); ?>
						>
						<?php
						if ( $wc_reviews_on ) {
							esc_html_e( 'WooCommerce (product review tab)', 'kramar' );
						} else {
							esc_html_e( 'WooCommerce — unavailable (product reviews are disabled)', 'kramar' );
						}
						?>
					</label>
					<label style="display:block;margin:4px 0;">
						<input type="checkbox" name="<?php echo esc_attr( $field_base ); ?>[targets][]" value="google" <?php checked( in_array( 'google', $targets, true ) ); ?>>
						<?php esc_html_e( 'Google', 'kramar' ); ?>
					</label>
					<label style="display:block;margin:4px 0;">
						<input type="checkbox" name="<?php echo esc_attr( $field_base ); ?>[targets][]" value="trustpilot" <?php checked( in_array( 'trustpilot', $targets, true ) ); ?>>
						<?php esc_html_e( 'Trustpilot', 'kramar' ); ?>
					</label>
					<?php if ( ! $wc_reviews_on ) : ?>
					<span class="description">
						<?php
						printf(
							/* translators: %s: WooCommerce settings URL */
							esc_html__( 'To use the WooCommerce target, turn on product reviews in %s.', 'kramar' ),
							'<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=products' ) ) . '">' . esc_html__( 'WooCommerce → Settings → Products', 'kramar' ) . '</a>'
						);
						?>
					</span>
					<?php endif; ?>
				</fieldset>

				<p>
					<label>
						<?php esc_html_e( 'Google review URL', 'kramar' ); ?><br>
						<input
							type="url"
							name="<?php echo esc_attr( $field_base ); ?>[google_url]"
							value="<?php echo esc_attr( $google_url ); ?>"
							class="regular-text"
							placeholder="https://g.page/r/YOUR_PLACE_ID/review"
						>
					</label>
					<span class="description"><?php esc_html_e( 'Required when Google is selected. Use the direct Google Maps review link or Place ID URL.', 'kramar' ); ?></span>
				</p>

				<p>
					<label>
						<?php esc_html_e( 'Trustpilot review URL', 'kramar' ); ?><br>
						<input
							type="url"
							name="<?php echo esc_attr( $field_base ); ?>[trustpilot_url]"
							value="<?php echo esc_attr( $trustpilot_url ); ?>"
							class="regular-text"
							placeholder="https://www.trustpilot.com/review/yourshop.com"
						>
					</label>
					<span class="description"><?php esc_html_e( 'Required when Trustpilot is selected.', 'kramar' ); ?></span>
				</p>
			</div>
		</div>
		<?php
	}

	// -----------------------------------------------------------------
	// Install (called on activation)
	// -----------------------------------------------------------------

	/**
	 * Ensure the HMAC secret exists and seed the Email Manager block template
	 * for `kramar_review_reminder` if it has not been customised yet.
	 *
	 * @since 1.0.0
	 */
	public static function install(): void {
		if ( ! get_option( self::HMAC_OPTION ) ) {
			add_option( self::HMAC_OPTION, bin2hex( random_bytes( 32 ) ), '', false );
		}

		// Seed default blocks for the Email Manager so the review CTA button
		// is pre-configured with the {review_url} merge field.
		$overrides = get_option( 'kramar_email_overrides', [] );
		if ( empty( $overrides['kramar_review_reminder']['blocks'] ) ) {
			$overrides['kramar_review_reminder']['blocks'] = self::default_email_blocks();
			update_option( 'kramar_email_overrides', $overrides );
		}
	}

	/**
	 * Build the default Email Manager block layout for the review reminder.
	 *
	 * @since 1.0.0
	 *
	 * @return array<int, array<string, mixed>>
	 */
	private static function default_email_blocks(): array {
		return [
			[
				'type'     => 'heading',
				'id'       => 'rrr_heading',
				'settings' => [ 'text' => '{email_heading}', 'level' => 'h2', 'align' => 'left', 'color' => '' ],
			],
			[
				'type'     => 'text',
				'id'       => 'rrr_intro',
				'settings' => [
					'content' => __( 'Thank you for your recent order! We hope you are enjoying your purchase.', 'kramar' ),
					'color'   => '',
					'bg'      => '',
				],
			],
			[
				'type'     => 'text',
				'id'       => 'rrr_body',
				'settings' => [
					'content' => __( 'We would love to hear what you think. Your feedback helps us improve and helps other shoppers make better decisions.', 'kramar' ),
					'color'   => '',
					'bg'      => '',
				],
			],
			[
				'type'       => 'button',
				'id'         => 'rrr_cta_wc',
				'settings'   => [
					'text'   => __( 'Leave a product review', 'kramar' ),
					'url'    => '{review_url_woocommerce}',
					'color'  => '#ffffff',
					'bg'     => '#000000',
					'align'  => 'center',
					'radius' => '4',
				],
				'visibility' => [ 'type' => 'show_if', 'field' => '{review_url_woocommerce}' ],
			],
			[
				'type'       => 'button',
				'id'         => 'rrr_cta_google',
				'settings'   => [
					'text'   => __( 'Review us on Google', 'kramar' ),
					'url'    => '{review_url_google}',
					'color'  => '#ffffff',
					'bg'     => '#4285F4',
					'align'  => 'center',
					'radius' => '4',
				],
				'visibility' => [ 'type' => 'show_if', 'field' => '{review_url_google}' ],
			],
			[
				'type'       => 'button',
				'id'         => 'rrr_cta_trustpilot',
				'settings'   => [
					'text'   => __( 'Review us on Trustpilot', 'kramar' ),
					'url'    => '{review_url_trustpilot}',
					'color'  => '#ffffff',
					'bg'     => '#00b67a',
					'align'  => 'center',
					'radius' => '4',
				],
				'visibility' => [ 'type' => 'show_if', 'field' => '{review_url_trustpilot}' ],
			],
			[
				'type'     => 'divider',
				'id'       => 'rrr_divider',
				'settings' => [ 'color' => '#e5e5e5', 'width' => '100%', 'margin' => '8' ],
			],
			[
				'type'     => 'text',
				'id'       => 'rrr_unsub',
				'settings' => [
					'content' => sprintf(
						/* translators: %s: unsubscribe merge field */
						__( 'You are receiving this because you recently completed an order with us. <a href="%s">Unsubscribe</a>', 'kramar' ),
						'{review_unsub_url}'
					),
					'color' => '#a0aec0',
					'bg'    => '',
					'size'  => 'small',
				],
			],
		];
	}

	// -----------------------------------------------------------------
	// run()
	// -----------------------------------------------------------------

	/**
	 * {@inheritDoc}
	 */
	protected function run(): void {
		// Register WC email class (lazy-required inside filter — WC_Email only
		// exists after WooCommerce loads).
		add_filter( 'woocommerce_email_classes', [ $this, 'register_email_classes' ] );

		// Order completed → schedule the reminder.
		add_action( 'woocommerce_order_status_completed', [ $this, 'on_order_completed' ], 10, 2 );

		// Order cancelled/refunded/returned → unschedule.
		add_action( 'woocommerce_order_status_cancelled', [ $this, 'on_order_cancelled' ], 10, 2 );
		add_action( 'woocommerce_order_status_refunded', [ $this, 'on_order_cancelled' ], 10, 2 );
		// Also cancel the reminder when a Kramar withdrawal/return is started or approved.
		add_action( 'woocommerce_order_status_return-requested', [ $this, 'on_order_cancelled' ], 10, 2 );
		add_action( 'woocommerce_order_status_return-approved', [ $this, 'on_order_cancelled' ], 10, 2 );

		// Action Scheduler callback.
		add_action( self::AS_HOOK, [ $this, 'as_send_reminder' ] );

		// Unsubscribe endpoint.
		add_action( 'init', [ $this, 'handle_unsubscribe' ] );

		// Inject {review_url} and {review_unsub_url} merge fields into the
		// Email Manager's block renderer so the button URL resolves correctly.
		add_filter( 'kramar_email_merge_field_values', [ $this, 'inject_merge_fields' ], 10, 2 );
		add_filter( 'kramar_email_merge_fields', [ $this, 'register_merge_field_labels' ], 10, 2 );

		// Ensure the Email Manager block template is seeded (covers sites that
		// were already active when this module was added, without reactivation).
		$this->maybe_seed_email_blocks();
	}

	/**
	 * Seed the Email Manager block layout for `kramar_review_reminder` if not
	 * already present. Runs on first `run()` call (module enabled + available).
	 *
	 * @since 1.0.0
	 */
	private function maybe_seed_email_blocks(): void {
		$overrides = get_option( 'kramar_email_overrides', [] );
		if ( ! empty( $overrides['kramar_review_reminder']['blocks'] ) ) {
			return;
		}

		$overrides['kramar_review_reminder']['blocks'] = self::default_email_blocks();
		update_option( 'kramar_email_overrides', $overrides );
	}

	// -----------------------------------------------------------------
	// Email Manager merge fields
	// -----------------------------------------------------------------

	/**
	 * Inject `{review_url}` and `{review_unsub_url}` merge field values into
	 * the Email Manager's block renderer for the `kramar_review_reminder` email.
	 *
	 * Values are set as static properties on Email_Review_Reminder just before
	 * trigger() calls WC's send() — so they are available when the Email Manager's
	 * `woocommerce_mail_callback_params` filter runs to replace the body.
	 *
	 * @since 1.0.0
	 *
	 * @param array<string, string> $replacements Existing merge field map.
	 * @param \WC_Order             $order        Current order.
	 * @return array<string, string> Updated replacements.
	 */
	public function inject_merge_fields( array $replacements, \WC_Order $order ): array {
		// The email class extends \WC_Email, which only exists once WooCommerce has
		// loaded its mailer. This filter also fires during the email builder's live
		// preview (where the mailer is not loaded) — requiring the class there would
		// fatal. The review URLs are only meaningful during an actual send (when the
		// mailer, and thus WC_Email, is loaded), so bail out otherwise.
		if ( ! class_exists( '\WC_Email' ) ) {
			return $replacements;
		}

		require_once KRAMAR_DIR . 'modules/marketing/review-reminder/class-email-review-reminder.php';

		$targets = Email_Review_Reminder::$current_targets;

		$replacements['{review_url}']             = Email_Review_Reminder::$current_review_url;
		$replacements['{review_url_woocommerce}'] = $targets['woocommerce'] ?? '';
		$replacements['{review_url_google}']      = $targets['google'] ?? '';
		$replacements['{review_url_trustpilot}']  = $targets['trustpilot'] ?? '';
		$replacements['{review_unsub_url}']       = Email_Review_Reminder::$current_unsub_url;

		return $replacements;
	}

	/**
	 * Register `{review_url}` and `{review_unsub_url}` in the Email Manager's
	 * merge field palette so they appear in the field-picker UI.
	 *
	 * @since 1.0.0
	 *
	 * @param array<string, array<string, mixed>> $fields  Existing merge field groups.
	 * @param string                               $context Email context ('order', 'account', …).
	 * @return array<string, array<string, mixed>> Updated merge field groups.
	 */
	public function register_merge_field_labels( array $fields, string $context ): array {
		if ( 'order' !== $context ) {
			return $fields;
		}

		$fields['review_reminder'] = [
			'label'  => __( 'Review Reminder', 'kramar' ),
			'fields' => [
				'{review_url}'             => __( 'Review CTA URL (first selected platform)', 'kramar' ),
				'{review_url_woocommerce}' => __( 'WooCommerce review URL', 'kramar' ),
				'{review_url_google}'      => __( 'Google review URL', 'kramar' ),
				'{review_url_trustpilot}'  => __( 'Trustpilot review URL', 'kramar' ),
				'{review_unsub_url}'       => __( 'Unsubscribe URL', 'kramar' ),
			],
		];

		return $fields;
	}

	// -----------------------------------------------------------------
	// WC email registration
	// -----------------------------------------------------------------

	/**
	 * Register the Review Reminder WC email class.
	 *
	 * Lazy-required here because \WC_Email is only available after WC loads.
	 *
	 * @since 1.0.0
	 *
	 * @param array<string, \WC_Email> $emails Existing email classes.
	 * @return array<string, \WC_Email> Modified email classes.
	 */
	public function register_email_classes( array $emails ): array {
		require_once KRAMAR_DIR . 'modules/marketing/review-reminder/class-email-review-reminder.php';
		$emails['Kramar_Email_Review_Reminder'] = new Email_Review_Reminder();
		return $emails;
	}

	// -----------------------------------------------------------------
	// Order completed: schedule reminder
	// -----------------------------------------------------------------

	/**
	 * Schedule a review reminder when an order transitions to completed.
	 *
	 * Idempotent: if a pending action already exists for this order, skip.
	 *
	 * @since 1.0.0
	 *
	 * @param int       $order_id Order ID.
	 * @param \WC_Order $order    Order object.
	 */
	public function on_order_completed( int $order_id, \WC_Order $order ): void {
		// Don't schedule if the email was already sent (e.g., re-completing a
		// previously completed order).
		if ( $order->get_meta( self::SENT_META ) ) {
			return;
		}

		// Respect opt-out for registered customers AND guests (email-keyed).
		$customer_id = (int) $order->get_customer_id();
		if ( $customer_id > 0 && $this->is_user_opted_out( $customer_id ) ) {
			return;
		}
		if ( $this->is_email_opted_out( $order->get_billing_email() ) ) {
			return;
		}

		// Idempotency: bail if an AS action is already pending for this order.
		if ( $this->has_pending_action( $order_id ) ) {
			return;
		}

		$delay_days = (int) $this->get_setting( 'delay_days' );
		if ( $delay_days < 1 ) {
			$delay_days = 7;
		}

		$scheduled_at = time() + ( $delay_days * DAY_IN_SECONDS );

		as_schedule_single_action(
			$scheduled_at,
			self::AS_HOOK,
			[ 'order_id' => $order_id ],
			self::AS_GROUP
		);
	}

	// -----------------------------------------------------------------
	// Order cancelled/refunded: unschedule reminder
	// -----------------------------------------------------------------

	/**
	 * Unschedule any pending review reminder when an order is cancelled or refunded.
	 *
	 * @since 1.0.0
	 *
	 * @param int       $order_id Order ID.
	 * @param \WC_Order $order    Order object (unused, kept for hook signature).
	 */
	public function on_order_cancelled( int $order_id, \WC_Order $order ): void {
		as_unschedule_all_actions(
			self::AS_HOOK,
			[ 'order_id' => $order_id ],
			self::AS_GROUP
		);
	}

	// -----------------------------------------------------------------
	// Action Scheduler callback
	// -----------------------------------------------------------------

	/**
	 * AS callback — send the review reminder for an order.
	 *
	 * @since 1.0.0
	 *
	 * @param int $order_id Order ID.
	 */
	public function as_send_reminder( int $order_id ): void {
		$order = wc_get_order( $order_id );
		if ( ! $order instanceof \WC_Order ) {
			return;
		}

		// Re-check order is still completed (not subsequently cancelled/refunded).
		if ( 'completed' !== $order->get_status() ) {
			return;
		}

		// Idempotency: don't resend.
		if ( $order->get_meta( self::SENT_META ) ) {
			return;
		}

		// Opt-out check at send time (registered customers AND guests).
		$customer_id = (int) $order->get_customer_id();
		if ( $customer_id > 0 && $this->is_user_opted_out( $customer_id ) ) {
			return;
		}

		$recipient = $order->get_billing_email();
		if ( ! is_email( $recipient ) ) {
			return;
		}

		if ( $this->is_email_opted_out( $recipient ) ) {
			return;
		}

		// Resolve a review URL for every selected + configured platform.
		$target_urls = $this->get_selected_target_urls( $order );

		// Nothing to link to (no platforms configured) — skip the send.
		if ( empty( $target_urls ) ) {
			return;
		}

		// Build opt-out unsubscribe link.
		$unsub_link = $this->get_unsubscribe_url( $order_id, $recipient );

		// Fire email via WC mailer.
		/** @var Email_Review_Reminder|null $email_obj */
		$email_obj = WC()->mailer()->emails['Kramar_Email_Review_Reminder'] ?? null;
		if ( ! $email_obj ) {
			// Force class registration if not yet done (e.g., AS fired early).
			require_once KRAMAR_DIR . 'modules/marketing/review-reminder/class-email-review-reminder.php';
			$email_obj = new Email_Review_Reminder();
		}

		$email_obj->trigger( $order, $target_urls, $unsub_link );

		// Mark sent on the order so we never resend.
		$order->update_meta_data( self::SENT_META, current_time( 'mysql', true ) );
		$order->save();
	}

	// -----------------------------------------------------------------
	// Unsubscribe
	// -----------------------------------------------------------------

	/**
	 * Build an HMAC-signed unsubscribe URL for a specific order + email.
	 *
	 * @since 1.0.0
	 *
	 * @param int    $order_id Order ID.
	 * @param string $email    Recipient email address.
	 * @return string Signed URL.
	 */
	public function get_unsubscribe_url( int $order_id, string $email ): string {
		$secret  = (string) get_option( self::HMAC_OPTION, '' );
		$payload = $order_id . '|' . $email;
		$sig     = hash_hmac( 'sha256', $payload, $secret );

		return add_query_arg(
			[
				'kramar-rrr-unsub' => '1',
				'order_id'       => $order_id,
				'email'          => rawurlencode( $email ),
				'sig'            => $sig,
			],
			home_url( '/' )
		);
	}

	/**
	 * Handle unsubscribe GET request on `init`.
	 *
	 * Sets `_kramar_no_review_reminder` on the WP user (if found) OR stores a
	 * transient keyed on the email for guests, so the meta check is skipped for
	 * registered customers and the email-keyed check covers guests.
	 *
	 * @since 1.0.0
	 */
	public function handle_unsubscribe(): void {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- public unsubscribe link authenticated by HMAC signature (hash_hmac/hash_equals), not a nonce; recipient is logged out.
		if ( ! isset( $_GET['kramar-rrr-unsub'] ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- public unsubscribe link authenticated by HMAC signature (hash_hmac/hash_equals), not a nonce; recipient is logged out.
		$order_id = isset( $_GET['order_id'] ) ? absint( $_GET['order_id'] ) : 0;
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- public unsubscribe link authenticated by HMAC signature (hash_hmac/hash_equals), not a nonce; recipient is logged out.
		$email = isset( $_GET['email'] ) ? sanitize_email( wp_unslash( (string) $_GET['email'] ) ) : '';
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- public unsubscribe link authenticated by HMAC signature (hash_hmac/hash_equals), not a nonce; recipient is logged out.
		$sig = isset( $_GET['sig'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['sig'] ) ) : '';

		$secret   = (string) get_option( self::HMAC_OPTION, '' );
		$payload  = $order_id . '|' . $email;
		$expected = hash_hmac( 'sha256', $payload, $secret );

		if ( ! hash_equals( $expected, $sig ) || $order_id <= 0 || ! is_email( $email ) ) {
			wp_die( esc_html__( 'Invalid unsubscribe link.', 'kramar' ), '', [ 'response' => 400 ] );
		}

		// Set opt-out on the user account if one exists for this email.
		$user = get_user_by( 'email', $email );
		if ( $user ) {
			update_user_meta( $user->ID, self::OPT_OUT_META, '1' );
		}

		// Also store a transient keyed on email so guests are covered.
		set_transient( 'kramar_rrr_optout_' . md5( $email ), '1', YEAR_IN_SECONDS );

		wp_safe_redirect(
			add_query_arg( 'kramar-rrr-unsubscribed', '1', home_url( '/' ) )
		);
		exit;
	}

	// -----------------------------------------------------------------
	// Helpers
	// -----------------------------------------------------------------

	/**
	 * Check whether a user has opted out of review reminders.
	 *
	 * @since 1.0.0
	 *
	 * @param int $user_id WordPress user ID.
	 * @return bool True if opted out.
	 */
	private function is_user_opted_out( int $user_id ): bool {
		return '1' === (string) get_user_meta( $user_id, self::OPT_OUT_META, true );
	}

	/**
	 * Check whether an email address has opted out (covers guests via transient).
	 *
	 * @since 1.0.0
	 *
	 * @param string $email Email address.
	 * @return bool True if opted out.
	 */
	private function is_email_opted_out( string $email ): bool {
		return '1' === (string) get_transient( 'kramar_rrr_optout_' . md5( $email ) );
	}

	/**
	 * Whether an AS action is already pending for this order.
	 *
	 * @since 1.0.0
	 *
	 * @param int $order_id Order ID.
	 * @return bool
	 */
	private function has_pending_action( int $order_id ): bool {
		if ( ! function_exists( 'as_has_scheduled_action' ) ) {
			return false;
		}
		return as_has_scheduled_action(
			self::AS_HOOK,
			[ 'order_id' => $order_id ],
			self::AS_GROUP
		);
	}

	/**
	 * Resolve a review URL for every selected + configured platform.
	 *
	 * Returns an ordered map keyed by platform ('woocommerce', 'google',
	 * 'trustpilot'). Platforms with no usable URL (e.g. Google selected but no
	 * URL set) are omitted. The first entry is treated as the primary CTA.
	 *
	 * @since 1.0.0
	 *
	 * @param \WC_Order $order Order object.
	 * @return array<string, string> Platform => review URL.
	 */
	private function get_selected_target_urls( \WC_Order $order ): array {
		$targets        = (array) $this->get_setting( 'targets' );
		$trustpilot_url = (string) $this->get_setting( 'trustpilot_url' );
		$google_url     = (string) $this->get_setting( 'google_url' );

		$urls = [];
		foreach ( $targets as $target ) {
			switch ( $target ) {
				case 'woocommerce':
					$url = $this->get_wc_review_url( $order );
					if ( '' !== $url ) {
						$urls['woocommerce'] = $url;
					}
					break;

				case 'google':
					if ( '' !== $google_url ) {
						$urls['google'] = $google_url;
					}
					break;

				case 'trustpilot':
					if ( '' !== $trustpilot_url ) {
						$urls['trustpilot'] = $trustpilot_url;
					}
					break;
			}
		}

		return $urls;
	}

	/**
	 * Get the WooCommerce product review tab URL for the first product in the order.
	 *
	 * @since 1.0.0
	 *
	 * @param \WC_Order $order Order object.
	 * @return string Product permalink with #reviews anchor, or shop URL as fallback.
	 */
	private function get_wc_review_url( \WC_Order $order ): string {
		foreach ( $order->get_items() as $item ) {
			if ( ! $item instanceof \WC_Order_Item_Product ) {
				continue;
			}

			$product = $item->get_product();
			if ( $product instanceof \WC_Product ) {
				$permalink = $product->get_permalink();
				if ( $permalink ) {
					return $permalink . '#reviews';
				}
			}
		}

		// Fallback to shop URL if no product found.
		return wc_get_page_permalink( 'shop' ) ?: home_url( '/' );
	}

	/**
	 * Read a setting for this module.
	 *
	 * @since 1.0.0
	 *
	 * @param string $key Setting key.
	 * @return mixed Setting value.
	 */
	private function get_setting( string $key ): mixed {
		return $this->plugin->get_setting( $this->get_category(), $this->get_slug(), $key );
	}
}
