<?php
/**
 * Orders admin list and detail.
 *
 * @package WPDudeProducts
 */

declare(strict_types=1);

namespace WPDude\Products\Admin;

use WPDude\Products\Orders\Order_Repository;
use WPDude\Products\Orders\Statuses;
use WPDude\Products\Products\Post_Type as Product_Post_Type;
use WPDude\Products\Products\Product_Meta;
use WPDude\Products\Products\Stock_Service;
use WPDude\Products\Providers\Stripe\Stripe_Provider;
use WPDude\Products\Support\Countries;
use WPDude\Products\Support\Money;

/**
 * Admin orders UI.
 */
final class Orders_Page {

	public const PAGE_SLUG = 'dude-commerce-orders';

	/**
	 * Register hooks.
	 */
	public function register(): void {
		add_action( 'admin_init', array( $this, 'handle_status_action' ) );
		add_action( 'admin_init', array( $this, 'handle_refund_action' ) );
		add_action( 'admin_init', array( $this, 'handle_picked_action' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
	}

	/**
	 * Orders list / detail styles.
	 */
	public function enqueue_assets( string $hook_suffix ): void {
		unset( $hook_suffix );
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- admin screen check.
		$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( (string) $_GET['page'] ) ) : '';
		if ( self::PAGE_SLUG !== $page ) {
			return;
		}
		wp_enqueue_style(
			'wpdude-dc-admin-orders',
			WPDUDE_DC_URL . 'assets/css/admin-orders.css',
			array(),
			WPDUDE_DC_VERSION
		);
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! empty( $_GET['order'] ) ) {
			wp_enqueue_script(
				'wpdude-dc-admin-orders',
				WPDUDE_DC_URL . 'assets/js/admin-orders.js',
				array(),
				WPDUDE_DC_VERSION,
				true
			);
		}
	}

	/**
	 * Orders list URL.
	 */
	public static function list_url( array $args = array() ): string {
		return add_query_arg(
			array_merge(
				array( 'page' => self::PAGE_SLUG ),
				$args
			),
			admin_url( 'admin.php' )
		);
	}

	/**
	 * Handle manual status transitions from detail view.
	 */
	public function handle_status_action(): void {
		if ( empty( $_POST['wpdude_dc_order_status_nonce'] ) || empty( $_POST['wpdude_dc_order_uuid'] ) || empty( $_POST['wpdude_dc_order_new_status'] ) ) {
			return;
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( (string) $_POST['wpdude_dc_order_status_nonce'] ) ), 'wpdude_dc_order_status' ) ) {
			return;
		}

		$uuid   = sanitize_text_field( wp_unslash( (string) $_POST['wpdude_dc_order_uuid'] ) );
		$to     = sanitize_key( wp_unslash( (string) $_POST['wpdude_dc_order_new_status'] ) );
		$repo   = new Order_Repository();
		$order  = $repo->find_by_uuid( $uuid );

		$redirect = self::list_url( array( 'order' => $uuid ) );

		if ( ! $order ) {
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'not_found', $redirect ) );
			exit;
		}

		if ( ! in_array( $to, Statuses::admin_actionable(), true ) ) {
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'invalid', $redirect ) );
			exit;
		}

		$result = $repo->transition_status( (int) $order['id'], $to );
		if ( is_wp_error( $result ) ) {
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'transition', $redirect ) );
			exit;
		}

		wp_safe_redirect( add_query_arg( 'wpdude_dc_order_updated', '1', $redirect ) );
		exit;
	}

	/**
	 * Refund the order via Stripe, then mark refunded locally.
	 */
	public function handle_refund_action(): void {
		if ( empty( $_POST['wpdude_dc_order_refund_nonce'] ) || empty( $_POST['wpdude_dc_order_uuid'] ) || empty( $_POST['wpdude_dc_refund_stripe'] ) ) {
			return;
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( (string) $_POST['wpdude_dc_order_refund_nonce'] ) ), 'wpdude_dc_order_refund' ) ) {
			return;
		}

		$uuid     = sanitize_text_field( wp_unslash( (string) $_POST['wpdude_dc_order_uuid'] ) );
		$repo     = new Order_Repository();
		$order    = $repo->find_by_uuid( $uuid );
		$redirect = self::list_url( array( 'order' => $uuid ) );

		if ( ! $order ) {
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'not_found', $redirect ) );
			exit;
		}

		$pi = (string) ( $order['stripe_payment_intent_id'] ?? '' );
		if ( '' === $pi ) {
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'no_payment', $redirect ) );
			exit;
		}

		if ( ! Statuses::can_transition( (string) $order['status'], Statuses::REFUNDED ) ) {
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'transition', $redirect ) );
			exit;
		}

		$result = ( new Stripe_Provider() )->refund_payment_intent( $pi );
		if ( is_wp_error( $result ) ) {
			self::store_order_notice( $result->get_error_message() );
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'refund', $redirect ) );
			exit;
		}

		$transition = $repo->transition_status( (int) $order['id'], Statuses::REFUNDED );
		if ( is_wp_error( $transition ) ) {
			// Stripe refund succeeded; webhook may still sync status.
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_refunded', 'stripe_only', $redirect ) );
			exit;
		}

		wp_safe_redirect( add_query_arg( 'wpdude_dc_order_refunded', '1', $redirect ) );
		exit;
	}

	/**
	 * Mark order as picked (fulfilment meta only — status stays New).
	 */
	public function handle_picked_action(): void {
		if ( empty( $_POST['wpdude_dc_order_picked_nonce'] ) || empty( $_POST['wpdude_dc_order_uuid'] ) || empty( $_POST['wpdude_dc_mark_picked'] ) ) {
			return;
		}
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}
		if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( (string) $_POST['wpdude_dc_order_picked_nonce'] ) ), 'wpdude_dc_order_picked' ) ) {
			return;
		}

		$uuid     = sanitize_text_field( wp_unslash( (string) $_POST['wpdude_dc_order_uuid'] ) );
		$repo     = new Order_Repository();
		$order    = $repo->find_by_uuid( $uuid );
		$redirect = self::list_url( array( 'order' => $uuid ) );

		if ( ! $order ) {
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'not_found', $redirect ) );
			exit;
		}

		if ( Statuses::NEW !== (string) $order['status'] ) {
			wp_safe_redirect( add_query_arg( 'wpdude_dc_order_error', 'transition', $redirect ) );
			exit;
		}

		$repo->update(
			(int) $order['id'],
			array(
				'picked_at' => current_time( 'mysql', true ),
			)
		);

		wp_safe_redirect( add_query_arg( 'wpdude_dc_order_picked', '1', $redirect ) );
		exit;
	}

	/**
	 * Persist a short-lived admin notice for the order detail page.
	 */
	private static function store_order_notice( string $message ): void {
		$message = trim( $message );
		if ( '' === $message ) {
			return;
		}
		set_transient( 'wpdude_dc_order_notice_' . get_current_user_id(), $message, 5 * MINUTE_IN_SECONDS );
	}

	/**
	 * Read and clear the stored order notice.
	 */
	private static function consume_order_notice(): string {
		$key = 'wpdude_dc_order_notice_' . get_current_user_id();
		$msg = get_transient( $key );
		delete_transient( $key );
		return is_string( $msg ) ? $msg : '';
	}

	/**
	 * Render list or detail.
	 */
	public static function render(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You do not have permission to access this page.', 'dude-commerce' ) );
		}

		$repo = new Order_Repository();

		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only admin list/detail; manage_options checked above.
		$uuid = isset( $_GET['order'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['order'] ) ) : '';
		// phpcs:enable WordPress.Security.NonceVerification.Recommended

		if ( $uuid ) {
			self::render_detail( $repo, $uuid );
			return;
		}

		self::render_list( $repo );
	}

	/**
	 * List view.
	 */
	private static function render_list( Order_Repository $repo ): void {
		$filters = self::filters_from_request();

		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only pagination.
		$page = isset( $_GET['paged'] ) ? max( 1, absint( $_GET['paged'] ) ) : 1;
		// phpcs:enable WordPress.Security.NonceVerification.Recommended

		$per_page = 20;
		$result   = $repo->query(
			array_merge(
				$filters,
				array(
					'page'     => $page,
					'per_page' => $per_page,
				)
			)
		);

		$total_pages = (int) max( 1, (int) ceil( $result['total'] / $per_page ) );
		$products    = self::product_choices();
		?>
		<div class="wrap">
			<h1 class="wp-heading-inline"><?php echo esc_html__( 'Orders', 'dude-commerce' ); ?></h1>
			<?php
			/**
			 * Title actions on the orders list (e.g. Export CSV).
			 */
			do_action( 'wpdude_dc_orders_page_title_actions' );
			?>
			<hr class="wp-header-end" />

			<form method="get" style="margin:1em 0;">
				<input type="hidden" name="page" value="<?php echo esc_attr( self::PAGE_SLUG ); ?>" />
				<select name="status">
					<option value=""><?php echo esc_html__( 'All statuses', 'dude-commerce' ); ?></option>
					<?php foreach ( Statuses::all() as $st ) : ?>
						<option value="<?php echo esc_attr( $st ); ?>" <?php selected( $filters['status'], $st ); ?>><?php echo esc_html( Statuses::label( $st ) ); ?></option>
					<?php endforeach; ?>
				</select>
				<select name="product_id">
					<option value=""><?php echo esc_html__( 'All products', 'dude-commerce' ); ?></option>
					<?php foreach ( $products as $pid => $title ) : ?>
						<option value="<?php echo esc_attr( (string) $pid ); ?>" <?php selected( $filters['product_id'], $pid ); ?>><?php echo esc_html( $title ); ?></option>
					<?php endforeach; ?>
				</select>
				<input type="date" name="date_from" value="<?php echo esc_attr( $filters['date_from'] ); ?>" />
				<input type="date" name="date_to" value="<?php echo esc_attr( $filters['date_to'] ); ?>" />
				<?php submit_button( __( 'Filter', 'dude-commerce' ), 'secondary', '', false ); ?>
			</form>

			<p class="description">
				<?php
				echo esc_html(
					sprintf(
						/* translators: %d: order count */
						_n( '%d order', '%d orders', $result['total'], 'dude-commerce' ),
						$result['total']
					)
				);
				?>
			</p>

			<table class="widefat striped">
				<thead>
					<tr>
						<th><?php echo esc_html__( 'Date', 'dude-commerce' ); ?></th>
						<th><?php echo esc_html__( 'Customer', 'dude-commerce' ); ?></th>
						<th><?php echo esc_html__( 'Email', 'dude-commerce' ); ?></th>
						<th><?php echo esc_html__( 'Product', 'dude-commerce' ); ?></th>
						<th><?php echo esc_html__( 'Qty', 'dude-commerce' ); ?></th>
						<th><?php echo esc_html__( 'Amount', 'dude-commerce' ); ?></th>
						<th><?php echo esc_html__( 'Status', 'dude-commerce' ); ?></th>
						<th><?php echo esc_html__( 'Risk', 'dude-commerce' ); ?></th>
						<th><?php echo esc_html__( 'Stripe', 'dude-commerce' ); ?></th>
					</tr>
				</thead>
				<tbody>
				<?php if ( empty( $result['items'] ) ) : ?>
					<tr><td colspan="9"><?php echo esc_html__( 'No orders found.', 'dude-commerce' ); ?></td></tr>
				<?php else : ?>
					<?php foreach ( $result['items'] as $order ) : ?>
						<?php
						$detail     = self::list_url( array( 'order' => (string) $order['uuid'] ) );
						$date       = (string) ( $order['paid_at'] ?: $order['created_at'] );
						$name       = trim( (string) ( $order['customer_name'] ?? '' ) );
						$email      = trim( (string) ( $order['customer_email'] ?? '' ) );
						$risk_level = sanitize_key( (string) ( $order['stripe_risk_level'] ?? '' ) );
						$risk_score = isset( $order['stripe_risk_score'] ) && '' !== (string) $order['stripe_risk_score']
							? (int) $order['stripe_risk_score']
							: null;
						?>
						<tr>
							<td><a href="<?php echo esc_url( $detail ); ?>"><?php echo esc_html( $date ); ?></a></td>
							<td><?php echo esc_html( '' !== $name ? $name : '—' ); ?></td>
							<td>
								<?php if ( '' !== $email ) : ?>
									<a href="<?php echo esc_url( 'mailto:' . $email ); ?>"><?php echo esc_html( $email ); ?></a>
								<?php else : ?>
									—
								<?php endif; ?>
							</td>
							<td><?php echo esc_html( self::product_label( $order ) ); ?></td>
							<td><?php echo esc_html( (string) (int) $order['qty'] ); ?></td>
							<td><?php echo esc_html( Money::format( (int) $order['amount'], (string) $order['currency'] ) ); ?></td>
							<td><?php echo wp_kses_post( Statuses::badge_html( (string) $order['status'] ) ); ?></td>
							<td>
								<?php
								echo '' !== $risk_level
									? wp_kses_post( self::risk_badge_html( $risk_level, $risk_score, true ) )
									: '—';
								?>
							</td>
							<td><code><?php echo esc_html( (string) ( $order['stripe_checkout_session_id'] ?? '' ) ); ?></code></td>
						</tr>
					<?php endforeach; ?>
				<?php endif; ?>
				</tbody>
			</table>

			<?php if ( $total_pages > 1 ) : ?>
				<div class="tablenav bottom">
					<div class="tablenav-pages">
						<?php
						echo wp_kses_post(
							paginate_links(
								array(
									'base'      => add_query_arg( 'paged', '%#%' ),
									'format'    => '',
									'current'   => $page,
									'total'     => $total_pages,
									'prev_text' => '&laquo;',
									'next_text' => '&raquo;',
								)
							) ?: ''
						);
						?>
					</div>
				</div>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Detail view.
	 */
	private static function render_detail( Order_Repository $repo, string $uuid ): void {
		$order = $repo->find_by_uuid( $uuid );
		if ( ! $order ) {
			echo '<div class="wrap"><p>' . esc_html__( 'Order not found.', 'dude-commerce' ) . '</p></div>';
			return;
		}

		$back          = self::list_url();
		$status        = (string) ( $order['status'] ?? '' );
		$pi            = (string) ( $order['stripe_payment_intent_id'] ?? '' );
		$pi_url        = '' !== $pi ? Stripe_Provider::dashboard_payment_url( $pi ) : '';
		$can_refund    = '' !== $pi && Statuses::can_transition( $status, Statuses::REFUNDED );
		$can_return    = Statuses::can_transition( $status, Statuses::RETURNED );
		$can_ship      = Statuses::can_transition( $status, Statuses::SHIPPED );
		$can_revert    = Statuses::can_transition( $status, Statuses::NEW );
		$sku           = self::order_sku( $order );
		$product_name  = self::clean_product_name( $order );
		$option_lines  = self::product_options_lines( $order );
		$image_url     = self::product_image_url( $order );
		$money         = self::money_breakdown( $order );
		$address_lines = self::delivery_address_lines( $order );
		$address_text  = self::format_delivery_address( $order );
		$customer_name  = trim( (string) ( $order['customer_name'] ?? '' ) );
		$customer_email = trim( (string) ( $order['customer_email'] ?? '' ) );
		$paid_at       = trim( (string) ( $order['paid_at'] ?? '' ) );
		$picked_at     = trim( (string) ( $order['picked_at'] ?? '' ) );
		$shipped_at    = trim( (string) ( $order['shipped_at'] ?? '' ) );
		$order_id      = (int) ( $order['id'] ?? 0 );
		$qty           = (int) ( $order['qty'] ?? 0 );
		$order         = self::ensure_stripe_charge_details( $order );
		$risk_level    = sanitize_key( (string) ( $order['stripe_risk_level'] ?? '' ) );
		$risk_score    = isset( $order['stripe_risk_score'] ) && '' !== (string) $order['stripe_risk_score']
			? (int) $order['stripe_risk_score']
			: null;
		$fee_currency  = strtolower( (string) ( $order['stripe_fee_currency'] ?? $order['currency'] ?? 'gbp' ) );
		$fee_raw       = $order['stripe_fee_amount'] ?? null;
		$fee_amount    = ( null !== $fee_raw && '' !== (string) $fee_raw )
			? (int) $fee_raw
			: null;
		$net_raw       = $order['stripe_net_amount'] ?? null;
		$net_amount    = ( null !== $net_raw && '' !== (string) $net_raw )
			? (int) $net_raw
			: null;

		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- admin notice query args after redirect.
		$updated  = isset( $_GET['wpdude_dc_order_updated'] );
		$refunded = isset( $_GET['wpdude_dc_order_refunded'] ) ? sanitize_key( wp_unslash( (string) $_GET['wpdude_dc_order_refunded'] ) ) : '';
		$picked   = isset( $_GET['wpdude_dc_order_picked'] );
		$error    = isset( $_GET['wpdude_dc_order_error'] ) ? sanitize_key( wp_unslash( (string) $_GET['wpdude_dc_order_error'] ) ) : '';
		// phpcs:enable WordPress.Security.NonceVerification.Recommended
		$notice = self::consume_order_notice();
		?>
		<div class="wrap wpdude-dc-order-detail">
			<p class="wpdude-dc-order-detail__back">
				<a href="<?php echo esc_url( $back ); ?>">&larr; <?php echo esc_html__( 'Back to orders', 'dude-commerce' ); ?></a>
			</p>

			<?php if ( $updated ) : ?>
				<div class="notice notice-success is-dismissible"><p><?php echo esc_html__( 'Order status updated.', 'dude-commerce' ); ?></p></div>
			<?php endif; ?>
			<?php if ( '1' === $refunded ) : ?>
				<div class="notice notice-success is-dismissible"><p><?php echo esc_html__( 'Payment refunded in Stripe and order marked as refunded.', 'dude-commerce' ); ?></p></div>
			<?php elseif ( 'stripe_only' === $refunded ) : ?>
				<div class="notice notice-warning is-dismissible"><p><?php echo esc_html__( 'Payment refunded in Stripe. Order status will update when the Stripe webhook arrives (or mark refunded manually).', 'dude-commerce' ); ?></p></div>
			<?php endif; ?>
			<?php if ( $picked ) : ?>
				<div class="notice notice-success is-dismissible"><p><?php echo esc_html__( 'Order marked as picked.', 'dude-commerce' ); ?></p></div>
			<?php endif; ?>
			<?php if ( '' !== $error ) : ?>
				<div class="notice notice-error is-dismissible"><p><?php echo esc_html( self::error_message( $error, $notice ) ); ?></p></div>
			<?php endif; ?>

			<div class="wpdude-dc-order-detail__header">
				<div>
					<div class="wpdude-dc-order-detail__title-row">
						<h1 class="wpdude-dc-order-detail__title">
							<?php
							echo esc_html(
								sprintf(
									/* translators: %d: order post ID */
									__( 'Order #%d', 'dude-commerce' ),
									$order_id
								)
							);
							?>
						</h1>
						<?php echo wp_kses_post( Statuses::badge_html( $status ) ); ?>
						<?php if ( '' !== $risk_level ) : ?>
							<?php echo wp_kses_post( self::risk_badge_html( $risk_level, $risk_score ) ); ?>
						<?php endif; ?>
					</div>
					<?php if ( '' !== $paid_at ) : ?>
						<p class="wpdude-dc-order-detail__paid">
							<?php
							echo esc_html(
								sprintf(
									/* translators: %s: formatted paid datetime */
									__( 'Paid %s', 'dude-commerce' ),
									self::format_datetime( $paid_at )
								)
							);
							?>
						</p>
					<?php endif; ?>
				</div>

				<?php if ( '' !== $pi_url ) : ?>
					<div class="wpdude-dc-order-detail__actions">
						<a class="button" href="<?php echo esc_url( $pi_url ); ?>" target="_blank" rel="noopener noreferrer">
							<?php echo esc_html__( 'View in Stripe', 'dude-commerce' ); ?>
						</a>
					</div>
				<?php endif; ?>
			</div>

			<div class="wpdude-dc-order-card">
				<div class="wpdude-dc-order-detail__split">
					<div>
						<p class="wpdude-dc-order-detail__label"><?php echo esc_html__( 'Customer', 'dude-commerce' ); ?></p>
						<?php if ( '' !== $customer_name ) : ?>
							<p class="wpdude-dc-order-detail__name"><?php echo esc_html( $customer_name ); ?></p>
						<?php endif; ?>
						<?php if ( '' !== $customer_email ) : ?>
							<p class="wpdude-dc-order-detail__email">
								<a href="<?php echo esc_url( 'mailto:' . $customer_email ); ?>"><?php echo esc_html( $customer_email ); ?></a>
							</p>
						<?php elseif ( '' === $customer_name ) : ?>
							<p class="wpdude-dc-order-detail__name">—</p>
						<?php endif; ?>
					</div>
					<div>
						<p class="wpdude-dc-order-detail__label"><?php echo esc_html__( 'Delivery Address', 'dude-commerce' ); ?></p>
						<p class="wpdude-dc-order-detail__address"><?php echo esc_html( $address_text ); ?></p>
						<?php if ( ! empty( $address_lines ) ) : ?>
							<p class="wpdude-dc-order-detail__copy">
								<button
									type="button"
									class="button"
									data-wpdude-dc-copy-address="<?php echo esc_attr( $address_text ); ?>"
								>
									<?php echo esc_html__( 'Copy Address', 'dude-commerce' ); ?>
								</button>
								<span class="wpdude-dc-order-detail__copy-feedback" hidden><?php echo esc_html__( 'Copied', 'dude-commerce' ); ?></span>
							</p>
						<?php endif; ?>
					</div>
				</div>
			</div>

			<div class="wpdude-dc-order-card">
				<h2 class="wpdude-dc-order-card__title"><?php echo esc_html__( 'Product ordered', 'dude-commerce' ); ?></h2>
				<div class="wpdude-dc-order-product">
					<?php if ( '' !== $image_url ) : ?>
						<img class="wpdude-dc-order-product__image" src="<?php echo esc_url( $image_url ); ?>" alt="" />
					<?php else : ?>
						<div class="wpdude-dc-order-product__image wpdude-dc-order-product__image--empty" aria-hidden="true">
							<?php echo esc_html__( 'No image', 'dude-commerce' ); ?>
						</div>
					<?php endif; ?>
					<div>
						<h3 class="wpdude-dc-order-product__name"><?php echo esc_html( $product_name ); ?></h3>
						<ul class="wpdude-dc-order-product__meta">
							<?php foreach ( $option_lines as $option_line ) : ?>
								<li><?php echo esc_html( $option_line ); ?></li>
							<?php endforeach; ?>
							<li>
								<?php
								echo esc_html__( 'SKU', 'dude-commerce' ) . ': ';
								echo '' !== $sku ? esc_html( $sku ) : '—';
								?>
							</li>
							<li>
								<?php
								echo esc_html(
									sprintf(
										/* translators: %d: quantity */
										__( 'Quantity: %d', 'dude-commerce' ),
										$qty
									)
								);
								?>
							</li>
						</ul>
					</div>
				</div>
				<div class="wpdude-dc-order-totals">
					<p class="wpdude-dc-order-totals__row">
						<span><?php echo esc_html__( 'Item subtotal', 'dude-commerce' ); ?></span>
						<span><?php echo esc_html( Money::format( $money['subtotal'], $money['currency'] ) ); ?></span>
					</p>
					<p class="wpdude-dc-order-totals__row">
						<span><?php echo esc_html__( 'Shipping', 'dude-commerce' ); ?></span>
						<span><?php echo esc_html( Money::format( $money['shipping'], $money['currency'] ) ); ?></span>
					</p>
					<p class="wpdude-dc-order-totals__row">
						<span><?php echo esc_html__( 'Tax', 'dude-commerce' ); ?></span>
						<span><?php echo esc_html( Money::format( $money['tax'], $money['currency'] ) ); ?></span>
					</p>
					<p class="wpdude-dc-order-totals__row wpdude-dc-order-totals__row--total">
						<span><?php echo esc_html__( 'Total', 'dude-commerce' ); ?></span>
						<span><?php echo esc_html( Money::format( $money['total'], $money['currency'] ) ); ?></span>
					</p>
				</div>
			</div>

			<div class="wpdude-dc-order-card">
				<h2 class="wpdude-dc-order-card__title"><?php echo esc_html__( 'Fulfilment', 'dude-commerce' ); ?></h2>
				<div class="wpdude-dc-order-fulfilil__rows">
					<div class="wpdude-dc-order-fulfilil__row">
						<span class="wpdude-dc-order-fulfilil__key"><?php echo esc_html__( 'Stock updated', 'dude-commerce' ); ?></span>
						<span><?php echo esc_html( ! empty( $order['stock_decremented'] ) ? __( 'Yes', 'dude-commerce' ) : __( 'No', 'dude-commerce' ) ); ?></span>
					</div>
					<div class="wpdude-dc-order-fulfilil__row">
						<span class="wpdude-dc-order-fulfilil__key"><?php echo esc_html__( 'Order status', 'dude-commerce' ); ?></span>
						<span><?php echo wp_kses_post( Statuses::badge_html( $status ) ); ?></span>
					</div>
					<?php if ( '' !== $shipped_at ) : ?>
						<div class="wpdude-dc-order-fulfilil__row">
							<span class="wpdude-dc-order-fulfilil__key"><?php echo esc_html__( 'Shipped at', 'dude-commerce' ); ?></span>
							<span><?php echo esc_html( self::format_datetime( $shipped_at ) ); ?></span>
						</div>
					<?php endif; ?>
					<?php if ( '' !== $picked_at ) : ?>
						<div class="wpdude-dc-order-fulfilil__row">
							<span class="wpdude-dc-order-fulfilil__key"><?php echo esc_html__( 'Picked at', 'dude-commerce' ); ?></span>
							<span><?php echo esc_html( self::format_datetime( $picked_at ) ); ?></span>
						</div>
					<?php endif; ?>
				</div>

				<div class="wpdude-dc-order-fulfilil__actions">
					<?php
					/**
					 * Print actions in the Fulfilment card (packing slip, shipping label).
					 *
					 * @param array<string, mixed> $order Order.
					 */
					do_action( 'wpdude_dc_order_fulfilment_print_actions', $order );
					?>

					<?php if ( Statuses::NEW === $status ) : ?>
						<?php if ( '' === $picked_at ) : ?>
							<form method="post">
								<?php wp_nonce_field( 'wpdude_dc_order_picked', 'wpdude_dc_order_picked_nonce' ); ?>
								<input type="hidden" name="wpdude_dc_order_uuid" value="<?php echo esc_attr( (string) $order['uuid'] ); ?>" />
								<button type="submit" name="wpdude_dc_mark_picked" value="1" class="button button-hero">
									<?php echo esc_html__( 'Mark as Picked', 'dude-commerce' ); ?>
								</button>
							</form>
						<?php endif; ?>
						<?php if ( $can_ship ) : ?>
							<form method="post">
								<?php wp_nonce_field( 'wpdude_dc_order_status', 'wpdude_dc_order_status_nonce' ); ?>
								<input type="hidden" name="wpdude_dc_order_uuid" value="<?php echo esc_attr( (string) $order['uuid'] ); ?>" />
								<button type="submit" class="button button-primary button-hero" name="wpdude_dc_order_new_status" value="<?php echo esc_attr( Statuses::SHIPPED ); ?>">
									<?php echo esc_html__( 'Mark as Shipped', 'dude-commerce' ); ?>
								</button>
							</form>
						<?php endif; ?>
					<?php elseif ( Statuses::SHIPPED === $status && $can_revert ) : ?>
						<form method="post">
							<?php wp_nonce_field( 'wpdude_dc_order_status', 'wpdude_dc_order_status_nonce' ); ?>
							<input type="hidden" name="wpdude_dc_order_uuid" value="<?php echo esc_attr( (string) $order['uuid'] ); ?>" />
							<button type="submit" class="button button-hero" name="wpdude_dc_order_new_status" value="<?php echo esc_attr( Statuses::NEW ); ?>">
								<?php echo esc_html( Statuses::action_label( Statuses::NEW ) ); ?>
							</button>
						</form>
					<?php endif; ?>
				</div>
			</div>

			<?php if ( $can_refund || $can_return ) : ?>
				<div class="wpdude-dc-order-card">
					<h2 class="wpdude-dc-order-card__title"><?php echo esc_html__( 'Refund', 'dude-commerce' ); ?></h2>
					<div class="wpdude-dc-order-fulfilil__actions">
						<?php if ( $can_return ) : ?>
							<form method="post">
								<?php wp_nonce_field( 'wpdude_dc_order_status', 'wpdude_dc_order_status_nonce' ); ?>
								<input type="hidden" name="wpdude_dc_order_uuid" value="<?php echo esc_attr( (string) $order['uuid'] ); ?>" />
								<button type="submit" class="button button-hero" name="wpdude_dc_order_new_status" value="<?php echo esc_attr( Statuses::RETURNED ); ?>">
									<?php echo esc_html__( 'Mark as Returned', 'dude-commerce' ); ?>
								</button>
							</form>
						<?php endif; ?>
						<?php if ( $can_refund ) : ?>
							<form method="post" onsubmit="return confirm('<?php echo esc_js( __( 'Refund this payment in Stripe? This cannot be undone.', 'dude-commerce' ) ); ?>');">
								<?php wp_nonce_field( 'wpdude_dc_order_refund', 'wpdude_dc_order_refund_nonce' ); ?>
								<input type="hidden" name="wpdude_dc_order_uuid" value="<?php echo esc_attr( (string) $order['uuid'] ); ?>" />
								<button type="submit" name="wpdude_dc_refund_stripe" value="1" class="button button-hero">
									<?php echo esc_html__( 'Refund in Stripe', 'dude-commerce' ); ?>
								</button>
							</form>
						<?php endif; ?>
					</div>
				</div>
			<?php endif; ?>

			<details class="wpdude-dc-order-detail__tech">
				<summary><?php echo esc_html__( 'Technical Details', 'dude-commerce' ); ?></summary>
				<table class="wpdude-dc-order-detail__tech-table">
					<tr>
						<th><?php echo esc_html__( 'Checkout Session', 'dude-commerce' ); ?></th>
						<td><code><?php echo esc_html( (string) ( $order['stripe_checkout_session_id'] ?? '' ) ); ?></code></td>
					</tr>
					<tr>
						<th><?php echo esc_html__( 'Payment Intent', 'dude-commerce' ); ?></th>
						<td>
							<?php if ( '' !== $pi_url ) : ?>
								<a href="<?php echo esc_url( $pi_url ); ?>" target="_blank" rel="noopener noreferrer"><code><?php echo esc_html( $pi ); ?></code></a>
							<?php else : ?>
								<code><?php echo esc_html( $pi ); ?></code>
							<?php endif; ?>
						</td>
					</tr>
					<tr>
						<th><?php echo esc_html__( 'Stripe Customer', 'dude-commerce' ); ?></th>
						<td><code><?php echo esc_html( (string) ( $order['stripe_customer_id'] ?? '' ) ); ?></code></td>
					</tr>
					<tr>
						<th><?php echo esc_html__( 'Radar risk', 'dude-commerce' ); ?></th>
						<td>
							<?php if ( '' !== $risk_level ) : ?>
								<?php echo wp_kses_post( self::risk_badge_html( $risk_level, $risk_score ) ); ?>
							<?php else : ?>
								—
							<?php endif; ?>
						</td>
					</tr>
					<tr>
						<th><?php echo esc_html__( 'Processing fee', 'dude-commerce' ); ?></th>
						<td>
							<?php
							echo null !== $fee_amount
								? esc_html( Money::format( $fee_amount, $fee_currency ) )
								: '—';
							?>
						</td>
					</tr>
					<tr>
						<th><?php echo esc_html__( 'Net', 'dude-commerce' ); ?></th>
						<td>
							<?php
							echo null !== $net_amount
								? esc_html( Money::format( $net_amount, $fee_currency ) )
								: '—';
							?>
						</td>
					</tr>
					<tr>
						<th><?php echo esc_html__( 'Order UUID', 'dude-commerce' ); ?></th>
						<td><code><?php echo esc_html( (string) ( $order['uuid'] ?? '' ) ); ?></code></td>
					</tr>
				</table>
			</details>

			<details class="wpdude-dc-order-detail__tech">
				<summary><?php echo esc_html__( 'Raw Order Data', 'dude-commerce' ); ?></summary>
				<pre class="wpdude-dc-order-detail__raw"><?php echo esc_html( (string) wp_json_encode( $order, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) ); ?></pre>
			</details>
		</div>
		<?php
	}

	/**
	 * Human error from redirect code.
	 */
	private static function error_message( string $code, string $detail = '' ): string {
		if ( 'refund' === $code && '' !== $detail ) {
			return $detail;
		}
		$map = array(
			'not_found'  => __( 'Order not found.', 'dude-commerce' ),
			'invalid'    => __( 'That status change is not allowed.', 'dude-commerce' ),
			'transition' => __( 'Could not update order status.', 'dude-commerce' ),
			'no_payment' => __( 'This order has no Stripe Payment Intent to refund.', 'dude-commerce' ),
			'refund'     => __( 'Could not refund this payment in Stripe.', 'dude-commerce' ),
		);
		return $map[ $code ] ?? __( 'Could not update order status.', 'dude-commerce' );
	}

	/**
	 * Fetch and persist Stripe Radar risk / processing fee when missing.
	 *
	 * @param array<string, mixed> $order Order.
	 * @return array<string, mixed>
	 */
	private static function ensure_stripe_charge_details( array $order ): array {
		$order_id = (int) ( $order['id'] ?? 0 );
		$has_risk = '' !== sanitize_key( (string) ( $order['stripe_risk_level'] ?? '' ) );
		$has_fee  = $order_id > 0 && metadata_exists( 'post', $order_id, Order_Repository::META_PREFIX . 'stripe_fee_amount' );
		if ( $has_risk && $has_fee ) {
			return $order;
		}

		$pi = (string) ( $order['stripe_payment_intent_id'] ?? '' );
		if ( '' === $pi ) {
			return $order;
		}

		$details = ( new Stripe_Provider() )->get_payment_charge_details( $pi );
		$update  = array();
		if ( ! $has_risk && '' !== $details['level'] ) {
			$update['stripe_risk_level'] = $details['level'];
			$order['stripe_risk_level']  = $details['level'];
			if ( null !== $details['score'] ) {
				$update['stripe_risk_score'] = (int) $details['score'];
				$order['stripe_risk_score']  = (int) $details['score'];
			}
		}
		if ( ! $has_fee && null !== $details['fee'] ) {
			$update['stripe_fee_amount'] = (int) $details['fee'];
			$order['stripe_fee_amount']  = (int) $details['fee'];
			if ( null !== $details['net'] ) {
				$update['stripe_net_amount'] = (int) $details['net'];
				$order['stripe_net_amount']  = (int) $details['net'];
			}
			if ( '' !== $details['currency'] ) {
				$update['stripe_fee_currency'] = $details['currency'];
				$order['stripe_fee_currency']  = $details['currency'];
			}
		}

		if ( ! empty( $update ) && $order_id > 0 ) {
			( new Order_Repository() )->update( $order_id, $update );
		}

		return $order;
	}

	/**
	 * Coloured Stripe Radar risk badge.
	 *
	 * @param bool $compact Short label for list tables (Normal / Elevated / Highest).
	 */
	private static function risk_badge_html( string $level, ?int $score = null, bool $compact = false ): string {
		$level = sanitize_key( $level );
		$allowed = array( 'normal', 'elevated', 'highest', 'not_assessed', 'unknown' );
		if ( ! in_array( $level, $allowed, true ) ) {
			return '';
		}

		$labels = $compact
			? array(
				'normal'       => __( 'Normal', 'dude-commerce' ),
				'elevated'     => __( 'Elevated', 'dude-commerce' ),
				'highest'      => __( 'Highest', 'dude-commerce' ),
				'not_assessed' => __( 'Not assessed', 'dude-commerce' ),
				'unknown'      => __( 'Unknown', 'dude-commerce' ),
			)
			: array(
				'normal'       => __( 'Risk: Normal', 'dude-commerce' ),
				'elevated'     => __( 'Risk: Elevated', 'dude-commerce' ),
				'highest'      => __( 'Risk: Highest', 'dude-commerce' ),
				'not_assessed' => __( 'Risk: Not assessed', 'dude-commerce' ),
				'unknown'      => __( 'Risk: Unknown', 'dude-commerce' ),
			);

		$label = $labels[ $level ];
		if ( null !== $score && ! $compact ) {
			$label = sprintf(
				/* translators: 1: risk level label, 2: numeric risk score */
				__( '%1$s (%2$d)', 'dude-commerce' ),
				$label,
				$score
			);
		}

		$title = __( 'Stripe Radar risk evaluation', 'dude-commerce' );
		if ( null !== $score ) {
			$title = sprintf(
				/* translators: %d: Stripe Radar risk score */
				__( 'Stripe Radar risk score: %d', 'dude-commerce' ),
				$score
			);
		}

		return sprintf(
			'<span class="wpdude-dc-risk wpdude-dc-risk--%1$s" title="%2$s">%3$s</span>',
			esc_attr( $level ),
			esc_attr( $title ),
			esc_html( $label )
		);
	}

	/**
	 * SKU from order snapshot, or live product/combination lookup.
	 *
	 * @param array<string, mixed> $order Order.
	 */
	private static function order_sku( array $order ): string {
		$line = is_array( $order['line_item'] ?? null ) ? $order['line_item'] : array();
		$sku  = isset( $line['sku'] ) ? Product_Meta::normalize_sku( (string) $line['sku'] ) : '';
		if ( '' !== $sku ) {
			return $sku;
		}
		return Stock_Service::line_sku(
			(int) ( $order['product_id'] ?? 0 ),
			(string) ( $order['combination_key'] ?? '' )
		);
	}

	/**
	 * Format a GMT MySQL datetime for admin display.
	 */
	private static function format_datetime( string $gmt_mysql ): string {
		$gmt_mysql = trim( $gmt_mysql );
		if ( '' === $gmt_mysql ) {
			return '';
		}

		$timestamp = strtotime( $gmt_mysql . ' UTC' );
		if ( false === $timestamp ) {
			return $gmt_mysql;
		}

		return (string) wp_date(
			get_option( 'date_format' ) . ' ' . get_option( 'time_format' ),
			$timestamp
		);
	}

	/**
	 * Plain-text delivery address with country names.
	 *
	 * @param array<string, mixed> $order Order.
	 */
	private static function format_delivery_address( array $order ): string {
		$lines = self::delivery_address_lines( $order );
		return ! empty( $lines ) ? implode( "\n", $lines ) : '—';
	}

	/**
	 * Delivery address lines for display / copy.
	 *
	 * @param array<string, mixed> $order Order.
	 * @return list<string>
	 */
	private static function delivery_address_lines( array $order ): array {
		$shipping = is_array( $order['shipping_address'] ?? null ) ? $order['shipping_address'] : array();
		$lines    = array();

		$name = trim( (string) ( $shipping['name'] ?? $order['customer_name'] ?? '' ) );
		if ( '' !== $name ) {
			$lines[] = $name;
		}

		$address = is_array( $shipping['address'] ?? null ) ? $shipping['address'] : $shipping;
		foreach ( array( 'line1', 'line2', 'city', 'state', 'postal_code' ) as $key ) {
			$val = trim( (string) ( $address[ $key ] ?? '' ) );
			if ( '' !== $val ) {
				$lines[] = $val;
			}
		}

		$country_code = strtoupper( trim( (string) ( $address['country'] ?? '' ) ) );
		if ( '' !== $country_code ) {
			$countries = Countries::all();
			$lines[]   = $countries[ $country_code ] ?? $country_code;
		}

		return $lines;
	}

	/**
	 * Human-readable product option lines (e.g. "Size: S").
	 *
	 * @param array<string, mixed> $order Order.
	 * @return list<string>
	 */
	private static function product_options_lines( array $order ): array {
		$key        = trim( (string) ( $order['combination_key'] ?? '' ) );
		$product_id = (int) ( $order['product_id'] ?? 0 );
		if ( '' === $key ) {
			return array();
		}

		if ( $product_id > 0 ) {
			foreach ( Product_Meta::get_combinations( $product_id ) as $combo ) {
				if ( (string) ( $combo['key'] ?? '' ) !== $key ) {
					continue;
				}
				$options = is_array( $combo['options'] ?? null ) ? $combo['options'] : array();
				if ( empty( $options ) ) {
					break;
				}
				$lines = array();
				foreach ( $options as $opt_name => $opt_value ) {
					$name  = trim( (string) $opt_name );
					$value = trim( (string) $opt_value );
					if ( '' === $name || '' === $value ) {
						continue;
					}
					$lines[] = $name . ': ' . $value;
				}
				if ( ! empty( $lines ) ) {
					return $lines;
				}
				break;
			}
		}

		$lines = array();
		foreach ( explode( '|', $key ) as $part ) {
			$part = trim( $part );
			if ( '' === $part || false === strpos( $part, '=' ) ) {
				continue;
			}
			$bits  = explode( '=', $part, 2 );
			$name  = trim( (string) ( $bits[0] ?? '' ) );
			$value = trim( (string) ( $bits[1] ?? '' ) );
			if ( '' === $name || '' === $value ) {
				continue;
			}
			$lines[] = ucwords( str_replace( array( '-', '_' ), ' ', strtolower( $name ) ) ) . ': ' . ucwords( str_replace( array( '-', '_' ), ' ', strtolower( $value ) ) );
		}

		return $lines;
	}

	/**
	 * Product image URL for the order detail card.
	 *
	 * @param array<string, mixed> $order Order.
	 */
	private static function product_image_url( array $order ): string {
		$line     = is_array( $order['line_item'] ?? null ) ? $order['line_item'] : array();
		$image_id = absint( $line['image_id'] ?? 0 );
		if ( $image_id > 0 ) {
			$url = wp_get_attachment_image_url( $image_id, 'medium' );
			if ( is_string( $url ) && '' !== $url ) {
				return $url;
			}
		}

		$product_id = (int) ( $order['product_id'] ?? 0 );
		if ( $product_id > 0 ) {
			$thumb_id = (int) get_post_thumbnail_id( $product_id );
			if ( $thumb_id > 0 ) {
				$url = wp_get_attachment_image_url( $thumb_id, 'medium' );
				if ( is_string( $url ) && '' !== $url ) {
					return $url;
				}
			}
		}

		return '';
	}

	/**
	 * Clean product title (no em-dash option suffix).
	 *
	 * @param array<string, mixed> $order Order.
	 */
	private static function clean_product_name( array $order ): string {
		$product_id = (int) ( $order['product_id'] ?? 0 );
		if ( $product_id > 0 ) {
			$title = get_the_title( $product_id );
			if ( is_string( $title ) && '' !== trim( $title ) ) {
				return $title;
			}
		}

		$line = is_array( $order['line_item'] ?? null ) ? $order['line_item'] : array();
		$name = trim( (string) ( $line['name'] ?? '' ) );
		if ( '' === $name ) {
			return __( '(deleted product)', 'dude-commerce' );
		}

		$em_pos = strpos( $name, '—' );
		if ( false !== $em_pos ) {
			$name = trim( substr( $name, 0, $em_pos ) );
		}

		return '' !== $name ? $name : __( '(deleted product)', 'dude-commerce' );
	}

	/**
	 * Money rows for the product card.
	 *
	 * @param array<string, mixed> $order Order.
	 * @return array{subtotal: int, shipping: int, tax: int, total: int, currency: string}
	 */
	private static function money_breakdown( array $order ): array {
		$currency = (string) ( $order['currency'] ?? 'usd' );
		$tax      = (int) ( $order['tax_amount'] ?? 0 );
		$shipping = (int) ( $order['shipping_amount'] ?? 0 );
		$total    = (int) ( $order['amount'] ?? 0 );
		$qty      = max( 1, (int) ( $order['qty'] ?? 1 ) );
		$line     = is_array( $order['line_item'] ?? null ) ? $order['line_item'] : array();

		if ( isset( $line['unit_amount'] ) && is_numeric( $line['unit_amount'] ) ) {
			$subtotal = (int) $line['unit_amount'] * $qty;
		} else {
			$subtotal = max( 0, $total - $tax - $shipping );
		}

		return array(
			'subtotal' => $subtotal,
			'shipping' => $shipping,
			'tax'      => $tax,
			'total'    => $total,
			'currency' => $currency,
		);
	}

	/**
	 * @param array<string, mixed> $shipping Shipping snapshot from Stripe.
	 */
	private static function format_shipping_address( array $shipping ): string {
		return self::format_delivery_address(
			array(
				'shipping_address' => $shipping,
			)
		);
	}

	/**
	 * @param array<string, mixed> $order Order.
	 */
	private static function customer_label( array $order ): string {
		$name  = trim( (string) ( $order['customer_name'] ?? '' ) );
		$email = trim( (string) ( $order['customer_email'] ?? '' ) );
		if ( '' !== $name && '' !== $email ) {
			return $name . ' <' . $email . '>';
		}
		return $name !== '' ? $name : ( $email !== '' ? $email : '—' );
	}

	/**
	 * @param array<string, mixed> $order Order.
	 */
	private static function product_label( array $order ): string {
		return self::clean_product_name( $order );
	}

	/**
	 * Product select options.
	 *
	 * @return array<int, string>
	 */
	private static function product_choices(): array {
		$ids = get_posts(
			array(
				'post_type'      => Product_Post_Type::POST_TYPE,
				'post_status'    => array( 'publish', 'draft', 'private' ),
				'posts_per_page' => 200,
				'orderby'        => 'title',
				'order'          => 'ASC',
				'fields'         => 'ids',
			)
		);
		$out = array();
		foreach ( $ids as $id ) {
			$out[ (int) $id ] = get_the_title( (int) $id );
		}
		return $out;
	}

	/**
	 * @return array{status: string, product_id: int, date_from: string, date_to: string}
	 */
	private static function filters_from_request(): array {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only admin list filters.
		$status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( (string) $_GET['status'] ) ) : '';
		if ( '' !== $status && ! in_array( $status, Statuses::all(), true ) ) {
			$status = '';
		}
		return array(
			'status'     => $status,
			'product_id' => isset( $_GET['product_id'] ) ? absint( $_GET['product_id'] ) : 0,
			'date_from'  => isset( $_GET['date_from'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['date_from'] ) ) : '',
			'date_to'    => isset( $_GET['date_to'] ) ? sanitize_text_field( wp_unslash( (string) $_GET['date_to'] ) ) : '',
		);
		// phpcs:enable WordPress.Security.NonceVerification.Recommended
	}
}
