<?php
namespace Pay4All\Pro\Admin\AbandonedCarts;

use Pay4All\Form\Admin\Menu;
use Pay4All\Form\Admin\Orders\ListTable;
use Pay4All\Form\Admin\Settings\Schema;
use Pay4All\Pro\Domain\AbandonedCart;
use Pay4All\Form\Services\Currency;
use Pay4All\Pro\Services\Mailer;

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

/**
 * Admin page « Pay4All Shop › Abandon panier ».
 *
 * Simple list table : filter by status (pending / sent / recovered /
 * converted), inline delete via admin-post.php?action=p4all_ac_delete.
 * Voluntarily minimalist : the carts are short-lived data (90 days max)
 * so we don't bother with pagination yet — `list_rows` caps at 200.
 */
final class Page {

	public const SLUG               = 'pay4all-form-abandoned-carts';
	public const DELETE_ACTION      = 'p4all_ac_delete';
	public const BULK_DELETE_ACTION = 'p4all_ac_bulk_delete';
	public const SEND_ACTION        = 'p4all_ac_send';
	public const SET_DELAY_ACTION   = 'p4all_ac_set_delay';

	/**
	 * Allowed delay values for the merchant-facing dropdown. `0` = « Jamais »
	 * (auto-send disabled) — matches the existing `cron_tick()` guard which
	 * skips the loop when `delay <= 0`. Kept in one place so translations
	 * and validation stay aligned.
	 *
	 * @return array<int,string>
	 */
	public static function delay_choices() : array {
		return [
			1  => __( '1 jour',  'pay4all-pro' ),
			5  => __( '5 jours', 'pay4all-pro' ),
			10 => __( '10 jours', 'pay4all-pro' ),
			0  => __( 'Jamais',  'pay4all-pro' ),
		];
	}

	public static function register() : void {
		add_action( 'admin_menu',                              [ self::class, 'add_submenu' ], 19 );
		add_action( 'admin_post_' . self::DELETE_ACTION,       [ self::class, 'handle_delete' ] );
		add_action( 'admin_post_' . self::BULK_DELETE_ACTION,  [ self::class, 'handle_bulk_delete' ] );
		add_action( 'admin_post_' . self::SEND_ACTION,         [ self::class, 'handle_send' ] );
		add_action( 'admin_post_' . self::SET_DELAY_ACTION,    [ self::class, 'handle_set_delay' ] );
	}

	public static function add_submenu() : void {
		add_submenu_page(
			Menu::SLUG_TOP,
			__( 'Abandon panier', 'pay4all-pro' ),
			__( 'Abandon panier', 'pay4all-pro' ),
			'manage_options',
			self::SLUG,
			[ self::class, 'render' ]
		);
	}

	public static function render() : void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission refusée.', 'pay4all-pro' ) );
		}
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only filter on GET.
		$status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( (string) $_GET['status'] ) ) : '';
		$rows   = AbandonedCart::list_rows( $status, 200 );
		?>
		<?php
		$delay_days      = (int) Schema::get( 'abandoned_cart_delay_days', 10 );
		$delay_choices   = self::delay_choices();
		$delay_nonce     = wp_create_nonce( self::SET_DELAY_ACTION );
		$auto_send       = ( $delay_days > 0 );
		?>
		<div class="wrap">
			<h1><?php esc_html_e( 'Abandon de panier', 'pay4all-pro' ); ?></h1>

			<?php // Délai d'envoi de la relance. « Jamais » (valeur 0) coupe
			// l'envoi automatique, le commerçant peut alors relancer manuel-
			// lement via le bouton « Envoyer » de chaque ligne. Le <select>
			// s'auto-submit au changement (JS natif). Repli sans-JS : bouton
			// « Enregistrer » via <noscript>. ?>
			<form id="p4all-ac-delay-form" method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="margin:16px 0 8px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
				<input type="hidden" name="action"   value="<?php echo esc_attr( self::SET_DELAY_ACTION ); ?>">
				<input type="hidden" name="_wpnonce" value="<?php echo esc_attr( $delay_nonce ); ?>">
				<label for="p4all-ac-delay" style="font-weight:600;color:#1d2327;">
					<?php esc_html_e( 'Envoyer l\'e-mail de relance', 'pay4all-pro' ); ?>
				</label>
				<select id="p4all-ac-delay" name="delay_days"
				        onchange="document.getElementById('p4all-ac-delay-form').submit();">
					<?php foreach ( $delay_choices as $value => $label ) : ?>
						<option value="<?php echo esc_attr( (string) $value ); ?>" <?php selected( $delay_days, $value ); ?>>
							<?php echo esc_html( $label ); ?>
						</option>
					<?php endforeach; ?>
				</select>
				<span style="color:#666;font-size:.9em;">
					<?php esc_html_e( 'après l\'abandon du panier', 'pay4all-pro' ); ?>
				</span>
				<noscript>
					<button type="submit" class="button button-primary"><?php esc_html_e( 'Enregistrer', 'pay4all-pro' ); ?></button>
				</noscript>
			</form>

			<p class="description" style="margin-top:0;">
				<?php if ( $auto_send ) : ?>
					<?php
					printf(
						/* translators: %d = delay in days configured in the settings. */
						esc_html( _n(
							'Les paniers commencés mais non validés. Un e-mail de récupération est envoyé automatiquement %d jour après l\'abandon.',
							'Les paniers commencés mais non validés. Un e-mail de récupération est envoyé automatiquement %d jours après l\'abandon.',
							max( 1, $delay_days ),
							'pay4all-pro'
						) ),
						(int) $delay_days
					);
					?>
				<?php else : ?>
					<?php esc_html_e( 'Les paniers commencés mais non validés. L\'envoi automatique est désactivé, vous pouvez envoyer les relances manuellement depuis le bouton « Envoyer » sur chaque ligne.', 'pay4all-pro' ); ?>
				<?php endif; ?>
			</p>

			<?php
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only flag set by self::handle_send() redirect.
			if ( ! empty( $_GET['sent'] ) ) : ?>
				<div class="notice notice-success is-dismissible"><p>
					<?php esc_html_e( 'E-mail de récupération envoyé.', 'pay4all-pro' ); ?>
				</p></div>
			<?php endif; ?>
			<?php
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only flag.
			if ( ! empty( $_GET['send_err'] ) ) : ?>
				<div class="notice notice-error is-dismissible"><p>
					<?php esc_html_e( 'L\'envoi du mail de récupération a échoué. Vérifiez la configuration SMTP de WordPress.', 'pay4all-pro' ); ?>
				</p></div>
			<?php endif; ?>
			<?php
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only flag set by handle_bulk_delete redirect.
			$bulk_deleted_count = isset( $_GET['bulk_deleted'] ) ? (int) $_GET['bulk_deleted'] : 0;
			if ( $bulk_deleted_count > 0 ) : ?>
				<div class="notice notice-success is-dismissible"><p>
					<?php
					printf(
						/* translators: %d = number of rows deleted. */
						esc_html( _n( '%d panier abandonné supprimé.', '%d paniers abandonnés supprimés.', $bulk_deleted_count, 'pay4all-pro' ) ),
						(int) $bulk_deleted_count
					);
					?>
				</p></div>
			<?php endif; ?>

			<ul class="subsubsub">
				<?php
				$tabs = [
					''           => __( 'Tous',     'pay4all-pro' ),
					'pending'    => __( 'En attente',  'pay4all-pro' ),
					'sent'       => __( 'E-mail envoyé', 'pay4all-pro' ),
					'recovered'  => __( 'Récupéré',  'pay4all-pro' ),
					'converted'  => __( 'Converti',  'pay4all-pro' ),
				];
				$i = 0;
				foreach ( $tabs as $key => $label ) :
					$active = $status === $key;
					$url = admin_url( 'admin.php?page=' . self::SLUG . ( $key !== '' ? '&status=' . $key : '' ) );
					?>
					<li>
						<a href="<?php echo esc_url( $url ); ?>" <?php echo $active ? 'class="current"' : ''; ?>>
							<?php echo esc_html( $label ); ?>
						</a><?php echo ++$i < count( $tabs ) ? ' |' : ''; ?>
					</li>
				<?php endforeach; ?>
			</ul>
			<br class="clear">

			<?php if ( empty( $rows ) ) : ?>
				<p><?php esc_html_e( 'Aucun panier abandonné pour ce filtre.', 'pay4all-pro' ); ?></p>
			<?php else :
				// Standalone bulk-delete form. Kept OUTSIDE the table so the
				// per-row action forms don't end up nested (browsers close
				// the outer form on the first inner <form> otherwise). Each
				// row checkbox uses the HTML5 form= attribute to enroll
				// into this form despite living in a sibling table.
				$bulk_form_id = 'p4all-ac-bulk-form';
				$bulk_nonce   = wp_create_nonce( self::BULK_DELETE_ACTION );
				?>
				<form id="<?php echo esc_attr( $bulk_form_id ); ?>"
				      method="post"
				      action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"
				      onsubmit="return p4allAcBulkConfirm(this);">
					<input type="hidden" name="action" value="<?php echo esc_attr( self::BULK_DELETE_ACTION ); ?>">
					<input type="hidden" name="_wpnonce" value="<?php echo esc_attr( $bulk_nonce ); ?>">
				</form>
				<div class="tablenav top" style="margin:8px 0;display:flex;align-items:center;gap:8px;">
					<button type="submit"
					        form="<?php echo esc_attr( $bulk_form_id ); ?>"
					        class="button button-primary"
					        id="p4all-ac-bulk-delete-btn"
					        disabled>
						<?php esc_html_e( 'Supprimer la sélection', 'pay4all-pro' ); ?>
						(<span id="p4all-ac-bulk-count">0</span>)
					</button>
				</div>
				<table class="widefat striped" id="p4all-ac-table">
					<thead>
						<tr>
							<td class="check-column" style="width:2.2em;">
								<input type="checkbox"
								       id="p4all-ac-select-all"
								       aria-label="<?php esc_attr_e( 'Tout sélectionner', 'pay4all-pro' ); ?>">
							</td>
							<th><?php esc_html_e( 'E-mail', 'pay4all-pro' ); ?></th>
							<th><?php esc_html_e( 'Client', 'pay4all-pro' ); ?></th>
							<th><?php esc_html_e( 'Articles', 'pay4all-pro' ); ?></th>
							<th><?php esc_html_e( 'Total', 'pay4all-pro' ); ?></th>
							<th><?php esc_html_e( 'Statut', 'pay4all-pro' ); ?></th>
							<th><?php esc_html_e( 'Créé', 'pay4all-pro' ); ?></th>
							<th><?php esc_html_e( 'Actions', 'pay4all-pro' ); ?></th>
						</tr>
					</thead>
					<tbody>
					<?php foreach ( $rows as $row ) :
						$customer = json_decode( (string) $row['customer_data'], true ) ?: [];
						$items    = json_decode( (string) $row['items'], true ) ?: [];
						$name     = trim( (string) ( $customer['firstname'] ?? $customer['first_name'] ?? '' ) . ' '
						               . (string) ( $customer['lastname']  ?? $customer['last_name']  ?? '' ) );
						$total_qty = 0;
						foreach ( $items as $q ) { $total_qty += (int) $q; }
						?>
						<tr>
							<td class="check-column">
								<input type="checkbox"
								       class="p4all-ac-row-cb"
								       name="ids[]"
								       value="<?php echo (int) $row['id']; ?>"
								       form="<?php echo esc_attr( $bulk_form_id ); ?>"
								       style="margin-left:10px;"
								       aria-label="<?php
								       printf(
								           /* translators: %s = customer email. */
								           esc_attr__( 'Sélectionner le panier de %s', 'pay4all-pro' ),
								           esc_attr( (string) $row['email'] )
								       );
								       ?>">
							</td>
							<td><code><?php echo esc_html( (string) $row['email'] ); ?></code></td>
							<td><?php echo esc_html( $name ); ?></td>
							<td><?php echo (int) $total_qty; ?> <?php esc_html_e( 'article(s)', 'pay4all-pro' ); ?></td>
							<td><?php echo esc_html( Currency::format( ( (int) $row['total_cents'] ) / 100 ) ); ?></td>
							<td><?php echo self::status_badge( $row ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- helper returns pre-escaped span. ?></td>
							<td><?php echo esc_html( self::fmt_date( (string) $row['created_at'] ) ); ?></td>
							<td>
								<?php $is_converted = ! empty( $row['converted_to_order_id'] ); ?>
								<button type="button" class="button button-small"
								        onclick="document.getElementById('p4all-ac-view-<?php echo (int) $row['id']; ?>').showModal();"
								        style="margin-right:6px;"
								        title="<?php esc_attr_e( 'Voir les détails du panier', 'pay4all-pro' ); ?>"><?php
									esc_html_e( 'Voir', 'pay4all-pro' );
								?></button>
								<?php if ( ! $is_converted ) : ?>
									<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display:inline-block;margin-right:6px;"
									      onsubmit="return confirm('<?php echo esc_js( __( 'Envoyer maintenant le mail de récupération à ce client ?', 'pay4all-pro' ) ); ?>');">
										<?php wp_nonce_field( self::SEND_ACTION, '_p4all_ac_nonce' ); ?>
										<input type="hidden" name="action" value="<?php echo esc_attr( self::SEND_ACTION ); ?>">
										<input type="hidden" name="id" value="<?php echo (int) $row['id']; ?>">
										<button class="button button-small" type="submit"
										        title="<?php esc_attr_e( 'Envoyer le mail de récupération maintenant', 'pay4all-pro' ); ?>"><?php
											esc_html_e( 'Envoyer', 'pay4all-pro' );
										?></button>
									</form>
								<?php endif; ?>
								<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" style="display:inline-block;"
								      onsubmit="return confirm('<?php echo esc_js( __( 'Supprimer définitivement cet enregistrement ?', 'pay4all-pro' ) ); ?>');">
									<?php wp_nonce_field( self::DELETE_ACTION, '_p4all_ac_nonce' ); ?>
									<input type="hidden" name="action" value="<?php echo esc_attr( self::DELETE_ACTION ); ?>">
									<input type="hidden" name="id" value="<?php echo (int) $row['id']; ?>">
									<button class="button button-small button-link-delete" type="submit"
									        title="<?php esc_attr_e( 'Supprimer', 'pay4all-pro' ); ?>">×</button>
								</form>
								<?php echo self::render_view_dialog( $row, $customer, $items ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- helper renvoie du HTML déjà escapé. ?>
							</td>
						</tr>
					<?php endforeach; ?>
					</tbody>
				</table>
				<div class="tablenav bottom" style="margin:8px 0;display:flex;align-items:center;gap:8px;">
					<button type="submit"
					        form="<?php echo esc_attr( $bulk_form_id ); ?>"
					        class="button button-primary p4all-ac-bulk-delete-btn"
					        disabled>
						<?php esc_html_e( 'Supprimer la sélection', 'pay4all-pro' ); ?>
						(<span class="p4all-ac-bulk-count">0</span>)
					</button>
				</div>
				<script>
				(function () {
					var table = document.getElementById('p4all-ac-table');
					if (!table) return;
					var master = document.getElementById('p4all-ac-select-all');
					var rowCbs = table.querySelectorAll('.p4all-ac-row-cb');
					var mainBtn = document.getElementById('p4all-ac-bulk-delete-btn');
					var btns = document.querySelectorAll('#p4all-ac-bulk-delete-btn, .p4all-ac-bulk-delete-btn');
					var counters = document.querySelectorAll('#p4all-ac-bulk-count, .p4all-ac-bulk-count');

					function refresh() {
						var checked = 0;
						for (var i = 0; i < rowCbs.length; i++) { if (rowCbs[i].checked) checked++; }
						for (var j = 0; j < counters.length; j++) { counters[j].textContent = checked; }
						for (var k = 0; k < btns.length; k++) { btns[k].disabled = (checked === 0); }
						if (master) { master.checked = (checked > 0 && checked === rowCbs.length); }
					}

					if (master) {
						master.addEventListener('change', function () {
							for (var i = 0; i < rowCbs.length; i++) { rowCbs[i].checked = master.checked; }
							refresh();
						});
					}
					for (var i = 0; i < rowCbs.length; i++) {
						rowCbs[i].addEventListener('change', refresh);
					}

					// Confirmation on submit, exposed as global so the form onsubmit can call it.
					window.p4allAcBulkConfirm = function () {
						var checked = 0;
						for (var i = 0; i < rowCbs.length; i++) { if (rowCbs[i].checked) checked++; }
						if (checked === 0) return false;
						return window.confirm(
							<?php echo wp_json_encode( __( 'Supprimer définitivement les paniers sélectionnés ?', 'pay4all-pro' ) ); ?>
							+ ' (' + checked + ')'
						);
					};

					refresh();
				})();
				</script>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Génère la modale « Voir » d'un panier abandonné. Affiche les coordonnées
	 * complètes du client, la liste des produits commandés (nom + qty + prix
	 * ligne), la livraison, le mode de paiement et les totaux. Utilise un
	 * <dialog> HTML5 natif déclenché par showModal() en JS sans framework.
	 *
	 * @param array $row      Ligne BDD de l'abandoned cart.
	 * @param array $customer customer_data déjà décodé en tableau associatif.
	 * @param array $items    items déjà décodés (product_id => qty).
	 */
	private static function render_view_dialog( array $row, array $customer, array $items ) : string {
		$cart_id  = (int) $row['id'];
		$currency = (string) ( $row['currency'] ?? 'CHF' );

		ob_start();
		?>
		<dialog id="p4all-ac-view-<?php echo (int) $cart_id; ?>" class="p4all-ac-dialog" style="max-width:640px;width:90%;border:0;border-radius:8px;padding:0;box-shadow:0 20px 60px rgba(0,0,0,0.3);">
			<form method="dialog" style="margin:0;">
				<div style="display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid #e5e5e5;background:#f6f7f7;">
					<h2 style="margin:0;font-size:1.1em;">
						<?php
						printf(
							/* translators: %s = customer e-mail */
							esc_html__( 'Détails du panier — %s', 'pay4all-pro' ),
							esc_html( (string) ( $row['email'] ?? '' ) )
						);
						?>
					</h2>
					<button type="submit" class="button button-small" aria-label="<?php esc_attr_e( 'Fermer', 'pay4all-pro' ); ?>">×</button>
				</div>
				<div style="padding:20px;max-height:70vh;overflow:auto;">
					<h3 style="margin:0 0 8px;font-size:.95em;text-transform:uppercase;color:#666;letter-spacing:.04em;">
						<?php esc_html_e( 'Client', 'pay4all-pro' ); ?>
					</h3>
					<?php if ( empty( $customer ) ) : ?>
						<p class="description"><?php esc_html_e( '— Aucune coordonnée saisie —', 'pay4all-pro' ); ?></p>
					<?php else : ?>
						<table class="widefat striped" style="margin-bottom:16px;">
							<tbody>
							<?php foreach ( $customer as $k => $v ) : ?>
								<tr>
									<th style="width:35%;"><?php echo esc_html( self::humanize_field_key( (string) $k ) ); ?></th>
									<td><?php echo nl2br( esc_html( is_scalar( $v ) ? (string) $v : wp_json_encode( $v ) ) ); ?></td>
								</tr>
							<?php endforeach; ?>
							</tbody>
						</table>
					<?php endif; ?>

					<h3 style="margin:16px 0 8px;font-size:.95em;text-transform:uppercase;color:#666;letter-spacing:.04em;">
						<?php esc_html_e( 'Articles', 'pay4all-pro' ); ?>
					</h3>
					<?php if ( empty( $items ) ) : ?>
						<p class="description"><?php esc_html_e( '— Aucun article —', 'pay4all-pro' ); ?></p>
					<?php else : ?>
						<table class="widefat striped" style="margin-bottom:16px;">
							<thead>
								<tr>
									<th><?php esc_html_e( 'Produit', 'pay4all-pro' ); ?></th>
									<th style="width:80px;text-align:center;"><?php esc_html_e( 'Qté', 'pay4all-pro' ); ?></th>
									<th style="width:120px;text-align:right;"><?php esc_html_e( 'P.U.', 'pay4all-pro' ); ?></th>
									<th style="width:120px;text-align:right;"><?php esc_html_e( 'Total', 'pay4all-pro' ); ?></th>
								</tr>
							</thead>
							<tbody>
							<?php foreach ( $items as $pid => $qty ) :
								$pid       = (int) $pid;
								$qty       = (int) $qty;
								$post      = $pid > 0 ? get_post( $pid ) : null;
								$prod_name = $post ? \Pay4All\Form\Domain\Product::display_title( $pid ) : '#' . $pid;
								$unit      = $post ? (float) \Pay4All\Form\Domain\Product::price( $pid ) : 0.0;
								$line      = $unit * $qty;
								?>
								<tr>
									<td>
										<?php if ( $post ) : ?>
											<a href="<?php echo esc_url( (string) get_edit_post_link( $pid ) ); ?>"><?php echo esc_html( $prod_name ); ?></a>
										<?php else : ?>
											<em><?php echo esc_html( $prod_name ); ?></em>
											<span class="description"><?php esc_html_e( '(produit supprimé)', 'pay4all-pro' ); ?></span>
										<?php endif; ?>
									</td>
									<td style="text-align:center;"><?php echo (int) $qty; ?></td>
									<td style="text-align:right;"><?php echo esc_html( \Pay4All\Form\Services\Currency::format( $unit ) ); ?></td>
									<td style="text-align:right;"><?php echo esc_html( \Pay4All\Form\Services\Currency::format( $line ) ); ?></td>
								</tr>
							<?php endforeach; ?>
							</tbody>
						</table>
					<?php endif; ?>

					<h3 style="margin:16px 0 8px;font-size:.95em;text-transform:uppercase;color:#666;letter-spacing:.04em;">
						<?php esc_html_e( 'Livraison & paiement', 'pay4all-pro' ); ?>
					</h3>
					<?php
					$delivery_id    = (int) ( $row['delivery_id'] ?? 0 );
					$delivery_label = '';
					if ( $delivery_id > 0 ) {
						$delivery_post  = get_post( $delivery_id );
						$delivery_label = $delivery_post ? (string) $delivery_post->post_title : '#' . $delivery_id;
					}
					$payment_method = (string) ( $row['payment_method'] ?? '' );
					?>
					<table class="widefat striped" style="margin-bottom:16px;">
						<tbody>
							<tr>
								<th style="width:35%;"><?php esc_html_e( 'Mode de livraison', 'pay4all-pro' ); ?></th>
								<td><?php echo esc_html( $delivery_label !== '' ? $delivery_label : __( '— Non renseigné —', 'pay4all-pro' ) ); ?></td>
							</tr>
							<tr>
								<th><?php esc_html_e( 'Mode de paiement', 'pay4all-pro' ); ?></th>
								<td><?php echo esc_html(
									$payment_method !== ''
										? ListTable::payment_method_label( $payment_method )
										: __( '— Non renseigné —', 'pay4all-pro' )
								); ?></td>
							</tr>
						</tbody>
					</table>

					<h3 style="margin:16px 0 8px;font-size:.95em;text-transform:uppercase;color:#666;letter-spacing:.04em;">
						<?php esc_html_e( 'Totaux', 'pay4all-pro' ); ?>
					</h3>
					<table class="widefat striped" style="margin-bottom:16px;">
						<tbody>
							<tr>
								<th style="width:35%;"><?php esc_html_e( 'Sous-total', 'pay4all-pro' ); ?></th>
								<td><?php echo esc_html( \Pay4All\Form\Services\Currency::format( ( (int) $row['subtotal_cents'] ) / 100 ) ); ?></td>
							</tr>
							<tr>
								<th><?php esc_html_e( 'Livraison', 'pay4all-pro' ); ?></th>
								<td><?php echo esc_html( \Pay4All\Form\Services\Currency::format( ( (int) $row['delivery_cents'] ) / 100 ) ); ?></td>
							</tr>
							<tr>
								<th><strong><?php esc_html_e( 'Total', 'pay4all-pro' ); ?></strong></th>
								<td><strong><?php echo esc_html( \Pay4All\Form\Services\Currency::format( ( (int) $row['total_cents'] ) / 100 ) ); ?></strong></td>
							</tr>
						</tbody>
					</table>

					<p class="description" style="font-size:.85em;color:#888;">
						<?php
						printf(
							/* translators: 1: created date, 2: last update date */
							esc_html__( 'Créé le %1$s · Dernière mise à jour : %2$s', 'pay4all-pro' ),
							esc_html( self::fmt_date( (string) $row['created_at'] ) ),
							esc_html( self::fmt_date( (string) $row['last_updated_at'] ) )
						);
						?>
					</p>
				</div>
				<div style="padding:12px 20px;border-top:1px solid #e5e5e5;text-align:right;background:#f6f7f7;">
					<button type="submit" class="button button-primary"><?php esc_html_e( 'Fermer', 'pay4all-pro' ); ?></button>
				</div>
			</form>
		</dialog>
		<?php
		return (string) ob_get_clean();
	}

	/**
	 * Tente de transformer un identifiant de champ (firstname, postal_code,
	 * etc.) en libellé lisible. Pas une vraie traduction — juste une
	 * cosmétique pour éviter `firstname` brut dans la popup.
	 */
	private static function humanize_field_key( string $key ) : string {
		$labels = [
			'firstname'   => __( 'Prénom', 'pay4all-pro' ),
			'first_name'  => __( 'Prénom', 'pay4all-pro' ),
			'lastname'    => __( 'Nom', 'pay4all-pro' ),
			'last_name'   => __( 'Nom', 'pay4all-pro' ),
			'email'       => __( 'E-mail', 'pay4all-pro' ),
			'phone'       => __( 'Téléphone', 'pay4all-pro' ),
			'company'     => __( 'Société', 'pay4all-pro' ),
			'address'     => __( 'Adresse', 'pay4all-pro' ),
			'street'      => __( 'Rue', 'pay4all-pro' ),
			'postal_code' => __( 'Code postal', 'pay4all-pro' ),
			'zipcode'     => __( 'Code postal', 'pay4all-pro' ),
			'city'        => __( 'Ville', 'pay4all-pro' ),
			'country'     => __( 'Pays', 'pay4all-pro' ),
			'message'     => __( 'Message', 'pay4all-pro' ),
			'notes'       => __( 'Notes', 'pay4all-pro' ),
		];
		if ( isset( $labels[ $key ] ) ) { return $labels[ $key ]; }
		// Fallback : transforme "some_key" en "Some key" pour lisibilité.
		return ucfirst( strtolower( str_replace( '_', ' ', $key ) ) );
	}

	/**
	 * Formate une date MySQL (UTC, Y-m-d H:i:s) au format européen jj.mm.aaaa
	 * HH:MM dans le fuseau du site. On omet volontairement les secondes :
	 * la précision n'apporte rien dans l'admin et alourdit la lecture.
	 */
	private static function fmt_date( string $mysql ) : string {
		if ( $mysql === '' ) { return ''; }
		// Les dates sont stockées en UTC (gmdate). get_date_from_gmt() convertit
		// vers le fuseau configuré dans Réglages › Général › Fuseau horaire.
		$local = get_date_from_gmt( $mysql, 'd.m.Y H:i' );
		return $local ?: $mysql;
	}

	/**
	 * Renvoie un <span class="dashicons-..."> esc-safe pour la colonne statut.
	 */
	private static function status_badge( array $row ) : string {
		if ( ! empty( $row['converted_to_order_id'] ) ) {
			// Pas de colonne converted_at dans la table — on lit le post_date
			// de la commande liée, c'est la définition même du « converti ».
			$order_id = (int) $row['converted_to_order_id'];
			$order    = get_post( $order_id );
			$date_src = $order ? (string) $order->post_date_gmt : (string) ( $row['last_updated_at'] ?? '' );
			return '<span style="color:#46b450;">'
			     . esc_html( sprintf(
			         /* translators: %s = formatted date, e.g. 29.11.2025 15:34 */
			         __( 'Converti %s', 'pay4all-pro' ),
			         self::fmt_date( $date_src )
			     ) )
			     . '</span>';
		}
		if ( ! empty( $row['recovered_at'] ) ) {
			return '<span style="color:#2271b1;">'
			     . esc_html( sprintf(
			         /* translators: %s = formatted date, e.g. 29.11.2025 15:34 */
			         __( 'Récupéré %s', 'pay4all-pro' ),
			         self::fmt_date( (string) $row['recovered_at'] )
			     ) )
			     . '</span>';
		}
		if ( ! empty( $row['email_sent_at'] ) ) {
			return '<span style="color:#7c3aed;">'
			     . esc_html( sprintf(
			         /* translators: %s = formatted date, e.g. 29.11.2025 15:34 */
			         __( 'Envoyé %s', 'pay4all-pro' ),
			         self::fmt_date( (string) $row['email_sent_at'] )
			     ) )
			     . '</span>';
		}
		return '<span style="color:#999;">⏳ ' . esc_html__( 'En attente', 'pay4all-pro' ) . '</span>';
	}

	/**
	 * Enregistre la valeur choisie de `abandoned_cart_delay_days` (nombre de
	 * jours avant l'envoi de la relance, ou 0 pour désactiver l'envoi
	 * automatique). Écrit directement dans l'option `p4all_form_settings`
	 * (partagée avec Pay4All Form) — Schema n'expose pas de setter global.
	 * Validé contre la liste blanche `delay_choices()`.
	 */
	public static function handle_set_delay() : void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission refusée.', 'pay4all-pro' ) );
		}
		check_admin_referer( self::SET_DELAY_ACTION );
		$requested = isset( $_POST['delay_days'] ) ? (int) $_POST['delay_days'] : -1;
		$allowed   = array_keys( self::delay_choices() );
		if ( ! in_array( $requested, $allowed, true ) ) {
			// Valeur non-whitelistée : on ignore et on renvoie sur la liste.
			wp_safe_redirect( admin_url( 'admin.php?page=' . self::SLUG ) );
			exit;
		}
		$settings = get_option( 'p4all_form_settings', [] );
		if ( ! is_array( $settings ) ) { $settings = []; }
		$settings['abandoned_cart_delay_days'] = $requested;
		update_option( 'p4all_form_settings', $settings );
		wp_safe_redirect( admin_url( 'admin.php?page=' . self::SLUG ) );
		exit;
	}

	public static function handle_delete() : void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission refusée.', 'pay4all-pro' ) );
		}
		check_admin_referer( self::DELETE_ACTION, '_p4all_ac_nonce' );
		$id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;
		if ( $id > 0 ) { AbandonedCart::delete( $id ); }
		wp_safe_redirect( admin_url( 'admin.php?page=' . self::SLUG ) );
		exit;
	}

	/**
	 * Bulk delete : accepts `ids[]` from the standalone bulk form
	 * emitted at the top of the list. Redirects back with the count so
	 * the page can flash a success notice. Standard WP nonce field
	 * (`_wpnonce`) since check_admin_referer defaults to that name.
	 */
	public static function handle_bulk_delete() : void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission refusée.', 'pay4all-pro' ) );
		}
		check_admin_referer( self::BULK_DELETE_ACTION );

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- values coerced to int below.
		$raw   = isset( $_POST['ids'] ) && is_array( $_POST['ids'] ) ? (array) wp_unslash( $_POST['ids'] ) : [];
		$deleted = 0;
		foreach ( $raw as $id ) {
			$id = (int) $id;
			if ( $id > 0 ) {
				AbandonedCart::delete( $id );
				$deleted++;
			}
		}
		$url = add_query_arg(
			[ 'page' => self::SLUG, 'bulk_deleted' => $deleted ],
			admin_url( 'admin.php' )
		);
		wp_safe_redirect( $url );
		exit;
	}

	/**
	 * Envoie manuellement le mail de récupération à un client (sans attendre
	 * le cron). Permet aussi de relancer un client à qui le mail a déjà été
	 * envoyé : on remet à jour email_sent_at à l'instant courant.
	 */
	public static function handle_send() : void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission refusée.', 'pay4all-pro' ) );
		}
		check_admin_referer( self::SEND_ACTION, '_p4all_ac_nonce' );
		$id = isset( $_POST['id'] ) ? absint( wp_unslash( $_POST['id'] ) ) : 0;

		$status_param = 'send_err';
		if ( $id > 0 ) {
			$cart = AbandonedCart::find_by_id( $id );
			if ( $cart !== null ) {
				$ok = Mailer::send_abandoned_cart_recovery( $cart );
				if ( $ok ) {
					AbandonedCart::mark_email_sent( $id );
					$status_param = 'sent';
				}
			}
		}
		wp_safe_redirect( add_query_arg( $status_param, '1', admin_url( 'admin.php?page=' . self::SLUG ) ) );
		exit;
	}
}
