/**
 * CacheLoop Admin JavaScript
 *
 * Handles:
 *  - "Clear Cache" AJAX action with loading state
 *  - "Run DB Optimizer" AJAX action (Pro)
 *  - Pro toggle interception (shows upgrade prompt for free users)
 *  - Toast notification display
 *
 * clAdminData is injected by wp_localize_script() in CACHELOOP_Admin::cl_enqueue_assets().
 *
 * @package CacheLoop
 */

/* global clAdminData, jQuery */
( function ( $ ) {
	'use strict';

	// =========================================================
	// Expandable cards: extra fields (host/port, safelist, etc.)
	// stay collapsed until the card's own toggle is switched on —
	// keeps the settings list scannable instead of showing every
	// input for a feature the user hasn't even enabled yet.
	// =========================================================

	$( '.cl-card--expandable .cl-toggle input[type="checkbox"]' ).each(
		function () {
			var $extra = $( this ).closest( '.cl-card' ).find( '.cl-card__extra' );
			if ( ! $extra.length ) {
				return;
			}
			// Reflect the checkbox's real (possibly browser-restored) state
			// on load, since the PHP-rendered inline display:none only knows
			// the value saved server-side.
			this.checked ? $extra.show() : $extra.hide();
		}
	);

	$( document ).on(
		'change',
		'.cl-card--expandable .cl-toggle input[type="checkbox"]',
		function () {
			var $extra = $( this ).closest( '.cl-card' ).find( '.cl-card__extra' );
			if ( ! $extra.length ) {
				return;
			}
			if ( this.checked ) {
				$extra.slideDown( 150 );
			} else {
				$extra.slideUp( 150 );
			}
		}
	);

	// =========================================================
	// Toast helper
	// =========================================================

	/**
	 * Shows a toast notification.
	 *
	 * @param {string} message     The text to display.
	 * @param {string} [type='']   Optional type: 'success' | 'error'.
	 * @param {number} [duration]  Auto-hide delay in ms (default 3000).
	 */
	function clShowToast( message, type, duration ) {
		var $toast    = $( '#cl-toast' );
		var hideDelay = duration || 3000;

		// Reset state.
		$toast
			.removeClass( 'cl-toast--visible cl-toast--success cl-toast--error' )
			.text( message );

		if ( type ) {
			$toast.addClass( 'cl-toast--' + type );
		}

		// Force reflow to restart the transition.
		$toast[0].offsetHeight; // eslint-disable-line no-unused-expressions

		$toast.addClass( 'cl-toast--visible' );

		clearTimeout( $toast.data( 'timeout' ) );
		var timeoutId = setTimeout(
			function () {
				$toast.removeClass( 'cl-toast--visible' );
			},
			hideDelay
		);
		$toast.data( 'timeout', timeoutId );
	}

	// =========================================================
	// Button loading state helpers
	// =========================================================

	/**
	 * Puts a button into a loading state.
	 *
	 * @param {jQuery} $btn     The button element.
	 * @param {string} label    The label to show while loading.
	 */
	function clSetBtnLoading( $btn, label ) {
		$btn
			.prop( 'disabled', true )
			.data( 'original-text', $btn.html() )
			.html( '<span class="cl-spinner" aria-hidden="true"></span>' + label );
	}

	/**
	 * Restores a button to its original state.
	 *
	 * @param {jQuery} $btn The button element.
	 */
	function clResetBtn( $btn ) {
		$btn
			.prop( 'disabled', false )
			.html( $btn.data( 'original-text' ) );
	}

	// =========================================================
	// Clear Cache
	// =========================================================

	$( '#cl-clear-cache-btn' ).on(
		'click',
		function () {
			var $btn = $( this );

			clSetBtnLoading( $btn, clAdminData.i18n.clearing );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action: 'cacheloop_clear_cache',
						nonce:  clAdminData.nonceClearCache,
					},
					success: function ( response ) {
						clResetBtn( $btn );
						if ( response.success ) {
							clShowToast( clAdminData.i18n.cleared, 'success' );
						} else {
							clShowToast( clAdminData.i18n.error, 'error' );
						}
					},
					error: function () {
						clResetBtn( $btn );
						clShowToast( clAdminData.i18n.error, 'error' );
					},
				}
			);
		}
	);

	// =========================================================
	// Run DB Optimizer (Pro)
	// =========================================================

	$( '#cl-run-db-optimizer-btn' ).on(
		'click',
		function () {
			var $btn = $( this );

			clSetBtnLoading( $btn, clAdminData.i18n.optimizing );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action: 'cacheloop_run_db_optimizer',
						nonce:  clAdminData.nonceDbOptimizer,
					},
					success: function ( response ) {
						clResetBtn( $btn );
						if ( response.success ) {
							clShowToast( response.data.message || clAdminData.i18n.optimized, 'success' );
						} else {
							clShowToast( response.data.message || clAdminData.i18n.error, 'error' );
						}
					},
					error: function () {
						clResetBtn( $btn );
						clShowToast( clAdminData.i18n.error, 'error' );
					},
				}
			);
		}
	);

	// =========================================================
	// Image Optimization: Optimize All (Pro)
	// =========================================================

	var clImgOptimizePollTimer = null;

	function clStartImageOptimizePolling() {
		clearInterval( clImgOptimizePollTimer );
		clImgOptimizePollTimer = setInterval( clPollImageOptimizeStatus, 3000 );
	}

	function clPollImageOptimizeStatus() {
		$.ajax(
			{
				url:    clAdminData.ajaxUrl,
				method: 'POST',
				data: {
					action: 'cacheloop_get_image_optimize_status',
					nonce:  clAdminData.nonceDbOptimizer,
				},
				success: function ( response ) {
					if ( ! response.success ) {
						return;
					}
					var d = response.data;

					if ( ! d.running ) {
						clearInterval( clImgOptimizePollTimer );
						$( '#cl-image-optimize-progress-wrap' ).hide();
						$( '#cl-image-optimize-all-btn' ).prop( 'disabled', false );
						if ( typeof clLoadImageOptimizeStats === 'function' ) {
							clLoadImageOptimizeStats(); // Refresh counters now that the bulk run finished.
						}
						return;
					}

					// Still running: make sure the poll loop is armed (covers the
					// page-load resume case, where nothing started it yet) and
					// reflect current progress.
					if ( ! clImgOptimizePollTimer ) {
						clStartImageOptimizePolling();
					}
					$( '#cl-image-optimize-all-btn' ).prop( 'disabled', true );
					$( '#cl-image-optimize-progress-wrap' ).show();
					$( '#cl-image-optimize-progress-bar' ).css( 'width', d.percent + '%' );
					$( '#cl-image-optimize-status' ).text( d.completed + ' / ' + d.total + ' (' + d.percent + '%)' );
				},
			}
		);
	}

	function clEstimateOptimizeTime( count ) {
		// Bulk batch runner: 3 images per wp-cron tick, ticks every 60s
		// (see class-cl-image-optimizer__premium_only.php add_cron_schedule).
		var minutes = Math.ceil( count / 3 );
		if ( minutes < 60 ) {
			return minutes + ' minute' + ( 1 === minutes ? '' : 's' );
		}
		var hours = Math.ceil( minutes / 60 );
		if ( hours < 24 ) {
			return '~' + hours + ' hour' + ( 1 === hours ? '' : 's' );
		}
		var days = Math.ceil( hours / 24 );
		return '~' + days + ' day' + ( 1 === days ? '' : 's' );
	}

	function clStartBulkOptimize( $btn ) {
		$btn.prop( 'disabled', true );
		$( '#cl-image-optimize-status' ).text( 'Starting…' );

		$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action: 'cacheloop_start_image_optimize_all',
						nonce:  clAdminData.nonceDbOptimizer,
					},
					success: function ( response ) {
						if ( ! response.success ) {
							$( '#cl-image-optimize-status' ).text( response.data.message || clAdminData.i18n.error );
							$btn.prop( 'disabled', false );
							return;
						}

						if ( 0 === response.data.total ) {
							$( '#cl-image-optimize-status' ).text( response.data.message );
							$btn.prop( 'disabled', false );
							return;
						}

						$( '#cl-image-optimize-progress-wrap' ).show();
						$( '#cl-image-optimize-status' ).text( response.data.message );
						clStartImageOptimizePolling();
					},
					error: function () {
						$( '#cl-image-optimize-status' ).text( clAdminData.i18n.error );
						$btn.prop( 'disabled', false );
					},
				}
			);
	}

	$( '#cl-image-optimize-all-btn' ).on(
		'click',
		function () {
			var $btn        = $( this );
			var unoptimized = parseInt( $( '#cl-io-stat-unoptimized' ).text(), 10 ) || 0;

			if ( 0 === unoptimized ) {
				clStartBulkOptimize( $btn );
				return;
			}

			var timeEstimate  = clEstimateOptimizeTime( unoptimized );
			var originalLabel = $btn.text();
			$btn.prop( 'disabled', true ).text( 'Calculating…' );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action: 'cacheloop_estimate_disk_space',
						nonce:  clAdminData.nonceDbOptimizer,
					},
					success: function ( response ) {
						$btn.prop( 'disabled', false ).text( originalLabel );

						var diskLine = '';
						if ( response.success && ! response.data.nothing_to_do ) {
							var d = response.data;
							diskLine = '\n\nEstimated extra disk space needed: ~' + clFormatBytes( d.estimated_added_bytes )
								+ ' (originals are kept — WebP copies are added alongside them, they don\'t replace them).'
								+ ( -1 !== d.free_bytes ? '\nFree disk space now: ' + clFormatBytes( d.free_bytes ) : '' )
								+ ( 'measured' === d.ratio_source ? '\n(Based on this site\'s own optimization results so far.)' : '\n(Rough estimate — no optimized images on this site yet to measure from.)' );

							if ( ! d.sufficient ) {
								diskLine += '\n\n⚠️ This may not leave enough free disk space. Consider optimizing in smaller batches or freeing up space first.';
							}
						}

						var msg = 'Optimize ' + unoptimized + ' image' + ( 1 === unoptimized ? '' : 's' ) + '?\n\n'
							+ 'Estimated time: ' + timeEstimate + ' (processed in the background, 3 images/minute — depends on your site receiving regular visits to trigger WordPress cron).'
							+ diskLine
							+ '\n\nThis is fully reversible: original files are never touched, only a WebP copy is generated per image. You can revert any image (or all of them) at any time from the table below.';

						if ( window.confirm( msg ) ) {
							clStartBulkOptimize( $btn );
						}
					},
					error: function () {
						// Estimate is a nice-to-have — don't block the user from
						// optimizing just because this one lookup failed.
						$btn.prop( 'disabled', false ).text( originalLabel );
						var msg = 'Optimize ' + unoptimized + ' image' + ( 1 === unoptimized ? '' : 's' ) + '?\n\n'
							+ 'Estimated time: ' + timeEstimate + '.\n\n'
							+ 'This is fully reversible: original files are never touched, only a WebP copy is generated per image.';
						if ( window.confirm( msg ) ) {
							clStartBulkOptimize( $btn );
						}
					},
				}
			);
		}
	);

	// Resume polling on page load if a bulk optimize is already in progress
	// (e.g. the user started it, then navigated away and came back).
	// Staggered (setTimeout) so this doesn't fire in the same instant as the
	// stats/table loads below — some hosts reject bursts of concurrent
	// admin-ajax requests from the same client with a raw 400.
	if ( $( '#cl-image-optimize-all-btn' ).length ) {
		setTimeout( clPollImageOptimizeStatus, 0 );
	}

	// =========================================================
	// Image Optimization: stats dashboard + library availability (Pro)
	// =========================================================

	function clFormatBytes( bytes ) {
		bytes = parseInt( bytes, 10 ) || 0;
		if ( bytes <= 0 ) {
			return '0 B';
		}
		var units = [ 'B', 'KB', 'MB', 'GB' ];
		var i     = 0;
		while ( bytes >= 1024 && i < units.length - 1 ) {
			bytes /= 1024;
			i++;
		}
		return Math.round( bytes * 10 ) / 10 + ' ' + units[ i ];
	}

	function clShowStatsError( reason ) {
		$( '#cl-image-optimize-stats .cl-stat-value' ).text( '—' );

		var $widget = $( '#cl-image-optimize-stats' ).closest( '.cl-widget' );
		var $notice = $widget.find( '.cl-io-stats-error' );

		if ( ! $notice.length ) {
			$notice = $( '<p class="cl-io-stats-error" style="margin:10px 0 0; font-size:12px; color:#dc3545;"></p>' );
			$widget.append( $notice );
		}

		var $retry = $( '<a href="#">Retry</a>' ).on(
			'click',
			function ( e ) {
				e.preventDefault();
				$notice.remove();
				clLoadImageOptimizeStats();
			}
		);

		$notice.empty().text( reason + ' ' ).append( $retry );
	}

	function clLoadImageOptimizeStats() {
		$.ajax(
			{
				url:     clAdminData.ajaxUrl,
				method:  'POST',
				timeout: 20000, // On large media libraries this query can be genuinely slow — fail loud instead of hanging forever.
				data: {
					action: 'cacheloop_get_image_optimize_stats',
					nonce:  clAdminData.nonceDbOptimizer,
				},
				success: function ( response ) {
					if ( ! response.success ) {
						clShowStatsError( response.data && response.data.message ? response.data.message : 'Could not load stats.' );
						return;
					}
					var d = response.data;

					$( '#cl-io-stat-total' ).text( d.total );
					$( '#cl-io-stat-optimized' ).text( d.optimized );
					$( '#cl-io-stat-unoptimized' ).text( d.unoptimized );
					$( '#cl-io-stat-errors' ).text( d.errors );
					$( '#cl-io-stat-saved' ).text( clFormatBytes( d.total_reduction ) );
					$( '#cl-io-stat-percent' ).text( d.percent + '%' );

					var libStatus = {
						auto:    ( d.gd_available || d.imagick_available ) ? '✓ available' : '✗ unavailable',
						gd:      d.gd_available ? '✓ available' : '✗ not installed',
						imagick: d.imagick_available ? '✓ available' : '✗ not installed',
					};
					$( '.cl-io-lib-status' ).each(
						function () {
							var lib = $( this ).data( 'lib' );
							$( this ).text( libStatus[ lib ] || '' );
						}
					);
				},
				error: function ( xhr, textStatus ) {
					var reason = 'timeout' === textStatus
						? 'Timed out — this query can be slow on very large media libraries.'
						: 'Could not load stats (' + textStatus + ').';
					clShowStatsError( reason );
				},
			}
		);
	}

	if ( $( '#cl-image-optimize-stats' ).length ) {
		setTimeout( clLoadImageOptimizeStats, 150 );
	}

	// =========================================================
	// Image Optimization: per-image table + revert (Pro)
	// =========================================================

	var clIoPaged = 1;

	function clStatusLabel( status ) {
		switch ( status ) {
			case 'success': return 'Optimized';
			case 'error':   return 'Error';
			case 'skipped': return 'Skipped';
			default:        return status;
		}
	}

	function clStatusColor( status ) {
		switch ( status ) {
			case 'success': return '#198754';
			case 'error':   return '#dc3545';
			case 'skipped': return '#6c757d';
			default:        return 'inherit';
		}
	}

	function clShowIoTableMessage( text, showRetry ) {
		var $body = $( '#cl-io-table-body' );
		$body.empty();

		var $td = $( '<td colspan="8">' ).text( text );

		if ( showRetry ) {
			$td.append( ' ' ).append(
				$( '<a href="#">Retry</a>' ).on(
					'click',
					function ( e ) {
						e.preventDefault();
						clLoadIoTable( clIoPaged );
					}
				)
			);
		}

		$body.append( $( '<tr>' ).append( $td ) );
		$( '#cl-io-page-info' ).text( '' );
		$( '#cl-io-prev-page' ).prop( 'disabled', true );
		$( '#cl-io-next-page' ).prop( 'disabled', true );
	}

	function clShowImagePreview( url, title ) {
		$( '.cl-img-preview-overlay' ).remove(); // Only one at a time.

		var $overlay = $( '<div class="cl-img-preview-overlay">' ).css(
			{
				position:         'fixed',
				top:              0,
				left:             0,
				right:            0,
				bottom:           0,
				background:       'rgba(0,0,0,0.8)',
				display:          'flex',
				'align-items':    'center',
				'justify-content': 'center',
				'z-index':        999999,
				cursor:           'zoom-out',
			}
		);

		var $img = $( '<img>' ).attr( 'src', url ).attr( 'alt', title || '' ).css(
			{
				'max-width':  '90vw',
				'max-height': '90vh',
				'object-fit': 'contain',
				'box-shadow': '0 4px 30px rgba(0,0,0,0.5)',
				'border-radius': '4px',
			}
		);

		var $close = $( '<button type="button" aria-label="Close">&times;</button>' ).css(
			{
				position:   'fixed',
				top:        '20px',
				right:      '30px',
				background: 'transparent',
				border:     'none',
				color:      '#fff',
				'font-size': '36px',
				'line-height': 1,
				cursor:     'pointer',
			}
		);

		function close() {
			$overlay.remove();
			$( document ).off( 'keydown.clImgPreview' );
		}

		$overlay.on( 'click', close );
		$img.on( 'click', function ( e ) { e.stopPropagation(); } ); // Clicking the image itself shouldn't close it.
		$close.on( 'click', close );
		$( document ).on(
			'keydown.clImgPreview',
			function ( e ) {
				if ( 'Escape' === e.key ) {
					close();
				}
			}
		);

		$overlay.append( $img ).append( $close );
		$( 'body' ).append( $overlay );
	}

	function clLoadIoTable( paged ) {
		clIoPaged = paged || 1;

		$.ajax(
			{
				url:     clAdminData.ajaxUrl,
				method:  'POST',
				timeout: 20000, // On large media libraries this query can be genuinely slow — fail loud instead of hanging forever.
				data: {
					action: 'cacheloop_list_optimized_images',
					nonce:  clAdminData.nonceDbOptimizer,
					paged:  clIoPaged,
				},
				success: function ( response ) {
					if ( ! response.success ) {
						clShowIoTableMessage( response.data && response.data.message ? response.data.message : 'Could not load the image list.', true );
						return;
					}

					if ( ! response.data.rows || 0 === response.data.rows.length ) {
						clShowIoTableMessage( 'No optimized images yet.', false );
						return;
					}

					var $body = $( '#cl-io-table-body' );
					$body.empty();

					var d = response.data;

					d.rows.forEach(
						function ( row ) {
							var $tr = $( '<tr>' );

							var $imgCell = $( '<td>' );
							if ( row.thumbnail ) {
								var $thumb = $( '<img>' ).attr( 'src', row.thumbnail ).css( { width: '40px', height: '40px', 'object-fit': 'cover', 'border-radius': '4px' } );
								if ( row.full_url ) {
									$thumb.css( 'cursor', 'zoom-in' ).attr( 'title', 'Click to preview' ).on(
										'click',
										function () {
											clShowImagePreview( row.full_url, row.title );
										}
									);
								}
								$imgCell.append( $thumb );
							}
							$tr.append( $imgCell );

							$tr.append( $( '<td>' ).text( row.title ) );

							var $statusCell = $( '<td>' ).css( 'color', clStatusColor( row.status ) ).text( clStatusLabel( row.status ) );
							if ( row.reason ) {
								$statusCell.attr( 'title', row.reason );
							}
							$tr.append( $statusCell );

							$tr.append( $( '<td>' ).text( row.original_size > 0 ? clFormatBytes( row.original_size ) : '—' ) );
							$tr.append( $( '<td>' ).text( row.original_size > 0 ? clFormatBytes( row.optimized_size ) : '—' ) );
							$tr.append( $( '<td>' ).text( row.reduction > 0 ? clFormatBytes( row.reduction ) : '—' ) );
							$tr.append( $( '<td>' ).css( 'white-space', 'nowrap' ).attr( 'title', row.date_full || '' ).text( row.date ) );

							var $revertBtn = $( '<button type="button" class="button cl-btn cl-btn--small cl-io-revert-btn">' ).text( 'Revert' ).attr( 'data-id', row.id );
							$tr.append( $( '<td>' ).append( $revertBtn ) );

							$body.append( $tr );
						}
					);

					$( '#cl-io-page-info' ).text( 'Page ' + d.paged + ' of ' + Math.max( 1, d.pages ) + ' (' + d.total + ' total)' );
					$( '#cl-io-prev-page' ).prop( 'disabled', d.paged <= 1 );
					$( '#cl-io-next-page' ).prop( 'disabled', d.paged >= d.pages );
				},
				error: function ( xhr, textStatus ) {
					var reason = 'timeout' === textStatus
						? 'Timed out loading the image list — this can happen on very large media libraries.'
						: 'Could not load the image list (' + textStatus + ').';
					clShowIoTableMessage( reason, true );
				},
			}
		);
	}

	if ( $( '#cl-io-table-body' ).length ) {
		setTimeout( function () { clLoadIoTable( 1 ); }, 300 );
	}

	$( '#cl-io-prev-page' ).on( 'click', function () { clLoadIoTable( clIoPaged - 1 ); } );
	$( '#cl-io-next-page' ).on( 'click', function () { clLoadIoTable( clIoPaged + 1 ); } );

	// Delegated: revert buttons are rendered dynamically.
	$( document ).on(
		'click',
		'.cl-io-revert-btn',
		function () {
			var $btn = $( this );
			var id   = $btn.data( 'id' );

			if ( ! window.confirm( 'Revert this image? The WebP version will be deleted; the original stays untouched.' ) ) {
				return;
			}

			clSetBtnLoading( $btn, '…' );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action:         'cacheloop_revert_image',
						nonce:          clAdminData.nonceDbOptimizer,
						attachment_id:  id,
					},
					success: function () {
						clLoadIoTable( clIoPaged );
						clLoadImageOptimizeStats();
					},
					error: function () {
						clResetBtn( $btn );
					},
				}
			);
		}
	);

	// --- Revert All ---
	var clIoRevertPollTimer = null;

	function clPollRevertStatus() {
		$.ajax(
			{
				url:    clAdminData.ajaxUrl,
				method: 'POST',
				data: {
					action: 'cacheloop_get_revert_status',
					nonce:  clAdminData.nonceDbOptimizer,
				},
				success: function ( response ) {
					if ( ! response.success ) {
						return;
					}
					var d = response.data;

					if ( ! d.running ) {
						clearInterval( clIoRevertPollTimer );
						$( '#cl-io-revert-all-btn' ).prop( 'disabled', false );
						$( '#cl-io-revert-status' ).text( '' );
						clLoadIoTable( 1 );
						clLoadImageOptimizeStats();
						return;
					}

					if ( ! clIoRevertPollTimer ) {
						clIoRevertPollTimer = setInterval( clPollRevertStatus, 3000 );
					}
					$( '#cl-io-revert-all-btn' ).prop( 'disabled', true );
					$( '#cl-io-revert-status' ).text( d.completed + ' / ' + d.total + ' (' + d.percent + '%)' );
				},
			}
		);
	}

	$( '#cl-io-revert-all-btn' ).on(
		'click',
		function () {
			if ( ! window.confirm( 'Revert ALL optimized images? This deletes every generated WebP file. Originals stay untouched. This cannot be undone in bulk.' ) ) {
				return;
			}

			var $btn = $( this );
			$btn.prop( 'disabled', true );
			$( '#cl-io-revert-status' ).text( 'Starting…' );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action: 'cacheloop_start_revert_all',
						nonce:  clAdminData.nonceDbOptimizer,
					},
					success: function ( response ) {
						if ( ! response.success ) {
							$( '#cl-io-revert-status' ).text( response.data.message || clAdminData.i18n.error );
							$btn.prop( 'disabled', false );
							return;
						}
						if ( 0 === response.data.total ) {
							$( '#cl-io-revert-status' ).text( response.data.message );
							$btn.prop( 'disabled', false );
							return;
						}
						$( '#cl-io-revert-status' ).text( response.data.message );
						clearInterval( clIoRevertPollTimer );
						clIoRevertPollTimer = setInterval( clPollRevertStatus, 3000 );
					},
					error: function () {
						$( '#cl-io-revert-status' ).text( clAdminData.i18n.error );
						$btn.prop( 'disabled', false );
					},
				}
			);
		}
	);

	// =========================================================
	// Varnish: Test Connection (Pro)
	// =========================================================

	$( '#cl-varnish-test-btn' ).on(
		'click',
		function () {
			var $btn    = $( this );
			var $result = $( '#cl-varnish-test-result' );

			$result.text( '' );
			clSetBtnLoading( $btn, '…' );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action: 'cacheloop_test_varnish',
						nonce:  clAdminData.nonceDbOptimizer,
						host:   $( '#cl-varnish-host' ).val(),
						port:   $( '#cl-varnish-port' ).val(),
					},
					success: function ( response ) {
						clResetBtn( $btn );
						if ( response.success ) {
							$result.css( 'color', '#198754' ).text( response.data.message );
						} else {
							$result.css( 'color', '#dc3545' ).text( response.data.message || clAdminData.i18n.error );
						}
					},
					error: function () {
						clResetBtn( $btn );
						$result.css( 'color', '#dc3545' ).text( clAdminData.i18n.error );
					},
				}
			);
		}
	);

	// =========================================================
	// Cloudflare: Test Connection (Pro)
	// =========================================================

	$( '#cl-cf-test-btn' ).on(
		'click',
		function () {
			var $btn    = $( this );
			var $result = $( '#cl-cf-test-result' );

			$result.text( '' );
			clSetBtnLoading( $btn, '…' );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action:  'cacheloop_test_cloudflare',
						nonce:   clAdminData.nonceDbOptimizer,
						token:   $( '#cl-cf-token' ).val(),
						zone_id: $( '#cl-cf-zone' ).val(),
					},
					success: function ( response ) {
						clResetBtn( $btn );
						if ( response.success ) {
							$result.css( 'color', '#198754' ).text( response.data.message );
						} else {
							$result.css( 'color', '#dc3545' ).text( response.data.message || clAdminData.i18n.error );
						}
					},
					error: function () {
						clResetBtn( $btn );
						$result.css( 'color', '#dc3545' ).text( clAdminData.i18n.error );
					},
				}
			);
		}
	);

	// =========================================================
	// Object Cache: Test Connection (Pro)
	// =========================================================

	$( '#cl-oc-test-connection' ).on(
		'click',
		function () {
			var $btn    = $( this );
			var $result = $( '#cl-oc-test-result' );

			$result.text( '' );
			clSetBtnLoading( $btn, '…' );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action:   'cacheloop_test_object_cache',
						nonce:    clAdminData.nonceDbOptimizer,
						driver:   $( '#cl-oc-driver' ).val(),
						host:     $( '#cl-oc-host' ).val(),
						port:     $( '#cl-oc-port' ).val(),
						password: $( '#cl-oc-password' ).val(),
					},
					success: function ( response ) {
						clResetBtn( $btn );
						if ( response.success ) {
							$result.css( 'color', '#198754' ).text( response.data.message );
						} else {
							$result.css( 'color', '#dc3545' ).text( response.data.message || clAdminData.i18n.error );
						}
					},
					error: function () {
						clResetBtn( $btn );
						$result.css( 'color', '#dc3545' ).text( clAdminData.i18n.error );
					},
				}
			);
		}
	);

	// Load live stats once, if the Object Cache section is present.
	if ( $( '#cl-oc-stats' ).length ) {
		$.ajax(
			{
				url:    clAdminData.ajaxUrl,
				method: 'POST',
				data: {
					action: 'cacheloop_get_object_cache_stats',
					nonce:  clAdminData.nonceDbOptimizer,
				},
				success: function ( response ) {
					if ( ! response.success ) {
						return;
					}
					var d = response.data;
					var text = d.dropin_active
						? ( d.connected ? 'Backend connected. ' : 'Drop-in active, but backend unreachable (falling back to non-persistent). ' ) + 'Hits: ' + d.hits + ' / Misses: ' + d.misses
						: 'Drop-in not active yet — save settings with Object Cache enabled to install it.';
					$( '#cl-oc-stats' ).text( text );
				},
			}
		);
	}

	// =========================================================
	// Card active highlight (visual feedback when toggle changes)
	// =========================================================

	$( '.cl-card input[type="checkbox"]' ).on(
		'change',
		function () {
			var $card = $( this ).closest( '.cl-card' );
			if ( $( this ).is( ':checked' ) ) {
				$card.addClass( 'cl-card--active' );
			} else {
				$card.removeClass( 'cl-card--active' );
			}
		}
	);

	// Set initial active state based on checked status.
	$( '.cl-card input[type="checkbox"]:checked' ).each(
		function () {
			$( this ).closest( '.cl-card' ).addClass( 'cl-card--active' );
		}
	);

	// =========================================================
	// Debug Log
	// =========================================================

	$( '#cl-refresh-log-btn' ).on(
		'click',
		function () {
			var $btn  = $( this );
			var $out  = $( '#cl-debug-log-output' );
			var lines = $( '#cl-log-lines-select' ).val();

			$btn.prop( 'disabled', true ).text( 'Loading…' );

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action:  'cacheloop_view_debug_log',
						nonce:   clAdminData.nonceClearCache,
						lines:   lines,
					},
					success: function ( response ) {
						$btn.prop( 'disabled', false ).text( 'Refresh Log' );
						if ( response.success && response.data.log ) {
							// File is append-only (oldest first); flip so the
							// most recent entry is at the top — no scrolling
							// needed to see what just happened.
							var reversed = response.data.log.split( '\n' ).reverse().join( '\n' );
							$out.text( reversed );
						} else {
							$out.text( '(No log entries found.)' );
						}
					},
					error: function () {
						$btn.prop( 'disabled', false ).text( 'Refresh Log' );
						$out.text( '(Error loading debug log.)' );
					},
				}
			);
		}
	);

	function clCopyToClipboard( text, $btn ) {
		var restoreLabel = $btn.html();
		var done          = function ( ok ) {
			$btn.html( '<span class="dashicons dashicons-' + ( ok ? 'yes' : 'no' ) + '"></span> ' + ( ok ? 'Copied!' : 'Failed' ) );
			setTimeout(
				function () {
					$btn.html( restoreLabel );
				},
				1500
			);
		};

		if ( navigator.clipboard && navigator.clipboard.writeText ) {
			navigator.clipboard.writeText( text ).then(
				function () { done( true ); },
				function () { done( false ); }
			);
			return;
		}

		// Fallback for non-secure contexts / older browsers.
		var $tmp = $( '<textarea>' ).val( text ).css( { position: 'fixed', opacity: 0 } ).appendTo( 'body' );
		$tmp.trigger( 'select' );
		try {
			done( document.execCommand( 'copy' ) );
		} catch ( e ) {
			done( false );
		}
		$tmp.remove();
	}

	$( '#cl-copy-log-btn' ).on(
		'click',
		function () {
			clCopyToClipboard( $( '#cl-debug-log-output' ).text(), $( this ) );
		}
	);

	$( '#cl-copy-server-info-btn' ).on(
		'click',
		function () {
			clCopyToClipboard( $( '#cl-server-info-output' ).text(), $( this ) );
		}
	);

	$( '#cl-clear-log-btn' ).on(
		'click',
		function () {
			var $btn = $( this );
			if ( ! confirm( 'Are you sure you want to clear the debug log?' ) ) {
				return;
			}

			$.ajax(
				{
					url:    clAdminData.ajaxUrl,
					method: 'POST',
					data: {
						action:  'cacheloop_clear_debug_log',
						nonce:   clAdminData.nonceClearCache,
					},
					success: function ( response ) {
						if ( response.success ) {
							$( '#cl-debug-log-output' ).text( '(Log cleared.)' );
							clShowToast( 'Debug log cleared.', 'success' );
						}
					},
					error: function () {
						clShowToast( 'Error clearing debug log.', 'error' );
					},
				}
			);
		}
	);

	// =========================================================
	// Pro Analysis — Fix Buttons
	// =========================================================

	function clAttachFixButtons() {
		$( '.cl-pro-tip__fix' ).off( 'click' ).on(
			'click',
			function () {
				var $btn   = $( this );
				var action = $btn.data( 'action' );
				if ( ! action ) {
					return;
				}

				$btn.prop( 'disabled', true ).html( '<span class="cl-spinner" aria-hidden="true"></span> Applying…' );

				$.ajax(
					{
						url:    clAdminData.ajaxUrl,
						method: 'POST',
						data: {
							action:  'cacheloop_quick_enable_feature',
							nonce:   clAdminData.nonceClearCache,
							feature: action,
						},
						success: function ( response ) {
							$btn.prop( 'disabled', false ).html( '<span class="dashicons dashicons-update"></span> Fixed' );

							if ( response.success ) {
								if ( response.data.requires_pro ) {
									clShowToast( response.data.message || clAdminData.i18n.proRequired, 'error', 5000 );
									// Open upgrade page in new tab.
									if ( response.data.upgrade_url ) {
										window.open( response.data.upgrade_url, '_blank' );
									}
								} else if ( response.data.switch_tab ) {
									// Switch to the target tab, then scroll.
									var $targetTab = $( '#' + response.data.switch_tab );
									if ( $targetTab.length ) {
										// Show the tab pane.
										$( '.cl-tab-pane' ).hide();
										$targetTab.show();
										// Highlight the correct nav tab.
										$( '.cl-tabs a' ).removeClass( 'cl-active' );
										$( '.cl-tabs a[data-tab="' + response.data.switch_tab + '"]' ).addClass( 'cl-active' );
										// Scroll to the section after tab switch.
										setTimeout(
											function () {
												var $section = $( '#' + response.data.scroll_to );
												if ( $section.length ) {
														$section[ 0 ].scrollIntoView( { behavior: 'smooth', block: 'center' } );
												}
											},
											150
										);
									}
									clShowToast( response.data.message, 'success' );
								} else if ( response.data.scroll_to ) {
									// Scroll within the current tab.
									var $target = $( '#' + response.data.scroll_to );
									if ( $target.length ) {
										$target[ 0 ].scrollIntoView( { behavior: 'smooth', block: 'center' } );
									}
									clShowToast( response.data.message, 'success' );
								} else if ( response.data.updated ) {
									clShowToast( response.data.message || 'Feature enabled.', 'success' );
									// Update the corresponding checkbox in the UI.
									clUpdateCheckboxFromAction( action );
								} else {
									clShowToast( response.data.message || 'Feature already enabled.', 'success' );
								}
							} else {
								clShowToast( response.data.message || 'Error applying fix.', 'error' );
							}
						},
						error: function ( jqXHR, textStatus, errorThrown ) {
							$btn.prop( 'disabled', false ).html( '<span class="dashicons dashicons-update"></span> Retry' );
							clShowToast( 'AJAX error. Please try again.', 'error' );
						},
					}
				);
			}
		);
	}

	// Attach on page load
	clAttachFixButtons();
	clLoadServerInfo();

	/**
	 * Map AJAX action names to checkbox IDs.
	 * Used to visually update the UI after a successful AJAX toggle.
	 */
	function clUpdateCheckboxFromAction( action ) {
		var checkboxMap = {
			'enable_page_cache':      '#cl-page-cache',
			'enable_browser_cache':   '#cl-browser-cache',
			'enable_gzip':            '#cl-gzip',
			'enable_lazy_load':       '#cl-lazy-load',
			'enable_minify':          '#cl-minify-css',
			'enable_delay_js':        '#cl-delay-js',
		};
		var selector    = checkboxMap[ action ];
		if ( selector ) {
			var $cb = $( selector );
			if ( $cb.length ) {
				$cb.prop( 'checked', true );
			}
		}
	}

	/**
	 * Load server info & system check via AJAX.
	 */
	function clLoadServerInfo() {
		var $output  = $( '#cl-server-info-output' );
		var $spinner = $( '#cl-server-info-spinner' );

		if ( ! $output.length ) {
			return;
		}

		$spinner.show();

		$.ajax(
			{
				url:    clAdminData.ajaxUrl,
				method: 'POST',
				data: {
					action:  'cacheloop_get_server_info',
					nonce:   clAdminData.nonceClearCache,
				},
				success: function ( response ) {
					$spinner.hide();
					if ( response.success && response.data.info ) {
						// Populate summary cards.
						var info = response.data.info;

						if ( info['PHP'] && info['PHP']['Version'] ) {
							$( '#cl-server-php-version' ).text( 'PHP ' + info['PHP']['Version'] );
						}
						if ( info['WordPress'] && info['WordPress']['Version'] ) {
							$( '#cl-server-wp-version' ).text( 'WP ' + info['WordPress']['Version'] );
						}
						if ( info['Freemius'] ) {
							var fsText = info['Freemius']['Loaded'] === 'Yes' ? ( info['Freemius']['Can Use Premium'] === 'Yes' ? 'Pro ✓' : 'Free' ) : '✗ Not loaded';
							$( '#cl-server-fs-status' ).text( fsText );
						}
						if ( info['Disk'] ) {
							$( '#cl-server-cache-count' ).text( info['Disk']['Free Space'] || '—' );
						}

						// Populate detailed text.
						var text = '';
						$.each(
							info,
							function ( section, items ) {
								text += '=== ' + section + ' ===\n';
								$.each(
									items,
									function ( key, val ) {
										text += '  ' + key + ': ' + val + '\n';
									}
								);
								text += '\n';
							}
						);
						$output.text( text );
					} else {
						$output.text( 'Failed to load server information.' );
					}
				},
				error: function () {
					$spinner.hide();
					$output.text( 'Error loading server information.' );
				},
			}
		);
	}

	// Refresh button
	$( '#cl-refresh-server-info-btn' ).on(
		'click',
		function () {
			clLoadServerInfo();
		}
	);

	// Download button
	$( '#cl-download-server-info-btn' ).on(
		'click',
		function () {
			var text = $( '#cl-server-info-output' ).text();
			if ( ! text || text === 'Click "Refresh Info" to load server details.' ) {
				clShowToast( 'Please click "Refresh Info" first.', 'error' );
				return;
			}
			var blob   = new Blob( [ text ], { type: 'text/plain' } );
			var url    = URL.createObjectURL( blob );
			var a      = document.createElement( 'a' );
			a.href     = url;
			a.download = 'cacheloop-server-info-' + new Date().toISOString().slice( 0, 10 ) + '.txt';
			document.body.appendChild( a );
			a.click();
			document.body.removeChild( a );
			URL.revokeObjectURL( url );
		}
	);

	// Re-attach after tab switches
	$( document ).on(
		'click',
		'.cl-tabs a',
		function () {
			setTimeout(
				function () {
					clAttachFixButtons();
					clLoadServerInfo();
				},
				100
			);
		}
	);

	/**
	 * Fix footer positioning on long admin pages.
	 * Ensures #wpfooter stays relatively below all plugin content.
	 */
	( function () {
		var $wpfooter = $( '#wpfooter' );
		if ( $wpfooter.length ) {
			$wpfooter.css(
				{
					'position' : 'relative',
					'bottom'   : 'auto',
					'clear'    : 'both',
					'marginTop': '40px',
				}
			);
		}
	} )();

} )( jQuery );
