<?php
/**
 * Payout Detail Drawer
 *
 * Renders the View-Payout drawer shell. The Alpine store and row-click
 * intercept live in `assets/js/affwp-payout-drawer.js` (enqueued via
 * `includes/scripts.php`). PHP-computed data (nonce, ajax URL, list URL)
 * is handed to JS via `wp_localize_script()`.
 *
 * Guarded so the markup emits only once per request.
 *
 * @package     AffiliateWP
 * @subpackage  Admin/Payouts
 * @copyright   Copyright (c) 2026, Sandhills Development, LLC
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       2.33.0
 */

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Render the payout detail drawer markup.
 *
 * @since 2.33.0
 */
function affwp_render_payout_drawer() {

	/*
	 * Render-once guard. The Alpine.store('payoutDrawer', {...}) registration
	 * is global by design — every <div x-data='Alpine.store("payoutDrawer")'>
	 * binds to the same singleton. If `affwp_render_payout_drawer()` runs
	 * twice in one request (multiple list-tables on the same page, or an
	 * admin notice doing weird hook chains), the second markup block clobbers
	 * the first store's DOM bindings and Alpine throws on re-registration.
	 * The static keeps render exactly-once per request lifecycle.
	 *
	 * If a future screen genuinely needs two drawer instances side-by-side
	 * (e.g. a "compare payouts" view), refactor the store to accept an
	 * instance ID — drop this guard, parameterize the alpine_store value
	 * below, and update affwp-payout-drawer.js to register multiple stores
	 * keyed by ID. Until then, prefer reusing the singleton.
	 */
	static $rendered = false;

	if ( $rendered ) {
		return;
	}

	$rendered = true;

	affwp_drawer( [
		'id'                   => 'payout-drawer',
		'alpine_store'         => 'payoutDrawer',
		'width'                => 'xl',
		'header_style'         => 'flat',
		'close_on_backdrop'    => true,
		'close_on_escape'      => true,
		/*
		 * Show the drawer trait's built-in loading overlay while the AJAX
		 * payload resolves — but only on first-open (no payout yet).
		 * In-drawer navigation (↑/↓ keys, chevrons) keeps the existing
		 * content visible and relies on the body dim instead, so the
		 * full overlay doesn't flash between every step.
		 */
		'loading_x_show'       => '$store.payoutDrawer.isLoading && ! $store.payoutDrawer.payout',
		/*
		 * Status pill inline with "Payout #N" in the title row. See the
		 * batch drawer for the matching treatment.
		 */
		'title_badge_callback' => function () {
			?>
			<template x-if="$store.payoutDrawer.payout">
				<span :class="$store.payoutDrawer.badgeClass( $store.payoutDrawer.badgeVariantForPayoutStatus( $store.payoutDrawer.status ) )"
					x-text="$store.payoutDrawer.payout.status_label"></span>
			</template>
			<?php
		},
		'header_callback'   => function () {
			require __DIR__ . '/views/payout-drawer-header.php';
		},
		'content_callback'  => function () {
			?>
			<!--
				Alias the payoutDrawer store as `drawer` so card partials can
				reference state by bare name (e.g. `drawer.payout`) instead of
				the longer `$store.payoutDrawer.payout`. Getter-based to preserve
				Alpine's reactivity.
			-->
			<div x-data="{ get drawer() { return $store.payoutDrawer; } }"
				class="space-y-5 transition-opacity duration-200"
				:class="{ 'opacity-60': drawer.isNavigating }">

				<!--
					Skeleton placeholder for the first-open loading window.
					The drawer body partials all gate on `drawer.payout`, so
					during the ~50-200ms between open and AJAX landing the
					body was empty — looked like an error. This shows
					neutral shimmer blocks instead. Hidden as soon as
					payout data arrives (or on in-drawer keyboard nav,
					where the body dim already covers the swap).
				-->
				<template x-if="! drawer.payout && ! drawer.isNavigating">
					<div class="space-y-5" aria-hidden="true">
						<div class="affwp-card rounded-lg border border-gray-200 bg-white px-5 py-4 space-y-3">
							<div class="h-3 w-32 rounded bg-gray-100 animate-pulse"></div>
							<div class="h-3 w-full rounded bg-gray-100 animate-pulse"></div>
							<div class="h-3 w-3/4 rounded bg-gray-100 animate-pulse"></div>
						</div>
						<div class="affwp-card rounded-lg border border-gray-200 bg-white px-5 py-4 space-y-3">
							<div class="h-3 w-24 rounded bg-gray-100 animate-pulse"></div>
							<div class="h-3 w-full rounded bg-gray-100 animate-pulse"></div>
						</div>
					</div>
				</template>

				<?php
				/*
				 * Supersession card slots ABOVE everything else on
				 * superseded payouts so the resolution ("this was
				 * replaced, here's the active row") leads the read.
				 * Self-gated on isSuperseded; renders nothing on
				 * non-superseded payouts.
				 */
				require __DIR__ . '/views/payout-drawer-supersession.php';
				require __DIR__ . '/views/payout-drawer-summary.php';
				/*
				 * Next Steps card slots between the method-summary card
				 * and the referrals section: "why it failed" (summary)
				 * → "what to do about it" (next steps) → "which referrals
				 * are affected" (referrals). Renders only on failed
				 * payouts with a classified failure (see the x-if guard
				 * in the view).
				 */
				require __DIR__ . '/views/payout-drawer-next-steps.php';
				require __DIR__ . '/views/payout-drawer-referrals.php';
				require __DIR__ . '/views/payout-drawer-details.php';
				?>
			</div>
			<?php
		},
		/*
		 * Footer removed in 2.33.0. Every actionable affordance now lives
		 * inside body cards (Next Steps primary CTA / Resend / Switch
		 * payout method) and the drawer is closable via Escape, the X
		 * button in the header, and outside-click on the backdrop. A
		 * footer with just a Close button restated affordances the
		 * drawer chrome already provided. `no_footer` tells the trait to
		 * skip the footer region entirely so the body scroll extends to
		 * the bottom edge of the panel.
		 */
		'no_footer'         => true,
	] );

	// Toasts are safe to call multiple times (self-guarded).
	affwp_toast();
}
