<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/* -------------------------------------------------------------------------
 * Blogname meta sync (multisite)
 * ---------------------------------------------------------------------- */

add_action(
	'update_option_blogname',
	static function ( $old_value, $value ): void {
		if ( ! is_multisite() || ! function_exists( 'update_metadata' ) ) {
			return;
		}
		update_metadata( 'blog', (int) get_current_blog_id(), 'misticease_blogname', sanitize_text_field( (string) $value ) );
	},
	10,
	2
);


/* -------------------------------------------------------------------------
 * New site initialization
 * ---------------------------------------------------------------------- */

function misticease_on_new_blog( int $blog_id ): void {
	if ( ! is_multisite() ) { return; }

	require_once ABSPATH . 'wp-admin/includes/plugin.php';
	if ( ! is_plugin_active_for_network( plugin_basename( misticease_basefile() ) ) ) { return; }

	$blog_id = max( 1, (int) $blog_id );
	switch_to_blog( $blog_id );

	$site_dir = misticease_cache_dir();
	misticease_ensure_dir_with_index( $site_dir );

	if ( function_exists( 'update_metadata' ) ) {
		update_metadata( 'blog', $blog_id, 'misticease_used_cache_bytes', 0 );
		update_metadata( 'blog', $blog_id, 'misticease_blogname', sanitize_text_field( (string) get_bloginfo( 'name' ) ) );
	}

	restore_current_blog();
}

add_action( 'wpmu_new_blog', 'misticease_on_new_blog', 10, 1 );

function misticease_on_site_initialized( $site ): void {
	if ( ! is_object( $site ) || empty( $site->blog_id ) ) {
		return;
	}
	misticease_on_new_blog( (int) $site->blog_id );
}

add_action( 'wp_initialize_site', 'misticease_on_site_initialized', 10, 1 );


/* -------------------------------------------------------------------------
 * Network readme view
 * ---------------------------------------------------------------------- */

function misticease_render_network_readme_tab(): void {
	?>
	<div class="misticease-readme">
		<h2><?php esc_html_e( 'What this panel controls', 'misticease' ); ?></h2>
		<p><?php esc_html_e( 'This panel lets network administrators set shared defaults for thumbnail cache limits and CDN hostnames across the network.', 'misticease' ); ?></p>

		<h2><?php esc_html_e( 'Thumbnail cache limits', 'misticease' ); ?></h2>
		<p><?php esc_html_e( 'Enable the network cache limit when all sites should use the same per-site thumbnail cache limit.', 'misticease' ); ?></p>
		<p><?php esc_html_e( 'Leave it disabled when each site should manage its own thumbnail cache limit.', 'misticease' ); ?></p>

		<h2><?php esc_html_e( 'CDN hostnames', 'misticease' ); ?></h2>
		<p><?php esc_html_e( 'Use the network CDN host allowlist when all sites should rely on one approved list of CDN hostnames.', 'misticease' ); ?></p>
		<p><?php esc_html_e( 'When this is enforced, site-level CDN inputs are disabled and the network allowlist is used everywhere.', 'misticease' ); ?></p>

		<h2><?php esc_html_e( 'Recalculate usage for all sites', 'misticease' ); ?></h2>
		<p><?php esc_html_e( 'This action recalculates cached thumbnail usage for every site in the network. It is useful after migrations, manual cleanup, or large content updates.', 'misticease' ); ?></p>

		<h2><?php esc_html_e( 'Site usage table', 'misticease' ); ?></h2>
		<p><?php esc_html_e( 'This table shows thumbnail cache usage per site. Use it to see which sites are consuming cache storage across the network.', 'misticease' ); ?></p>

		<p style="margin-top:18px;"><?php esc_html_e( 'These settings help MisticEase support media-rich WordPress networks.', 'misticease' ); ?></p>
	</div>
	<?php
}

/* -------------------------------------------------------------------------
 * Network admin (multisite minimal)
 * ---------------------------------------------------------------------- */

add_action( 'network_admin_menu', static function (): void {
	if ( ! is_multisite() ) {
		return;
	}

	add_menu_page(
		__( 'MisticEase network settings', 'misticease' ),
		__( 'MisticEase network', 'misticease' ),
		'manage_network_options',
		'misticease-network',
		'misticease_network_page',
		plugins_url( 'inc/app/img/icon.png', misticease_basefile() ),
        99
	);
} );

add_action( 'network_admin_edit_misticease_network_save', 'misticease_network_save_handler' );
add_action( 'admin_post_misticease_network_save',        'misticease_network_save_handler' );

function misticease_network_save_handler(): void {
	if ( ! is_multisite() ) {
		wp_die( esc_html__( 'Multisite is not enabled.', 'misticease' ) );
	}

	if ( ! current_user_can( 'manage_network_options' ) ) {
		wp_die( esc_html__( 'Sorry, you are not allowed to manage network options.', 'misticease' ) );
	}

	check_admin_referer( 'misticease_network_save' );

	$raw = [];

	$has_net = isset( $_POST['misticease_net'] ) && is_array( $_POST['misticease_net'] );

	$enforce_cap = $has_net
		&& isset( $_POST['misticease_net']['enforce_cap'] )
		&& 'on' === sanitize_key( wp_unslash( (string) $_POST['misticease_net']['enforce_cap'] ) );

	$cap_mb = $has_net && isset( $_POST['misticease_net']['default_cap_mb'] )
		? max( 0, absint( wp_unslash( (string) $_POST['misticease_net']['default_cap_mb'] ) ) )
		: 0;

	$enforce_cdn = $has_net
		&& isset( $_POST['misticease_net']['enforce_cdn'] )
		&& 'on' === sanitize_key( wp_unslash( (string) $_POST['misticease_net']['enforce_cdn'] ) );

	$cdn_raw = $has_net && isset( $_POST['misticease_net']['cdn_hosts'] )
		? sanitize_textarea_field( wp_unslash( (string) $_POST['misticease_net']['cdn_hosts'] ) )
		: '';

	$lines = preg_split( '/\r\n|\r|\n/', $cdn_raw ) ?: [];
	$hosts = [];

	foreach ( $lines as $h ) {
		$h = strtolower( trim( (string) $h ) );

		if ( '' === $h ) {
			continue;
		}

		$h = preg_replace( '#^\w+://#', '', $h );
		$h = preg_replace( '#/.*$#', '', $h );

		if ( preg_match( '/^[a-z0-9.-]+$/', (string) $h ) ) {
			$hosts[] = $h;
		}
	}

	$hosts = array_values( array_unique( $hosts ) );

	update_site_option( 'misticease_network_enforce_quota', (bool) $enforce_cap );
	update_site_option( 'misticease_network_default_quota_mb', (int) $cap_mb );
	update_site_option( 'misticease_network_enforce_cdn', (bool) $enforce_cdn );
	update_site_option( 'misticease_network_cdn_hosts', $hosts );

	wp_safe_redirect(
		network_admin_url( 'admin.php?page=misticease-network&tab=settings&updated=done' )
	);
	exit;
}

add_action( 'network_admin_edit_misticease_network_sync', 'misticease_network_sync_handler' );
add_action( 'admin_post_misticease_network_sync',        'misticease_network_sync_handler' );

function misticease_network_sync_handler(): void {
	if ( ! is_multisite() ) {
		wp_die( esc_html__( 'Multisite is not enabled.', 'misticease' ) );
	}
	if ( ! current_user_can( 'manage_network_options' ) ) {
		wp_die( esc_html__( 'Sorry, you are not allowed to manage network options.', 'misticease' ) );
	}
	check_admin_referer( 'misticease_network_sync' );

	$sites = get_sites( [ 'number' => 0, 'fields' => 'ids' ] );
	foreach ( $sites as $bid ) {
		switch_to_blog( (int) $bid );
		misticease_sync_used_thumb_cache();
		restore_current_blog();
	}

	wp_safe_redirect(
		network_admin_url( 'admin.php?page=misticease-network&tab=settings&synced=done' )
	);
	exit;
}

function misticease_network_page(): void {
	if ( ! current_user_can( 'manage_network_options' ) ) {
		wp_die( esc_html__( 'Sorry, you are not allowed to manage network options.', 'misticease' ) );
	}

	$tab_raw = filter_input( INPUT_GET, 'tab', FILTER_DEFAULT );
	$tab     = is_string( $tab_raw ) ? sanitize_key( $tab_raw ) : 'settings';
	$allowed = [ 'settings', 'readme', 'upgrade' ];
	if ( ! in_array( $tab, $allowed, true ) ) {
		$tab = 'settings';
	}

	$tab_url = static function ( string $key ): string {
		return network_admin_url( 'admin.php?page=misticease-network&tab=' . $key );
	};

	?>
	<div class="wrap" style="max-width:800px;margin:0 auto;">
		<h1 style="text-align:center;margin-bottom:1rem;"><?php esc_html_e( 'MisticEase – Network control panel', 'misticease' ); ?></h1>

		<h2 class="nav-tab-wrapper">
			<a href="<?php echo esc_url( $tab_url( 'settings' ) ); ?>" class="nav-tab <?php echo ( 'settings' === $tab ) ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Settings', 'misticease' ); ?></a>
			<a href="<?php echo esc_url( $tab_url( 'readme' ) ); ?>"   class="nav-tab <?php echo ( 'readme'   === $tab ) ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Readme', 'misticease' ); ?></a>
			<a href="<?php echo esc_url( $tab_url( 'upgrade' ) ); ?>" class="nav-tab upgrade-tab <?php echo ( 'upgrade'  === $tab ) ? 'nav-tab-active' : ''; ?>"><?php esc_html_e( 'Upgrade', 'misticease' ); ?></a>
		</h2>

		<div style="padding: 15px; border: 1px solid #ddd;">
			<?php
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if ( isset( $_GET['updated'] ) && 'done' === $_GET['updated'] ) {
				echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Settings saved.', 'misticease' ) . '</p></div>';
			}
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if ( isset( $_GET['synced'] ) && 'done' === $_GET['synced'] ) {
				echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__( 'Usage recalculated for all sites.', 'misticease' ) . '</p></div>';
			}

			switch ( $tab ) {
				case 'readme':
					misticease_render_network_readme_tab();
					break;
				case 'upgrade':
					require_once __DIR__ . '/ug/misticease-ug-tab.php';
					if ( function_exists( 'misticease_render_upgrade_tab' ) ) {
						misticease_render_upgrade_tab();
					} else {
						echo '<p>' . esc_html__( 'Upgrade view is unavailable.', 'misticease' ) . '</p>';
					}
					break;

				case 'settings':
				default:
					misticease_network_settings_content();
					break;
			}
			?>
		</div>
	</div>
	<?php
}

function misticease_net_usage_parse_args(): array {
	$search_raw = filter_input( INPUT_GET, 'ms', FILTER_DEFAULT );
	$search     = is_string( $search_raw ) ? sanitize_text_field( wp_unslash( $search_raw ) ) : '';
	$search     = trim( $search );

	$per_raw = filter_input( INPUT_GET, 'mpp', FILTER_DEFAULT );
	$per     = is_scalar( $per_raw ) ? absint( (string) $per_raw ) : 50;
	$allowed = [ 25, 50, 100 ];
	if ( ! in_array( $per, $allowed, true ) ) {
		$per = 50;
	}

	$page_raw = filter_input( INPUT_GET, 'mp', FILTER_DEFAULT );
	$page     = is_scalar( $page_raw ) ? max( 1, absint( (string) $page_raw ) ) : 1;

	$ob_raw = filter_input( INPUT_GET, 'mob', FILTER_DEFAULT );
	$ob     = is_string( $ob_raw ) ? sanitize_key( $ob_raw ) : 'id';
	$ob_map = [ 'id', 'name', 'used' ];
	if ( ! in_array( $ob, $ob_map, true ) ) {
		$ob = 'id';
	}

	$order_raw = filter_input( INPUT_GET, 'morder', FILTER_DEFAULT );
	$order     = is_string( $order_raw ) ? strtoupper( sanitize_key( $order_raw ) ) : 'ASC';
	$order     = ( 'DESC' === $order ) ? 'DESC' : 'ASC';

	return [
		'search'   => $search,
		'per_page' => $per,
		'page'     => $page,
		'orderby'  => $ob,
		'order'    => $order,
	];
}

function misticease_net_usage_fetch( array $args ): array {
	global $wpdb;

	if ( empty( $wpdb->blogmeta ) ) {
		return [
			'items'             => [],
			'total_items'       => 0,
			'total_bytes'       => 0,
			'blogmeta_available'=> false,
		];
	}

	$blogs   = $wpdb->blogs;
	$blogmeta = $wpdb->blogmeta;

	$meta_name = 'misticease_blogname';
	$meta_used = 'misticease_used_cache_bytes';

	$network_id = function_exists( 'get_current_network_id' ) ? (int) get_current_network_id() : 1;

	$where_sql = "WHERE b.site_id = %d AND b.deleted = 0 AND b.spam = 0 AND b.archived = 0";
	$where_args = [ $network_id ];

	$search = (string) ( $args['search'] ?? '' );
	if ( '' !== $search ) {
		if ( ctype_digit( $search ) ) {
			$where_sql .= " AND b.blog_id = %d";
			$where_args[] = (int) $search;
		} else {
			$like = '%' . $wpdb->esc_like( $search ) . '%';
			$where_sql .= " AND bm_name.meta_value LIKE %s";
			$where_args[] = $like;
		}
	}

	$orderby_key = (string) ( $args['orderby'] ?? 'id' );
	$order_dir   = ( 'DESC' === (string) ( $args['order'] ?? 'ASC' ) ) ? 'DESC' : 'ASC';

	switch ( $orderby_key ) {
		case 'name':
			$order_by = 'bm_name.meta_value';
			break;
		case 'used':
			$order_by = 'CAST(bm_used.meta_value AS UNSIGNED)';
			break;
		case 'id':
		default:
			$order_by = 'b.blog_id';
			break;
	}

	$per_page = max( 1, (int) ( $args['per_page'] ?? 50 ) );
	$page     = max( 1, (int) ( $args['page'] ?? 1 ) );
	$offset   = ( $page - 1 ) * $per_page;

	$sql_totals = "
		SELECT
			COUNT(DISTINCT b.blog_id) AS cnt,
			COALESCE(SUM(CAST(bm_used.meta_value AS UNSIGNED)), 0) AS sum_bytes
		FROM {$blogs} b
		LEFT JOIN {$blogmeta} bm_name ON (bm_name.blog_id = b.blog_id AND bm_name.meta_key = %s)
		LEFT JOIN {$blogmeta} bm_used ON (bm_used.blog_id = b.blog_id AND bm_used.meta_key = %s)
		{$where_sql}
	";
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Admin-only listing totals; table names are internal and ORDER BY is allowlisted; placeholders used via prepare().
	$tot = $wpdb->get_row( $wpdb->prepare( $sql_totals, array_merge( [ $meta_name, $meta_used ], $where_args ) ), ARRAY_A );

	$total_items = isset( $tot['cnt'] ) ? (int) $tot['cnt'] : 0;
	$total_bytes = isset( $tot['sum_bytes'] ) ? (int) $tot['sum_bytes'] : 0;

	$sql_list = "
		SELECT
			b.blog_id AS blog_id,
			bm_name.meta_value AS blogname,
			bm_used.meta_value AS used_bytes
		FROM {$blogs} b
		LEFT JOIN {$blogmeta} bm_name ON (bm_name.blog_id = b.blog_id AND bm_name.meta_key = %s)
		LEFT JOIN {$blogmeta} bm_used ON (bm_used.blog_id = b.blog_id AND bm_used.meta_key = %s)
		{$where_sql}
		ORDER BY {$order_by} {$order_dir}
		LIMIT %d OFFSET %d
	";
	$params = array_merge( [ $meta_name, $meta_used ], $where_args, [ $per_page, $offset ] );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Admin-only listing query; table names are internal and ORDER BY is allowlisted; placeholders used via prepare().
	$rows = $wpdb->get_results( $wpdb->prepare( $sql_list, $params ), ARRAY_A );

	$items = [];
	foreach ( (array) $rows as $r ) {
		$bid = isset( $r['blog_id'] ) ? (int) $r['blog_id'] : 0;
		if ( $bid <= 0 ) { continue; }

		$name = isset( $r['blogname'] ) ? (string) $r['blogname'] : '';
		$name = sanitize_text_field( $name );

		$bytes = isset( $r['used_bytes'] ) ? (int) $r['used_bytes'] : 0;
		if ( $bytes < 0 ) { $bytes = 0; }

		if ( '' === $name ) {
			$opt_name = get_blog_option( $bid, 'blogname', '' );
			if ( is_string( $opt_name ) && '' !== $opt_name ) {
				$name = sanitize_text_field( $opt_name );
			} else {
				$details = get_blog_details( $bid, false );
				if ( $details && ! empty( $details->blogname ) ) {
					$name = sanitize_text_field( (string) $details->blogname );
				}
			}
			if ( '' !== $name && function_exists( 'update_metadata' ) ) {
				update_metadata( 'blog', $bid, 'misticease_blogname', $name );
			}
		}


		$items[] = [
			'blog_id'    => $bid,
			'blogname'   => $name,
			'used_bytes' => $bytes,
		];
	}

	return [
		'items'              => $items,
		'total_items'        => $total_items,
		'total_bytes'        => $total_bytes,
		'blogmeta_available' => true,
	];
}

function misticease_network_settings_content(): void {
	$enforce_cap = (bool) get_site_option( 'misticease_network_enforce_quota', false );
	$cap_mb      = (int)  get_site_option( 'misticease_network_default_quota_mb', 0 );

	$enforce_cdn = (bool) get_site_option( 'misticease_network_enforce_cdn', false );
	$cdn_hosts   = (array) get_site_option( 'misticease_network_cdn_hosts', [] );

	?>
	<form id="misticease-net-save"
		class="misticease-netcp"
		method="post"
		action="<?php echo esc_url( network_admin_url( 'edit.php?action=misticease_network_save' ) ); ?>"
		style="margin-bottom:0;">
		<?php wp_nonce_field( 'misticease_network_save' ); ?>
		<input type="hidden" name="action" value="misticease_network_save">

		<h2><?php esc_html_e( 'Network defaults', 'misticease' ); ?></h2>
		<table class="form-table" role="presentation">
			<tr>
				<th scope="row"><?php esc_html_e( 'Enforce one per-site thumbnail cache limit across the network', 'misticease' ); ?></th>
				<td>
					<label>
						<input type="checkbox" name="misticease_net[enforce_cap]" value="on" <?php checked( $enforce_cap ); ?>>
						<?php esc_html_e( 'When enabled, site-level limit inputs are disabled and the default per-site limit below applies to each site.', 'misticease' ); ?>
					</label>
				</td>
			</tr>
			<tr>
				<th scope="row"><?php esc_html_e( 'Default thumbnail cache limit (MB)', 'misticease' ); ?></th>
				<td>
					<input type="number" name="misticease_net[default_cap_mb]" value="<?php echo esc_attr( (string) $cap_mb ); ?>" min="0" step="10" style="width:180px;text-align:right;">
					<p><?php esc_html_e( 'Set the per-site thumbnail cache storage limit that applies across the network. The default value is 0, which means unlimited storage. When this option is not enforced, cache limits can be configured individually for each site.', 'misticease' ); ?></p>

					<p><?php esc_html_e( 'When a storage limit is configured, cached files are removed automatically, starting with the oldest files, as soon as the limit is reached. A scheduled cache cleanup also runs once every 30 days.', 'misticease' ); ?></p>
				</td>
			</tr>
			<tr>
				<th scope="row"><?php esc_html_e( 'Enforce network CDN host allowlist', 'misticease' ); ?></th>
				<td>
					<label>
						<input type="checkbox" name="misticease_net[enforce_cdn]" value="on" <?php checked( $enforce_cdn ); ?>>
						<?php esc_html_e( 'When enabled, each site uses this allowlist and site-level inputs are disabled.', 'misticease' ); ?>
					</label>
				</td>
			</tr>
			<tr>
				<th scope="row"><?php esc_html_e( 'CDN hostnames (one per line)', 'misticease' ); ?></th>
				<td>
					<textarea name="misticease_net[cdn_hosts]" rows="4" style="width:460px;"><?php echo esc_textarea( implode( "\n", array_map( 'strval', $cdn_hosts ) ) ); ?></textarea>
					<p><?php esc_html_e( 'Example: cdn.example.com (no scheme, no path).', 'misticease' ); ?></p>
					<p><?php esc_html_e( 'This setting lets media from the specified CDN hostnames open in your site’s pop-up viewer.', 'misticease' ); ?><?php esc_html_e( 'It is not intended for hotlink protection, and because modern browsers may omit or truncate the Referer header, Referer-based access control is not implemented to avoid blocking legitimate visitors.', 'misticease' ); ?></p>
				</td>
			</tr>
		</table>
	</form>

	<div class="misticease-netcp-actions">
		<form method="post" action="<?php echo esc_url( network_admin_url( 'edit.php?action=misticease_network_sync' ) ); ?>" style="margin:0;">
			<?php wp_nonce_field( 'misticease_network_sync' ); ?>
			<input type="submit" class="button" value="<?php echo esc_attr( __( 'Recalculate usage for all sites', 'misticease' ) ); ?>">
		</form>

		<input type="submit" form="misticease-net-save" class="button button-primary" value="<?php echo esc_attr( __( 'Save network settings', 'misticease' ) ); ?>">
	</div>


	<hr>
	<h2 id="misticease-site-usage"><?php esc_html_e( 'Site usage (thumbnail cache only)', 'misticease' ); ?></h2>
	<?php
	$list_args = misticease_net_usage_parse_args();
	$list_res  = misticease_net_usage_fetch( $list_args );

	if ( empty( $list_res['blogmeta_available'] ) ) {
		echo '<div class="notice notice-warning"><p>' . esc_html__( 'This view requires the multisite blogmeta table. Update WordPress to a newer version.', 'misticease' ) . '</p></div>';
		return;
	}

	$items       = (array) ( $list_res['items'] ?? [] );
	$total_items = (int) ( $list_res['total_items'] ?? 0 );
	$total_bytes = (int) ( $list_res['total_bytes'] ?? 0 );

	$per_page = (int) ( $list_args['per_page'] ?? 50 );
	$page_num = (int) ( $list_args['page'] ?? 1 );
	$orderby  = (string) ( $list_args['orderby'] ?? 'id' );
	$order    = (string) ( $list_args['order'] ?? 'ASC' );

	$total_pages = ( $per_page > 0 ) ? (int) ceil( $total_items / $per_page ) : 1;
	$total_pages = max( 1, $total_pages );
	$page_num    = min( max( 1, $page_num ), $total_pages );

	$fragment   = '#misticease-site-usage';
	$action_url = network_admin_url( 'admin.php' ) . $fragment;

	$base_args = [
		'page'   => 'misticease-network',
		'tab'    => 'settings',
		'ms'     => (string) ( $list_args['search'] ?? '' ),
		'mpp'    => $per_page,
		'mob'    => $orderby,
		'morder' => $order,
	];

	$build_url = static function ( array $patch ) use ( $base_args ): string {
		$base = network_admin_url( 'admin.php' );
		$q    = array_merge( $base_args, $patch );
		$q    = array_filter( $q, static function ( $v ) { return '' !== $v && null !== $v; } );
		return add_query_arg( $q, $base ) . '#misticease-site-usage';
	};

	$render_controls = static function () use ( $action_url, $base_args, $per_page, $page_num, $total_pages, $total_items, $build_url ): void {
		?>
		<div class="misticease-usage-controls" style="display:flex;justify-content:space-between;align-items:center;gap:10px;margin:10px 0;flex-wrap:wrap;">
			<form method="get" action="<?php echo esc_url( $action_url ); ?>" style="display:flex;gap:10px;align-items:center;margin:0;">
				<input type="hidden" name="page" value="misticease-network">
				<input type="hidden" name="tab" value="settings">
				<input type="hidden" name="mob" value="<?php echo esc_attr( (string) $base_args['mob'] ); ?>">
				<input type="hidden" name="morder" value="<?php echo esc_attr( (string) $base_args['morder'] ); ?>">
				<input type="hidden" name="mp" value="1">
				<input type="text" name="ms" value="<?php echo esc_attr( (string) $base_args['ms'] ); ?>" style="width:240px;" placeholder="<?php echo esc_attr( __( 'Search', 'misticease' ) ); ?>">
				<button type="submit" class="button"><?php esc_html_e( 'Search', 'misticease' ); ?></button>
				<select name="mpp" style="width:70px;" onchange="this.form.submit()">
					<?php foreach ( [ 25, 50, 100 ] as $n ) : ?>
						<option value="<?php echo esc_attr( (string) $n ); ?>" <?php selected( $per_page, $n ); ?>><?php echo esc_html( (string) $n ); ?></option>
					<?php endforeach; ?>
				</select>
			</form>

			<div style="display:flex;gap:8px;align-items:center;">
				<?php /* translators: %d: number of matching sites. */ ?>
				<span><?php echo esc_html( sprintf( _n( '%d site', '%d sites', $total_items, 'misticease' ), $total_items ) ); ?></span>

				<a class="button" href="<?php echo esc_url( $build_url( [ 'mp' => max( 1, $page_num - 1 ) ] ) ); ?>" <?php echo ( $page_num <= 1 ) ? 'aria-disabled="true" style="pointer-events:none;opacity:.5;"' : ''; ?>>&lsaquo;</a>

				<form method="get" action="<?php echo esc_url( $action_url ); ?>" style="display:flex;gap:6px;align-items:center;margin:0;">
					<?php foreach ( $base_args as $k => $v ) : ?>
						<input type="hidden" name="<?php echo esc_attr( $k ); ?>" value="<?php echo esc_attr( (string) $v ); ?>">
					<?php endforeach; ?>
					<input type="number" name="mp" min="1" max="<?php echo esc_attr( (string) $total_pages ); ?>" value="<?php echo esc_attr( (string) $page_num ); ?>" style="width:90px;text-align:right;">
					<span>/</span>
					<span><?php echo esc_html( (string) $total_pages ); ?></span>
					<button type="submit" class="button"><?php esc_html_e( 'Go', 'misticease' ); ?></button>
				</form>

				<a class="button" href="<?php echo esc_url( $build_url( [ 'mp' => min( $total_pages, $page_num + 1 ) ] ) ); ?>" <?php echo ( $page_num >= $total_pages ) ? 'aria-disabled="true" style="pointer-events:none;opacity:.5;"' : ''; ?>>&rsaquo;</a>
			</div>
		</div>
		<?php
	};

	$sort_link = static function ( string $key, string $label ) use ( $orderby, $order, $build_url ): string {
		$is_active = ( $orderby === $key );
		$next      = ( $is_active && 'ASC' === $order ) ? 'DESC' : 'ASC';
		$arrow     = $is_active ? ( 'ASC' === $order ? ' ▲' : ' ▼' ) : '';
		$url       = $build_url( [ 'mob' => $key, 'morder' => $next, 'mp' => 1 ] );
		return '<a href="' . esc_url( $url ) . '">' . esc_html( $label . $arrow ) . '</a>';
	};

	$render_controls();
	?>
	<table class="widefat striped" style="margin-top:10px;">
		<thead>
			<tr>
				<th style="width:70px;white-space:nowrap;"><?php echo wp_kses_post( $sort_link( 'id', __( 'Site ID', 'misticease' ) ) ); ?></th>
				<th><?php echo wp_kses_post( $sort_link( 'name', __( 'Site name', 'misticease' ) ) ); ?></th>
				<th style="text-align:right;"><?php echo wp_kses_post( $sort_link( 'used', __( 'Storage used (MB)', 'misticease' ) ) ); ?></th>
			</tr>
		</thead>
		<tbody>
			<?php if ( empty( $items ) ) : ?>
				<tr>
					<td colspan="3"><?php esc_html_e( 'No results found.', 'misticease' ); ?></td>
				</tr>
			<?php else : ?>
				<?php foreach ( $items as $row ) : ?>
					<?php
					$bid       = (int) ( $row['blog_id'] ?? 0 );
					$site_name = (string) ( $row['blogname'] ?? '' );
					$bytes     = (int) ( $row['used_bytes'] ?? 0 );
					$used_mb   = round( $bytes / 1048576, 2 );
					?>
					<tr>
						<td style="width:70px;white-space:nowrap;"><?php echo esc_html( (string) $bid ); ?></td>
						<td><?php echo esc_html( $site_name ); ?></td>
						<td style="text-align:right;"><?php echo esc_html( number_format_i18n( $used_mb, 2 ) ); ?></td>
					</tr>
				<?php endforeach; ?>
			<?php endif; ?>
		</tbody>
		<tfoot>
			<tr>
				<th colspan="2" style="text-align:right;"><?php esc_html_e( 'Total', 'misticease' ); ?></th>
				<th style="text-align:right;">
					<?php echo esc_html( number_format_i18n( round( $total_bytes / 1048576, 2 ), 2 ) ); ?>
				</th>
			</tr>
		</tfoot>
	</table>
	<?php
	$render_controls();
	?>
	<?php
}