<?php
/**
 * Admin: Payout Schedules Subpage
 *
 * Renders the Payout Schedules tab of the Payouts page — the management surface
 * for Automatic Payouts. Each row is a recurring rule (who + minimum + cadence)
 * that, on its run, produces a payout batch (visible under the Batches tab). A
 * schedule has no payout method: each affiliate is routed through their own
 * method, so one run can produce a single mixed-method batch.
 *
 * The list is a shared List_Table (see class-list-table-schedules.php) so it
 * matches the Payouts and Batches tables. Create/edit happens in the shared
 * slide-over drawer (schedule-drawer.php): "Add Schedule" / a whole-row click /
 * an "Edit" action open it; "Save as schedule" on the payout preview seeds it.
 *
 * @package     AffiliateWP
 * @subpackage  Admin/Payouts
 * @copyright   Copyright (c) 2026, Awesome Motive, Inc
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       2.35.0
 */

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

/*
 * The drawer mirrors ?edit_schedule=X into the URL. WP_List_Table builds
 * pagination + sortable-column links from $_SERVER['REQUEST_URI'], so that param
 * would otherwise leak into every link and re-open the drawer on each nav. Strip
 * it before the table renders; the drawer reads it client-side on load.
 */
if ( ! empty( $_SERVER['REQUEST_URI'] ) && isset( $_GET['edit_schedule'] ) ) {
	$_SERVER['REQUEST_URI'] = remove_query_arg( 'edit_schedule', $_SERVER['REQUEST_URI'] );
}

$schedules_table = new AffWP_Payout_Schedules_Table();
$schedules_table->prepare_items();

$is_filtered_view = ! empty( $_GET['status'] ) || ! empty( $_REQUEST['s'] );

// Routed through the overview-counts helper so DevTools can force the empty state (same pattern as batches.php).
$counts           = affwp_payouts_overview_counts( array( 'schedules' => (int) ( $schedules_table->total_count ?? 0 ) ) );
$show_empty_state = (int) $counts['schedules'] < 1 && ! $is_filtered_view;

// Non-Pro plans get inline "Pro" pills on the schedule triggers, so the gate is visible before the click (the drawer carries the full upsell).
$schedules_can_access = ! function_exists( 'affwp_can_access_pro_features' ) || affwp_can_access_pro_features();

/**
 * Empty-state preview card: two sample schedule rows.
 *
 * @since 2.35.0
 */
function affwp_payout_schedules_render_ghost_rows_card() {

	/*
	 * Sample rows previewing what a schedule looks like. The name is the bare
	 * audience ("VIP", "All affiliates"); the columns beside it carry the cadence
	 * and the next run, so the name never restates them. The third column shows a
	 * forward "Next run" date (computed live from the sample cadence), not a
	 * status badge: a never-configured schedule has no live state, and a green
	 * "Active" pill on a mockup reads as "you're already paying people." A future
	 * date instead reinforces the promise — paid on this date, hands-off.
	 */
	$date_format = get_option( 'date_format' );
	$next_friday = affwp_calculate_next_run( 'weekly', 5 );  // 5 = Friday (0=Sun … 6=Sat).
	$next_first  = affwp_calculate_next_run( 'monthly', 1 ); // 1st of the month.

	$rows = array(
		array(
			'name'     => __( 'VIP', 'affiliate-wp' ),
			'cadence'  => __( 'Weekly on Friday', 'affiliate-wp' ),
			'next_run' => $next_friday ? wp_date( $date_format, strtotime( $next_friday ) ) : '',
		),
		array(
			'name'     => __( 'All affiliates', 'affiliate-wp' ),
			'cadence'  => __( 'Monthly on the 1st', 'affiliate-wp' ),
			'next_run' => $next_first ? wp_date( $date_format, strtotime( $next_first ) ) : '',
		),
	);

	$last = count( $rows ) - 1;
	?>
	<?php // Faded, shadowless, dissolving surface so the realistic rows read as a mockup, not schedules the admin already has. ?>
	<div class="mx-auto max-w-xl mt-8 rounded-2xl bg-white opacity-75 text-left overflow-hidden mask-[linear-gradient(to_right,black_70%,transparent_100%)]" aria-hidden="true">
		<?php foreach ( $rows as $i => $row ) : ?>
			<div class="flex items-center gap-4 px-5 py-4 <?php echo $i === $last ? '' : 'border-b border-gray-100'; ?>">
				<?php // Fixed-width columns so name / cadence / next-run line up across rows; left-clustered so the last cell clears the card's right-edge fade. ?>
				<p class="w-32 shrink-0 truncate font-semibold text-gray-900 m-0"><?php echo esc_html( $row['name'] ); ?></p>
				<p class="w-32 shrink-0 text-gray-500 m-0 tabular-nums whitespace-nowrap"><?php echo esc_html( $row['cadence'] ); ?></p>
				<?php if ( '' !== $row['next_run'] ) : ?>
					<span class="shrink-0 text-gray-500 tabular-nums whitespace-nowrap">
						<?php
						printf(
							/* translators: %s: a date, e.g. "Jul 17, 2026" — when the sample schedule would next run. */
							esc_html__( 'Next run %s', 'affiliate-wp' ),
							'<span class="text-gray-700">' . esc_html( $row['next_run'] ) . '</span>'
						);
						?>
					</span>
				<?php endif; ?>
			</div>
		<?php endforeach; ?>
	</div>
	<?php
}
?>
<div class="wrap">

	<h1>
		<?php esc_html_e( 'Payouts', 'affiliate-wp' ); ?>
		<?php echo affwp_admin_link( 'reports', _x( 'Reports', 'payouts', 'affiliate-wp' ), [ 'tab' => 'payouts' ], [ 'class' => 'page-title-action' ] ); ?>
		<?php if ( current_user_can( 'manage_payouts' ) ) : ?>
			<a href="<?php echo esc_url( affwp_admin_url( 'payouts', [ 'action' => 'new_payout' ] ) ); ?>" class="page-title-action"><?php esc_html_e( 'Pay Affiliates', 'affiliate-wp' ); ?></a>
			<?php /* While the empty state is showing it owns the primary CTA; a header button would double it up. Show the header action only once at least one schedule exists. */ ?>
			<?php if ( ! $show_empty_state ) : ?>
				<button type="button" x-data @click="$store.scheduleDrawer.openNew()" class="page-title-action affwp-page-title-primary"><?php esc_html_e( 'Add Schedule', 'affiliate-wp' ); ?><?php echo $schedules_can_access ? '' : ' ' . affwp_get_pro_badge( array( 'variant' => 'on-fill', 'icon' => '', 'class' => 'text-[11px] uppercase tracking-wide' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- badge helper returns trusted HTML. ?></button>
			<?php endif; ?>
		<?php endif; ?>
	</h1>

	<?php affwp_payouts_render_tabs( 'schedules' ); ?>

	<?php
	/*
	 * Action results surface as a toast (matches the payout batch flow), not a WP
	 * admin notice. The toast container is rendered with the drawer below. We fire
	 * on the affwp_notice param, then scrub it from the URL so a refresh doesn't
	 * re-toast.
	 */
	$affwp_notice = isset( $_GET['affwp_notice'] ) ? sanitize_key( $_GET['affwp_notice'] ) : '';
	$toast_map    = array(
		'schedule_created'     => array( 'success', __( 'Schedule created.', 'affiliate-wp' ) ),
		'schedule_updated'     => array( 'success', __( 'Schedule updated.', 'affiliate-wp' ) ),
		'schedule_paused'      => array( 'success', __( 'Schedule paused.', 'affiliate-wp' ) ),
		'schedule_resumed'     => array( 'success', __( 'Schedule resumed.', 'affiliate-wp' ) ),
		'schedule_ran'         => array( 'success', __( 'Schedule run complete. Check the Payout Batches tab for the result.', 'affiliate-wp' ) ),
		'schedule_deleted'     => array( 'success', __( 'Schedule deleted.', 'affiliate-wp' ) ),
		'schedule_save_failed' => array( 'error', __( 'Could not save the schedule. Please give it a name and try again.', 'affiliate-wp' ) ),
	);
	if ( isset( $toast_map[ $affwp_notice ] ) ) :
		?>
		<script>
			( function () {
				var fire = function () {
					if ( ! window.affwpToast ) {
						return;
					}
					window.affwpToast( {
						message: <?php echo wp_json_encode( $toast_map[ $affwp_notice ][1] ); ?>,
						type: <?php echo wp_json_encode( $toast_map[ $affwp_notice ][0] ); ?>
					} );
					// Scrub the notice from the URL so a refresh doesn't re-toast.
					try {
						var url = new URL( window.location.href );
						url.searchParams.delete( 'affwp_notice' );
						window.history.replaceState( {}, '', url.toString() );
					} catch ( e ) { /* History API unavailable — leave the URL alone. */ }
				};
				if ( document.readyState === 'loading' ) {
					document.addEventListener( 'DOMContentLoaded', fire );
				} else {
					fire();
				}
			} )();
		</script>
		<?php
	endif;
	?>

	<?php if ( $show_empty_state ) : ?>

		<div class="affwp-ui min-h-[60vh] flex items-center justify-center">
			<div class="w-full">
				<?php
				affwp_list_page_empty_state(
					array(
						// Copy mirrors the drawer's upsell card, so the feature reads the same before and after the click.
						'title'        => __( 'Pay affiliates automatically', 'affiliate-wp' ),
						'title_class'  => 'text-4xl font-semibold text-gray-900 m-0 tracking-tight',
						'description'  => __( 'Set a schedule once and every affiliate is paid on time, every time, each through their own payout method. Create one below, or save any Pay Affiliates payout as a recurring schedule.', 'affiliate-wp' ),
						'content'      => 'affwp_payout_schedules_render_ghost_rows_card',
						'button_text'  => current_user_can( 'manage_payouts' ) ? __( 'Add Schedule', 'affiliate-wp' ) : '',
						'button_url'   => current_user_can( 'manage_payouts' ) ? affwp_admin_url( 'payouts', array( 'tab' => 'schedules', 'new_schedule' => 1 ) ) : '',
						'button_badge' => ( current_user_can( 'manage_payouts' ) && ! $schedules_can_access ) ? affwp_get_pro_badge( array( 'variant' => 'on-fill', 'icon' => '', 'class' => 'ml-2 text-[11px] uppercase tracking-wide' ) ) : '',
						/*
						 * Activation nudge for Pro plans only: they never see the gate
						 * card, so the recurrence stat duplicates nothing here. Gated
						 * plans read the same line on the card instead — showing it in
						 * both beats turns a personalized insight into a sales script.
						 */
						'button_subtitle' => ( current_user_can( 'manage_payouts' ) && $schedules_can_access ) ? affwp_get_manual_payout_run_days_line() : '',
						'button_subtitle_class' => 'mt-2.5 text-sm font-semibold text-gray-900',
					)
				);
				?>
			</div>
		</div>

	<?php else : ?>

		<?php
		/*
		 * Downgrade notice: a site with existing schedules but no Pro access got
		 * here by lapsing/downgrading from Pro. Those schedules no longer run (the
		 * processor skips them), so tell the admin plainly rather than let payouts
		 * silently stop. Only when there are schedules to run (the list branch).
		 */
		if ( ! $schedules_can_access && function_exists( 'affwp_render_notice' ) ) {
			$upgrade_link = function_exists( 'affwp_admin_upgrade_link' )
				? affwp_admin_upgrade_link( 'payout-schedules', 'gated-schedules-notice' )
				: 'https://affiliatewp.com/pricing/';
			echo '<div class="affwp-ui mb-4">' . affwp_render_notice( array( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- component returns pre-escaped HTML.
				// White card, warning-tinted icon: calm surface, but the stopped-schedules condition still signals (not the aspirational sell used on the empty state).
				'variant' => 'warning-soft',
				'heading' => esc_html__( 'Your payout schedules aren\'t running', 'affiliate-wp' ),
				'body'    => esc_html__( 'Automatic payouts require an AffiliateWP Pro license. Your existing schedules will not run until you upgrade. You can still pay affiliates manually from the Payouts tab.', 'affiliate-wp' ),
				'actions' => affwp_render_button( array(
					'text'       => __( 'Upgrade to Pro', 'affiliate-wp' ),
					'variant'    => 'primary',
					'size'       => 'sm',
					'href'       => $upgrade_link,
					'attributes' => array( 'target' => '_blank', 'rel' => 'noopener' ),
				) ),
			) ) . '</div>';
		}
		?>

		<form id="affwp-payout-schedules-filter" method="get" action="<?php echo esc_url( affwp_admin_url( 'payouts', [ 'tab' => 'schedules' ] ) ); ?>">
			<input type="hidden" name="page" value="affiliate-wp-payouts" />
			<input type="hidden" name="tab" value="schedules" />

			<?php $schedules_table->search_box( __( 'Search schedules', 'affiliate-wp' ), 'affwp-schedules' ); ?>

			<?php $schedules_table->views(); ?>
			<?php $schedules_table->display(); ?>
		</form>

	<?php endif; ?>

</div>

<?php
// Create/edit slide-over (auto-opens from ?edit_schedule=N or ?new_schedule=1).
affwp_render_schedule_drawer();
