/**
 * Paginated row loader for the Preview Payout screen. Sections cap at 5 rows and
 * load more on demand via a delegated click listener.
 *
 * @since 2.33.0
 */
( function () {
	'use strict';

	const data = window.affwpPreviewPayoutData || null;
	if ( ! data || ! data.ajaxUrl || ! data.nonce ) {
		return;
	}

	/**
	 * Serializes the load-more request body. The filter snapshot was localized at
	 * page-render time so the AJAX endpoint sees the exact same args the screen
	 * used — keeps row ordering stable across batches without server-side state.
	 *
	 * @param {HTMLButtonElement} button The button whose data attrs describe this batch.
	 * @returns {URLSearchParams}
	 */
	function buildBody( button ) {
		const body = new URLSearchParams();
		body.append( 'action', 'affwp_preview_payout_load_more' );
		body.append( 'nonce', data.nonce );

		body.append( 'section', button.dataset.section || '' );
		if ( button.dataset.methodKey ) {
			body.append( 'method_key', button.dataset.methodKey );
		}
		body.append( 'offset', button.dataset.offset || '0' );
		body.append( 'limit', button.dataset.batch || '100' );

		const filters = data.filters || {};

		( filters.include_affiliates || [] ).forEach( function ( id ) {
			body.append( 'include_affiliates[]', id );
		} );
		( filters.include_groups || [] ).forEach( function ( id ) {
			body.append( 'include_groups[]', id );
		} );
		( filters.payout_methods || [] ).forEach( function ( m ) {
			body.append( 'payout_methods[]', m );
		} );

		if ( filters.from ) body.append( 'from', filters.from );
		if ( filters.to ) body.append( 'to', filters.to );
		if ( filters.minimum ) body.append( 'minimum', filters.minimum );
		if ( filters.bypass_holding ) body.append( 'bypass_holding', '1' );
		if ( filters.payout_method ) body.append( 'payout_method', filters.payout_method );

		return body;
	}

	/**
	 * Updates the button's label after a batch lands. When `remaining` hits zero
	 * the button is removed from the DOM — the section just has the rows below
	 * the load-more strip vanish, which reads naturally as "you've reached the end."
	 */
	function updateButton( button, remaining ) {
		if ( remaining <= 0 ) {
			button.remove();
			return;
		}

		const labelEl = button.querySelector( '[data-affwp-load-more-label]' );
		if ( ! labelEl ) {
			return;
		}

		labelEl.textContent = buildLoadMoreLabel( remaining, parseInt( button.dataset.batch || '100', 10 ) );
	}

	/**
	 * Build the load-more button label for a given remaining/batch state.
	 *
	 * Two shapes, both phrased as "more":
	 *
	 *   - More than one batch left: "Show {batch} more · {remaining} hidden"
	 *     The action count is what THIS click reveals; the hidden count is the
	 *     state right now (so the same number stays put after the click — it
	 *     re-renders to a smaller value on the next call).
	 *
	 *   - Final click (remaining ≤ batch): "Show {N} more" — the dropped
	 *     "· hidden" suffix is the cue that this click reveals everything left.
	 */
	function buildLoadMoreLabel( remaining, batch ) {
		const strings = data.strings || {};

		if ( remaining > batch ) {
			const template = strings.showMoreWithHidden || 'Show %1$d more · %2$d hidden';
			return template
				.replace( '%1$d', String( batch ) )
				.replace( '%2$d', String( remaining ) );
		}

		const template = strings.showMore || 'Show %d more';
		return template.replace( '%d', String( remaining ) );
	}

	/**
	 * Toggles the visible label vs. the "Loading…" span. Disabling the button
	 * blocks double-clicks while the request is in flight.
	 */
	function setBusy( button, isBusy ) {
		const labelEl = button.querySelector( '[data-affwp-load-more-label]' );
		const busyEl  = button.querySelector( '[data-affwp-load-more-busy]' );

		button.disabled = isBusy;
		button.setAttribute( 'aria-busy', isBusy ? 'true' : 'false' );

		if ( labelEl ) labelEl.hidden = isBusy;
		if ( busyEl )  busyEl.hidden  = ! isBusy;
	}

	/**
	 * Resolves the section's row container — the AJAX handler returns plain HTML
	 * that we splice into `[data-affwp-preview-rows]`. We walk up from the button
	 * to its `[role=table]` ancestor and pick the rowgroup inside, which keeps
	 * the lookup local to the section even if the document has multiple sections.
	 */
	function findRowsContainer( button ) {
		const table = button.closest( '[role="table"]' );
		if ( ! table ) {
			return null;
		}
		return table.querySelector( '[data-affwp-preview-rows]' );
	}

	function handleClick( button ) {
		if ( button.disabled ) {
			return;
		}

		const container = findRowsContainer( button );
		if ( ! container ) {
			return;
		}

		setBusy( button, true );

		fetch( data.ajaxUrl, {
			method:      'POST',
			credentials: 'same-origin',
			headers:     { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
			body:        buildBody( button ).toString(),
		} )
			.then( function ( response ) {
				if ( ! response.ok ) {
					throw new Error( 'HTTP ' + response.status );
				}
				return response.json();
			} )
			.then( function ( payload ) {
				if ( ! payload || ! payload.success || ! payload.data ) {
					throw new Error( 'Bad payload' );
				}

				// insertAdjacentHTML preserves listeners on existing rows and avoids the full reparse of `innerHTML += ...`.
				container.insertAdjacentHTML( 'beforeend', payload.data.html || '' );

				const batch        = parseInt( button.dataset.batch || '100', 10 );
				const newOffset    = parseInt( button.dataset.offset || '0', 10 ) + batch;
				const newRemaining = parseInt( payload.data.remaining || '0', 10 );

				button.dataset.offset    = String( newOffset );
				button.dataset.remaining = String( newRemaining );

				setBusy( button, false );
				updateButton( button, newRemaining );
			} )
			.catch( function () {
				setBusy( button, false );
				// Re-render the label as-is; the user can retry the click.
				const labelEl = button.querySelector( '[data-affwp-load-more-label]' );
				if ( labelEl && data.strings && data.strings.loadError ) {
					labelEl.textContent = data.strings.loadError;
				}
			} );
	}

	document.addEventListener( 'click', function ( e ) {
		const button = e.target.closest && e.target.closest( '.affwp-preview-load-more' );
		if ( ! button ) {
			return;
		}
		e.preventDefault();
		handleClick( button );
	} );
} )();
