<?php
/**
 * Admin Attendance Management
 *
 * Handles admin interface for attendance management
 *
 * @package    Karate_Club_Manager
 * @subpackage Karate_Club_Manager/includes/admin
 * @since      1.0.0
 */

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

/**
 * Class MACM_Admin_Attendance
 *
 * Manages admin attendance page
 *
 * @since 1.0.0
 */
class MACM_Admin_Attendance {

	/**
	 * Constructor
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		add_action( 'admin_menu', array( $this, 'register_menu_page' ), 18 );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );

		// AJAX handlers.
		add_action( 'wp_ajax_macm_admin_generate_report', array( $this, 'ajax_generate_report' ) );
		add_action( 'wp_ajax_macm_admin_export_csv', array( $this, 'ajax_export_csv' ) );
		add_action( 'wp_ajax_macm_admin_clear_records', array( $this, 'ajax_clear_records' ) );
		add_action( 'wp_ajax_macm_admin_save_permissions', array( $this, 'ajax_save_permissions' ) );
	}

	/**
	 * Register admin menu page
	 *
	 * @since 1.0.0
	 */
	public function register_menu_page() {
		add_submenu_page(
			'martial-arts-club-manager',
			__( 'Attendance', 'martial-arts-club-manager' ),
			__( 'Attendance', 'martial-arts-club-manager' ),
			'manage_macm_attendance',
			'kcm-attendance',
			array( $this, 'render_page' )
		);
	}

	/**
	 * Enqueue admin scripts and styles
	 *
	 * @since 1.0.0
	 * @param string $hook Current admin page hook.
	 */
	public function enqueue_assets( $hook ) {
		if ( false === strpos( $hook, 'kcm-attendance' ) ) {
			return;
		}

		// Enqueue DataTables CSS.
		wp_enqueue_style(
			'datatables',
			plugins_url( 'assets/css/datatables.min.css', dirname( __DIR__ ) ),
			array(),
			'1.13.11'
		);

		// Enqueue DataTables JS.
		wp_enqueue_script(
			'datatables',
			plugins_url( 'assets/js/datatables.min.js', dirname( __DIR__ ) ),
			array( 'jquery' ),
			'1.13.11',
			true
		);

		// Enqueue Chart.js (locally hosted).
		wp_enqueue_script(
			'chartjs',
			plugins_url( 'assets/js/chart.min.js', dirname( __DIR__ ) ),
			array(),
			'4.4.1',
			true
		);

		wp_enqueue_script(
			'kcm-admin-attendance',
			plugins_url( 'assets/js/admin-attendance.js', dirname( __DIR__ ) ),
			array( 'jquery', 'datatables', 'chartjs' ),
			MACM_VERSION,
			true
		);

		wp_localize_script(
			'kcm-admin-attendance',
			'macmAdminAttendance',
			array(
				'ajaxurl' => admin_url( 'admin-ajax.php' ),
				'nonce'   => wp_create_nonce( 'kcm-admin-attendance-nonce' ),
			)
		);
	}

	/**
	 * Render admin attendance page
	 *
	 * @since 1.0.0
	 */
	public function render_page() {
		// Check user capability.
		if ( ! current_user_can( 'manage_macm_attendance' ) ) {
			wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'martial-arts-club-manager' ) );
		}

		// Get active tab - capability verified above, read-only navigation.
		$allowed_tabs = array( 'view', 'report', 'clear', 'permissions' );
		$tab_raw      = filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$tab_input    = ! empty( $tab_raw ) ? sanitize_key( $tab_raw ) : 'view';
		$active_tab   = in_array( $tab_input, $allowed_tabs, true ) ? $tab_input : 'view';

		?>
		<div class="wrap">
			<h1><?php esc_html_e( 'Attendance Management', 'martial-arts-club-manager' ); ?></h1>

			<h2 class="nav-tab-wrapper">
				<a href="?page=kcm-attendance&tab=view" class="nav-tab <?php echo 'view' === $active_tab ? 'nav-tab-active' : ''; ?>">
					<?php esc_html_e( 'View Records', 'martial-arts-club-manager' ); ?>
				</a>
				<a href="?page=kcm-attendance&tab=report" class="nav-tab <?php echo 'report' === $active_tab ? 'nav-tab-active' : ''; ?>">
					<?php esc_html_e( 'Generate Report', 'martial-arts-club-manager' ); ?>
				</a>
				<a href="?page=kcm-attendance&tab=clear" class="nav-tab <?php echo 'clear' === $active_tab ? 'nav-tab-active' : ''; ?>">
					<?php esc_html_e( 'Clear Records', 'martial-arts-club-manager' ); ?>
				</a>
				<a href="?page=kcm-attendance&tab=permissions" class="nav-tab <?php echo 'permissions' === $active_tab ? 'nav-tab-active' : ''; ?>">
					<?php esc_html_e( 'Permissions', 'martial-arts-club-manager' ); ?>
				</a>
			</h2>

			<div class="tab-content">
				<?php
				switch ( $active_tab ) {
					case 'report':
						$this->render_report_tab();
						break;
					case 'clear':
						$this->render_clear_tab();
						break;
					case 'permissions':
						$this->render_permissions_tab();
						break;
					case 'view':
					default:
						$this->render_view_tab();
						break;
				}
				?>
			</div>
		</div>
		<?php
	}

	/**
	 * Render View Records tab
	 *
	 * @since 1.0.0
	 */
	private function render_view_tab() {
		global $wpdb;

		// Get pagination parameters.
		$paged_raw = filter_input( INPUT_GET, 'paged', FILTER_SANITIZE_NUMBER_INT );
		$paged     = ! empty( $paged_raw ) ? absint( $paged_raw ) : 1;
		$per_page  = 20;
		$offset    = ( $paged - 1 ) * $per_page;

		// Get filter parameters.
		$filter_class_raw  = filter_input( INPUT_GET, 'filter_class', FILTER_SANITIZE_NUMBER_INT );
		$filter_class      = ! empty( $filter_class_raw ) ? absint( $filter_class_raw ) : 0;
		$filter_member_raw = filter_input( INPUT_GET, 'filter_member', FILTER_SANITIZE_NUMBER_INT );
		$filter_member     = ! empty( $filter_member_raw ) ? absint( $filter_member_raw ) : 0;
		$filter_inst_raw   = filter_input( INPUT_GET, 'filter_instructor', FILTER_SANITIZE_NUMBER_INT );
		$filter_instructor = ! empty( $filter_inst_raw ) ? absint( $filter_inst_raw ) : 0;
		$filter_date_raw   = filter_input( INPUT_GET, 'filter_date', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$filter_date       = ! empty( $filter_date_raw ) ? sanitize_text_field( $filter_date_raw ) : '';

		// Get sort parameters.
		$orderby_raw = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$orderby     = ! empty( $orderby_raw ) ? sanitize_text_field( $orderby_raw ) : 'attendance_date';
		$order_raw   = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$order       = ! empty( $order_raw ) ? sanitize_text_field( $order_raw ) : 'DESC';
		$order       = in_array( strtoupper( $order ), array( 'ASC', 'DESC' ), true ) ? strtoupper( $order ) : 'DESC';

		// Validate orderby column.
		$allowed_orderby = array( 'attendance_date', 'member_id', 'class_id', 'marked_by', 'instructor' );
		if ( ! in_array( $orderby, $allowed_orderby, true ) ) {
			$orderby = 'attendance_date';
		}

		// Build query.
		$table_name              = $wpdb->prefix . 'macm_attendance';
		$class_instructors_table = $wpdb->prefix . 'macm_class_instructors';
		$instructors_table       = $wpdb->prefix . 'macm_instructors';
		$where                   = array( '1=1' );
		$where_values            = array();
		$need_instructor_join    = false;

		if ( $filter_class ) {
			$where[]        = 'a.class_id = %d';
			$where_values[] = $filter_class;
		}

		if ( $filter_member ) {
			$where[]        = 'a.member_id = %d';
			$where_values[] = $filter_member;
		}

		if ( $filter_instructor ) {
			$need_instructor_join = true;
			$where[]              = 'ci.instructor_id = %d';
			$where_values[]       = $filter_instructor;
		}

		if ( $filter_date ) {
			$where[]        = 'a.attendance_date = %s';
			$where_values[] = $filter_date;
		}

		// Execute queries using helper method with literal SQL strings.
		// This approach satisfies WordPress Plugin Check static analysis requirements.
		$query_result = self::execute_attendance_list_query(
			$wpdb,
			$table_name,
			$class_instructors_table,
			$instructors_table,
			$where,
			$where_values,
			$orderby,
			$order,
			$need_instructor_join,
			$per_page,
			$offset
		);

		$total_items = $query_result['total'];
		$records     = $query_result['records'];

		// Get all classes, members, and instructors for filters.
		$classes     = MACM_Class::get_all( true );
		$members     = MACM_Member::get_all();
		$instructors = MACM_Instructor::get_all( true );

		// Helper function for sortable column header.
		$get_sort_url = function ( $column ) use ( $filter_class, $filter_member, $filter_instructor, $filter_date, $orderby, $order ) {
			$new_order  = ( $orderby === $column && 'ASC' === $order ) ? 'DESC' : 'ASC';
			$url_params = array(
				'page'    => 'kcm-attendance',
				'tab'     => 'view',
				'orderby' => $column,
				'order'   => $new_order,
			);
			if ( $filter_class ) {
				$url_params['filter_class'] = $filter_class;
			}
			if ( $filter_member ) {
				$url_params['filter_member'] = $filter_member;
			}
			if ( $filter_instructor ) {
				$url_params['filter_instructor'] = $filter_instructor;
			}
			if ( $filter_date ) {
				$url_params['filter_date'] = $filter_date;
			}
			return add_query_arg( $url_params, admin_url( 'admin.php' ) );
		};

		$get_sort_indicator = function ( $column ) use ( $orderby, $order ) {
			if ( $orderby === $column ) {
				return 'ASC' === $order ? ' ▲' : ' ▼';
			}
			return '';
		};

		?>
		<div class="kcm-admin-section">
			<h2><?php esc_html_e( 'Recent Attendance Records', 'martial-arts-club-manager' ); ?></h2>

			<!-- Filters -->
			<div class="kcm-filters">
				<form method="get">
					<input type="hidden" name="page" value="kcm-attendance" />
					<input type="hidden" name="tab" value="view" />

					<label for="filter_class"><?php esc_html_e( 'Class:', 'martial-arts-club-manager' ); ?></label>
					<select name="filter_class" id="filter_class">
						<option value=""><?php esc_html_e( 'All Classes', 'martial-arts-club-manager' ); ?></option>
						<?php foreach ( $classes as $class ) : ?>
							<option value="<?php echo esc_attr( $class->id ); ?>" <?php selected( $filter_class, $class->id ); ?>>
								<?php echo esc_html( $class->class_name ); ?>
							</option>
						<?php endforeach; ?>
					</select>

					<label for="filter_member"><?php esc_html_e( 'Member:', 'martial-arts-club-manager' ); ?></label>
					<select name="filter_member" id="filter_member">
						<option value=""><?php esc_html_e( 'All Members', 'martial-arts-club-manager' ); ?></option>
						<?php foreach ( $members as $member ) : ?>
							<option value="<?php echo esc_attr( $member->id ); ?>" <?php selected( $filter_member, $member->id ); ?>>
								<?php echo esc_html( $member->full_name ); ?>
							</option>
						<?php endforeach; ?>
					</select>

					<label for="filter_instructor"><?php esc_html_e( 'Instructor:', 'martial-arts-club-manager' ); ?></label>
					<select name="filter_instructor" id="filter_instructor">
						<option value=""><?php esc_html_e( 'All Instructors', 'martial-arts-club-manager' ); ?></option>
						<?php foreach ( $instructors as $instructor ) : ?>
							<option value="<?php echo esc_attr( $instructor->id ); ?>" <?php selected( $filter_instructor, $instructor->id ); ?>>
								<?php echo esc_html( $instructor->full_name ); ?>
							</option>
						<?php endforeach; ?>
					</select>

					<label for="filter_date"><?php esc_html_e( 'Date:', 'martial-arts-club-manager' ); ?></label>
					<input type="date" name="filter_date" id="filter_date" value="<?php echo esc_attr( $filter_date ); ?>" />

					<button type="submit" class="button"><?php esc_html_e( 'Filter', 'martial-arts-club-manager' ); ?></button>
					<a href="?page=kcm-attendance&tab=view" class="button"><?php esc_html_e( 'Clear', 'martial-arts-club-manager' ); ?></a>
				</form>
			</div>

			<!-- Records Table -->
			<?php if ( $records ) : ?>
				<table class="wp-list-table widefat fixed striped" style="margin-top: 20px;">
					<thead>
						<tr>
							<th>
								<a href="<?php echo esc_url( $get_sort_url( 'attendance_date' ) ); ?>">
									<?php esc_html_e( 'Date', 'martial-arts-club-manager' ); ?><?php echo esc_html( $get_sort_indicator( 'attendance_date' ) ); ?>
								</a>
							</th>
							<th>
								<a href="<?php echo esc_url( $get_sort_url( 'member_id' ) ); ?>">
									<?php esc_html_e( 'Member', 'martial-arts-club-manager' ); ?><?php echo esc_html( $get_sort_indicator( 'member_id' ) ); ?>
								</a>
							</th>
							<th>
								<a href="<?php echo esc_url( $get_sort_url( 'class_id' ) ); ?>">
									<?php esc_html_e( 'Class', 'martial-arts-club-manager' ); ?><?php echo esc_html( $get_sort_indicator( 'class_id' ) ); ?>
								</a>
							</th>
							<th>
								<a href="<?php echo esc_url( $get_sort_url( 'instructor' ) ); ?>">
									<?php esc_html_e( 'Instructors', 'martial-arts-club-manager' ); ?><?php echo esc_html( $get_sort_indicator( 'instructor' ) ); ?>
								</a>
							</th>
							<th>
								<a href="<?php echo esc_url( $get_sort_url( 'marked_by' ) ); ?>">
									<?php esc_html_e( 'Marked By', 'martial-arts-club-manager' ); ?><?php echo esc_html( $get_sort_indicator( 'marked_by' ) ); ?>
								</a>
							</th>
							<th><?php esc_html_e( 'Notes', 'martial-arts-club-manager' ); ?></th>
						</tr>
					</thead>
					<tbody>
						<?php foreach ( $records as $record ) : ?>
							<?php
							$member    = MACM_Member::get( $record->member_id );
							$class     = MACM_Class::get( $record->class_id );
							$marked_by = get_userdata( $record->marked_by );

							// Use stored instructor_ids from attendance record (historical data).
							// No fallback - if NULL or empty, shows "None".
							$instructor_names_str = MACM_Attendance::get_instructor_names_from_ids( $record->instructor_ids );
							?>
							<tr>
								<td><?php echo esc_html( wp_date( 'M j, Y', strtotime( $record->attendance_date ) ) ); ?></td>
								<td><?php echo $member ? esc_html( $member->full_name ) : esc_html__( 'Unknown', 'martial-arts-club-manager' ); ?></td>
								<td><?php echo $class ? esc_html( $class->class_name ) : esc_html__( 'Unknown', 'martial-arts-club-manager' ); ?></td>
								<td>
									<?php if ( ! empty( $instructor_names_str ) ) : ?>
										<?php echo esc_html( $instructor_names_str ); ?>
									<?php else : ?>
										<span class="description"><?php esc_html_e( 'None', 'martial-arts-club-manager' ); ?></span>
									<?php endif; ?>
								</td>
								<td><?php echo $marked_by ? esc_html( $marked_by->display_name ) : esc_html__( 'Unknown', 'martial-arts-club-manager' ); ?></td>
								<td><?php echo $record->notes ? esc_html( wp_trim_words( $record->notes, 10 ) ) : '—'; ?></td>
							</tr>
						<?php endforeach; ?>
					</tbody>
				</table>

				<!-- Pagination -->
				<?php
				$total_pages = ceil( $total_items / $per_page );
				if ( $total_pages > 1 ) :
					?>
					<div class="tablenav">
						<div class="tablenav-pages">
							<?php
							echo wp_kses_post(
								paginate_links(
									array(
										'base'      => add_query_arg( 'paged', '%#%' ),
										'format'    => '',
										'prev_text' => __( '&laquo;', 'martial-arts-club-manager' ),
										'next_text' => __( '&raquo;', 'martial-arts-club-manager' ),
										'total'     => $total_pages,
										'current'   => $paged,
									)
								)
							);
							?>
						</div>
					</div>
				<?php endif; ?>

			<?php else : ?>
				<p><?php esc_html_e( 'No attendance records found.', 'martial-arts-club-manager' ); ?></p>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Render Generate Report tab
	 *
	 * @since 1.0.0
	 */
	private function render_report_tab() {
		$classes     = MACM_Class::get_all( true );
		$members     = MACM_Member::get_all();
		$instructors = MACM_Instructor::get_all( true );

		?>
		<div class="kcm-admin-section">
			<h2><?php esc_html_e( 'Generate Attendance Report', 'martial-arts-club-manager' ); ?></h2>

			<div id="kcm-report-message" class="notice" style="display: none;"></div>

			<form id="kcm-generate-report-form" method="post">
				<table class="form-table">
					<tr>
						<th scope="row">
							<label for="report_type"><?php esc_html_e( 'Report Type', 'martial-arts-club-manager' ); ?></label>
						</th>
						<td>
							<select name="report_type" id="report_type" required>
								<option value="by_class"><?php esc_html_e( 'By Class', 'martial-arts-club-manager' ); ?></option>
								<option value="by_member"><?php esc_html_e( 'By Member', 'martial-arts-club-manager' ); ?></option>
								<option value="by_instructor"><?php esc_html_e( 'By Instructor', 'martial-arts-club-manager' ); ?></option>
								<option value="by_date"><?php esc_html_e( 'By Date Range', 'martial-arts-club-manager' ); ?></option>
							</select>
						</td>
					</tr>

					<tr id="report_class_row">
						<th scope="row">
							<label for="report_class_id"><?php esc_html_e( 'Class', 'martial-arts-club-manager' ); ?></label>
						</th>
						<td>
							<select name="class_id" id="report_class_id">
								<option value=""><?php esc_html_e( '-- Select Class --', 'martial-arts-club-manager' ); ?></option>
								<option value="all"><?php esc_html_e( 'All Classes', 'martial-arts-club-manager' ); ?></option>
								<?php foreach ( $classes as $class ) : ?>
									<option value="<?php echo esc_attr( $class->id ); ?>">
										<?php echo esc_html( $class->class_name ); ?>
									</option>
								<?php endforeach; ?>
							</select>
						</td>
					</tr>

					<tr id="report_member_row" style="display: none;">
						<th scope="row">
							<label for="report_member_id"><?php esc_html_e( 'Member', 'martial-arts-club-manager' ); ?></label>
						</th>
						<td>
							<select name="member_id" id="report_member_id">
								<option value=""><?php esc_html_e( '-- Select Member --', 'martial-arts-club-manager' ); ?></option>
								<option value="all"><?php esc_html_e( 'All Members', 'martial-arts-club-manager' ); ?></option>
								<?php foreach ( $members as $member ) : ?>
									<option value="<?php echo esc_attr( $member->id ); ?>">
										<?php echo esc_html( $member->full_name ); ?>
									</option>
								<?php endforeach; ?>
							</select>
						</td>
					</tr>

					<tr id="report_instructor_row" style="display: none;">
						<th scope="row">
							<label for="report_instructor_id"><?php esc_html_e( 'Instructor', 'martial-arts-club-manager' ); ?></label>
						</th>
						<td>
							<select name="instructor_id" id="report_instructor_id">
								<option value=""><?php esc_html_e( '-- Select Instructor --', 'martial-arts-club-manager' ); ?></option>
								<option value="all"><?php esc_html_e( 'All Instructors', 'martial-arts-club-manager' ); ?></option>
								<?php foreach ( $instructors as $instructor ) : ?>
									<option value="<?php echo esc_attr( $instructor->id ); ?>">
										<?php echo esc_html( $instructor->full_name ); ?>
									</option>
								<?php endforeach; ?>
							</select>
						</td>
					</tr>

					<tr>
						<th scope="row">
							<label for="date_from"><?php esc_html_e( 'Date From', 'martial-arts-club-manager' ); ?></label>
						</th>
						<td>
							<input type="date" name="date_from" id="date_from" />
						</td>
					</tr>

					<tr>
						<th scope="row">
							<label for="date_to"><?php esc_html_e( 'Date To', 'martial-arts-club-manager' ); ?></label>
						</th>
						<td>
							<input type="date" name="date_to" id="date_to" value="<?php echo esc_attr( wp_date( 'Y-m-d' ) ); ?>" />
						</td>
					</tr>

					<tr>
						<th scope="row">
							<label><?php esc_html_e( 'Quick Filters', 'martial-arts-club-manager' ); ?></label>
						</th>
						<td>
							<button type="button" class="button button-small kcm-quick-date-filter" data-filter="today">
								<?php esc_html_e( 'Today', 'martial-arts-club-manager' ); ?>
							</button>
							<button type="button" class="button button-small kcm-quick-date-filter" data-filter="this_week">
								<?php esc_html_e( 'This Week', 'martial-arts-club-manager' ); ?>
							</button>
							<button type="button" class="button button-small kcm-quick-date-filter" data-filter="this_month">
								<?php esc_html_e( 'This Month', 'martial-arts-club-manager' ); ?>
							</button>
							<button type="button" class="button button-small kcm-quick-date-filter" data-filter="last_30_days">
								<?php esc_html_e( 'Last 30 Days', 'martial-arts-club-manager' ); ?>
							</button>
							<button type="button" class="button button-small" id="kcm-clear-dates">
								<?php esc_html_e( 'Clear', 'martial-arts-club-manager' ); ?>
							</button>
						</td>
					</tr>
				</table>

				<p class="submit">
					<button type="submit" class="button button-primary">
						<?php esc_html_e( 'Generate Report', 'martial-arts-club-manager' ); ?>
					</button>
					<button type="button" id="kcm-export-csv" class="button" style="display: none;">
						<span class="dashicons dashicons-download" style="margin-top: 3px;"></span>
						<?php esc_html_e( 'Export to CSV', 'martial-arts-club-manager' ); ?>
					</button>
				</p>
			</form>

			<div id="kcm-report-results" style="display: none;">
				<h3><?php esc_html_e( 'Report Results', 'martial-arts-club-manager' ); ?></h3>
				<div id="kcm-report-stats"></div>

				<div id="kcm-report-table"></div>

				<!-- Charts Section -->
				<div id="kcm-report-charts" style="display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 20px; margin: 20px 0;">
					<div style="background: #fff; padding: 20px; border: 1px solid #D1D5DB; border-radius: 4px;">
						<h4 style="margin-top: 0;"><?php esc_html_e( 'Attendance Trend', 'martial-arts-club-manager' ); ?></h4>
						<canvas id="kcm-attendance-trend-chart"></canvas>
					</div>
					<div style="background: #fff; padding: 20px; border: 1px solid #D1D5DB; border-radius: 4px;">
						<h4 style="margin-top: 0;"><?php esc_html_e( 'Member Participation', 'martial-arts-club-manager' ); ?></h4>
						<canvas id="kcm-member-participation-chart"></canvas>
					</div>
					<div style="background: #fff; padding: 20px; border: 1px solid #D1D5DB; border-radius: 4px;">
						<h4 style="margin-top: 0;"><?php esc_html_e( 'Instructor Activity', 'martial-arts-club-manager' ); ?></h4>
						<canvas id="kcm-instructor-activity-chart"></canvas>
					</div>
				</div>
			</div>
		</div>
		<?php
	}

	/**
	 * Render Clear Records tab
	 *
	 * @since 1.0.0
	 */
	private function render_clear_tab() {
		$classes = MACM_Class::get_all( false );
		$members = MACM_Member::get_all();

		?>
		<div class="kcm-admin-section">
			<h2><?php esc_html_e( 'Clear Attendance Records', 'martial-arts-club-manager' ); ?></h2>

			<div id="kcm-clear-message" class="notice" style="display: none;"></div>

			<div class="kcm-warning-box">
				<p><strong><?php esc_html_e( 'Warning:', 'martial-arts-club-manager' ); ?></strong> <?php esc_html_e( 'This action cannot be undone. Please be careful when clearing records.', 'martial-arts-club-manager' ); ?></p>
			</div>

			<!-- Clear by Date -->
			<div class="kcm-clear-option">
				<h3><?php esc_html_e( 'Clear by Date', 'martial-arts-club-manager' ); ?></h3>
				<form id="kcm-clear-by-date-form">
					<input type="hidden" name="clear_type" value="date" />
					<p>
						<label for="clear_date"><?php esc_html_e( 'Select Date:', 'martial-arts-club-manager' ); ?></label>
						<input type="date" name="date" id="clear_date" required />
					</p>
					<p>
						<button type="submit" class="button button-secondary">
							<?php esc_html_e( 'Clear Records for This Date', 'martial-arts-club-manager' ); ?>
						</button>
					</p>
				</form>
			</div>

			<hr />

			<!-- Clear by Member -->
			<div class="kcm-clear-option">
				<h3><?php esc_html_e( 'Clear by Member', 'martial-arts-club-manager' ); ?></h3>
				<form id="kcm-clear-by-member-form">
					<input type="hidden" name="clear_type" value="member" />
					<p>
						<label for="clear_member"><?php esc_html_e( 'Select Member:', 'martial-arts-club-manager' ); ?></label>
						<select name="member_id" id="clear_member" required>
							<option value=""><?php esc_html_e( '-- Select Member --', 'martial-arts-club-manager' ); ?></option>
							<?php foreach ( $members as $member ) : ?>
								<option value="<?php echo esc_attr( $member->id ); ?>">
									<?php echo esc_html( $member->full_name ); ?>
								</option>
							<?php endforeach; ?>
						</select>
					</p>
					<p>
						<button type="submit" class="button button-secondary">
							<?php esc_html_e( 'Clear All Records for This Member', 'martial-arts-club-manager' ); ?>
						</button>
					</p>
				</form>
			</div>

			<hr />

			<!-- Clear All -->
			<div class="kcm-clear-option">
				<h3><?php esc_html_e( 'Clear All Records', 'martial-arts-club-manager' ); ?></h3>
				<form id="kcm-clear-all-form">
					<input type="hidden" name="clear_type" value="all" />
					<p class="kcm-danger">
						<strong><?php esc_html_e( 'DANGER:', 'martial-arts-club-manager' ); ?></strong>
						<?php esc_html_e( 'This will delete ALL attendance records in the database. This cannot be undone!', 'martial-arts-club-manager' ); ?>
					</p>
					<p>
						<button type="submit" class="button button-secondary">
							<?php esc_html_e( 'Clear All Attendance Records', 'martial-arts-club-manager' ); ?>
						</button>
					</p>
				</form>
			</div>
		</div>
		<?php
	}

	/**
	 * Render Permissions tab
	 *
	 * @since 1.0.0
	 */
	private function render_permissions_tab() {
		// Get sorting parameters.
		$orderby_raw = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$orderby     = ! empty( $orderby_raw ) ? sanitize_text_field( $orderby_raw ) : 'display_name';
		$order_raw   = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$order       = ! empty( $order_raw ) ? sanitize_text_field( $order_raw ) : 'ASC';
		$order       = 'DESC' === strtoupper( $order ) ? 'DESC' : 'ASC';

		// Validate orderby.
		$allowed_orderby = array( 'display_name', 'user_email' );
		if ( ! in_array( $orderby, $allowed_orderby, true ) ) {
			$orderby = 'display_name';
		}

		// Get all users with sorting.
		$users_args = array(
			'orderby' => $orderby,
			'order'   => $order,
		);
		$users      = get_users( $users_args );

		// If sorting by role, we need to sort manually since get_users doesn't support role sorting.
		if ( ! empty( $orderby_raw ) && 'role' === $orderby_raw ) {
			usort(
				$users,
				function ( $a, $b ) use ( $order ) {
					$role_a = ! empty( $a->roles ) ? $a->roles[0] : '';
					$role_b = ! empty( $b->roles ) ? $b->roles[0] : '';
					$result = strcmp( $role_a, $role_b );
					return ( 'DESC' === $order ) ? -$result : $result;
				}
			);
			$orderby = 'role'; // Keep track for the sorted class.
		}

		// Helper function to generate sortable column header.
		$get_sortable_link = function ( $column, $label ) use ( $orderby, $order ) {
			$new_order    = 'ASC';
			$sorted_class = '';

			if ( $orderby === $column ) {
				$new_order    = ( 'ASC' === $order ) ? 'DESC' : 'ASC';
				$sorted_class = ( 'ASC' === $order ) ? 'sorted ascending' : 'sorted descending';
			}

			$url = add_query_arg(
				array(
					'page'    => 'kcm-attendance',
					'tab'     => 'permissions',
					'orderby' => $column,
					'order'   => $new_order,
				),
				admin_url( 'admin.php' )
			);

			return array(
				'link'  => '<a href="' . esc_url( $url ) . '">' . esc_html( $label ) . '</a>',
				'class' => $sorted_class,
			);
		};

		$user_header = $get_sortable_link( 'display_name', __( 'User', 'martial-arts-club-manager' ) );
		$role_header = $get_sortable_link( 'role', __( 'Role', 'martial-arts-club-manager' ) );

		?>
		<div class="kcm-admin-section">
			<h2><?php esc_html_e( 'Class Register Permissions', 'martial-arts-club-manager' ); ?></h2>

			<div id="kcm-permissions-message" class="notice" style="display: none;"></div>

			<p><?php esc_html_e( 'Grant or revoke permission to use the class register for marking attendance. Administrators always have access.', 'martial-arts-club-manager' ); ?></p>

			<form id="kcm-permissions-form" method="post" action="javascript:void(0);">
				<table class="wp-list-table widefat fixed striped">
					<thead>
						<tr>
							<th class="sortable <?php echo esc_attr( $user_header['class'] ); ?>"><?php echo wp_kses_post( $user_header['link'] ); ?></th>
							<th><?php esc_html_e( 'Email', 'martial-arts-club-manager' ); ?></th>
							<th class="sortable <?php echo esc_attr( $role_header['class'] ); ?>"><?php echo wp_kses_post( $role_header['link'] ); ?></th>
							<th><?php esc_html_e( 'Class Register Access', 'martial-arts-club-manager' ); ?></th>
						</tr>
					</thead>
					<tbody>
						<?php foreach ( $users as $user ) : ?>
							<?php
							$has_access = get_user_meta( $user->ID, 'macm_class_register_access', true );
							$is_admin   = in_array( 'administrator', $user->roles, true );
							?>
							<tr>
								<td><?php echo esc_html( $user->display_name ); ?></td>
								<td><?php echo esc_html( $user->user_email ); ?></td>
								<td><?php echo esc_html( implode( ', ', $user->roles ) ); ?></td>
								<td>
									<?php if ( $is_admin ) : ?>
										<span class="dashicons dashicons-yes" style="color: green;"></span>
										<?php esc_html_e( 'Admin (Always)', 'martial-arts-club-manager' ); ?>
									<?php else : ?>
										<input
											type="checkbox"
											name="user_permissions[]"
											value="<?php echo esc_attr( $user->ID ); ?>"
											<?php checked( $has_access, '1' ); ?>
										/>
										<label><?php esc_html_e( 'Grant Access', 'martial-arts-club-manager' ); ?></label>
									<?php endif; ?>
								</td>
							</tr>
						<?php endforeach; ?>
					</tbody>
				</table>

				<p class="submit">
					<button type="submit" class="button button-primary">
						<?php esc_html_e( 'Save Permissions', 'martial-arts-club-manager' ); ?>
					</button>
				</p>
			</form>
		</div>
		<?php
	}

	/**
	 * AJAX handler for generating reports
	 *
	 * @since 1.0.0
	 */
	public function ajax_generate_report() {
		check_ajax_referer( 'kcm-admin-attendance-nonce', 'nonce' );

		if ( ! current_user_can( 'manage_macm_attendance' ) ) {
			wp_send_json_error( array( 'message' => __( 'Permission denied.', 'martial-arts-club-manager' ) ) );
		}

		// Get parameters.
		$report_type   = isset( $_POST['report_type'] ) ? sanitize_text_field( wp_unslash( $_POST['report_type'] ) ) : '';
		$class_id      = isset( $_POST['class_id'] ) ? sanitize_text_field( wp_unslash( $_POST['class_id'] ) ) : '';
		$member_id     = isset( $_POST['member_id'] ) ? sanitize_text_field( wp_unslash( $_POST['member_id'] ) ) : '';
		$instructor_id = isset( $_POST['instructor_id'] ) ? sanitize_text_field( wp_unslash( $_POST['instructor_id'] ) ) : '';
		$date_from     = isset( $_POST['date_from'] ) ? sanitize_text_field( wp_unslash( $_POST['date_from'] ) ) : '';
		$date_to       = isset( $_POST['date_to'] ) ? sanitize_text_field( wp_unslash( $_POST['date_to'] ) ) : '';

		// Build report arguments.
		$args = array();

		if ( $date_from ) {
			$args['start_date'] = $date_from;
		}
		if ( $date_to ) {
			$args['end_date'] = $date_to;
		}

		if ( 'by_class' === $report_type ) {
			if ( 'all' === $class_id ) {
				// All classes - use date_range type.
				$args['type'] = 'date_range';
			} elseif ( absint( $class_id ) > 0 ) {
				// Specific class.
				$args['type']     = 'class';
				$args['class_id'] = absint( $class_id );
			}
		} elseif ( 'by_member' === $report_type ) {
			if ( 'all' === $member_id ) {
				// All members - use date_range type.
				$args['type'] = 'date_range';
			} elseif ( absint( $member_id ) > 0 ) {
				// Specific member.
				$args['type']      = 'member';
				$args['member_id'] = absint( $member_id );
			}
		} elseif ( 'by_instructor' === $report_type ) {
			if ( 'all' === $instructor_id ) {
				// All instructors - use date_range type.
				$args['type'] = 'date_range';
			} elseif ( absint( $instructor_id ) > 0 ) {
				// Specific instructor.
				$args['type']          = 'instructor';
				$args['instructor_id'] = absint( $instructor_id );
			}
		} elseif ( 'by_date' === $report_type ) {
			// For date-only reports, use date_range type.
			$args['type'] = 'date_range';
		}

		// Generate report.
		$report = MACM_Attendance::generate_report( $args );

		// Check for errors.
		if ( is_wp_error( $report ) ) {
			wp_send_json_error( array( 'message' => $report->get_error_message() ) );
		}

		if ( empty( $report['records'] ) ) {
			wp_send_json_error( array( 'message' => __( 'No records found for the specified criteria.', 'martial-arts-club-manager' ) ) );
		}

		// Format records for display.
		$formatted_records = array();
		foreach ( $report['records'] as $record ) {
			$member = MACM_Member::get( $record->member_id );
			$class  = MACM_Class::get( $record->class_id );

			// Use stored instructor_names from attendance record (set by generate_report).
			// No fallback - shows "None" if not set.
			$instructor_names_str = ! empty( $record->instructor_names ) ? $record->instructor_names : __( 'None', 'martial-arts-club-manager' );

			$formatted_records[] = array(
				'id'          => $record->id,
				'date'        => wp_date( 'M j, Y', strtotime( $record->attendance_date ) ),
				'member_name' => $member ? $member->full_name : 'Unknown',
				'class_name'  => $class ? $class->class_name : 'Unknown',
				'instructors' => $instructor_names_str,
				'notes'       => $record->notes,
				'created_at'  => wp_date( 'M j, Y g:i A', strtotime( $record->created_at ) ),
			);
		}

		wp_send_json_success(
			array(
				'records' => $formatted_records,
				'stats'   => $report['stats'],
			)
		);
	}

	/**
	 * AJAX handler for exporting CSV
	 *
	 * @since 1.0.0
	 */
	public function ajax_export_csv() {
		check_ajax_referer( 'kcm-admin-attendance-nonce', 'nonce' );

		if ( ! current_user_can( 'manage_macm_attendance' ) ) {
			wp_die( esc_html__( 'Permission denied.', 'martial-arts-club-manager' ) );
		}

		// Get parameters (same as report generation).
		$report_type   = isset( $_POST['report_type'] ) ? sanitize_text_field( wp_unslash( $_POST['report_type'] ) ) : '';
		$class_id      = isset( $_POST['class_id'] ) ? sanitize_text_field( wp_unslash( $_POST['class_id'] ) ) : '';
		$member_id     = isset( $_POST['member_id'] ) ? sanitize_text_field( wp_unslash( $_POST['member_id'] ) ) : '';
		$instructor_id = isset( $_POST['instructor_id'] ) ? sanitize_text_field( wp_unslash( $_POST['instructor_id'] ) ) : '';
		$date_from     = isset( $_POST['date_from'] ) ? sanitize_text_field( wp_unslash( $_POST['date_from'] ) ) : '';
		$date_to       = isset( $_POST['date_to'] ) ? sanitize_text_field( wp_unslash( $_POST['date_to'] ) ) : '';

		// Build report arguments.
		$args = array();

		if ( $date_from ) {
			$args['start_date'] = $date_from;
		}
		if ( $date_to ) {
			$args['end_date'] = $date_to;
		}

		if ( 'by_class' === $report_type ) {
			if ( 'all' === $class_id ) {
				// All classes - use date_range type.
				$args['type'] = 'date_range';
			} elseif ( absint( $class_id ) > 0 ) {
				// Specific class.
				$args['type']     = 'class';
				$args['class_id'] = absint( $class_id );
			}
		} elseif ( 'by_member' === $report_type ) {
			if ( 'all' === $member_id ) {
				// All members - use date_range type.
				$args['type'] = 'date_range';
			} elseif ( absint( $member_id ) > 0 ) {
				// Specific member.
				$args['type']      = 'member';
				$args['member_id'] = absint( $member_id );
			}
		} elseif ( 'by_instructor' === $report_type ) {
			if ( 'all' === $instructor_id ) {
				// All instructors - use date_range type.
				$args['type'] = 'date_range';
			} elseif ( absint( $instructor_id ) > 0 ) {
				// Specific instructor.
				$args['type']          = 'instructor';
				$args['instructor_id'] = absint( $instructor_id );
			}
		} elseif ( 'by_date' === $report_type ) {
			$args['type'] = 'date_range';
		}

		// Generate report.
		$report = MACM_Attendance::generate_report( $args );

		// Check for errors.
		if ( is_wp_error( $report ) ) {
			wp_die( esc_html( $report->get_error_message() ) );
		}

		if ( empty( $report['records'] ) ) {
			wp_die( esc_html__( 'No records found for export.', 'martial-arts-club-manager' ) );
		}

		// Export CSV - pass the full report with records.
		MACM_Attendance::export_csv( $report, 'attendance-report-' . wp_date( 'Y-m-d' ) . '.csv' );
		exit;
	}

	/**
	 * AJAX handler for clearing records
	 *
	 * @since 1.0.0
	 */
	public function ajax_clear_records() {
		check_ajax_referer( 'kcm-admin-attendance-nonce', 'nonce' );

		if ( ! current_user_can( 'manage_macm_attendance' ) ) {
			wp_send_json_error( array( 'message' => __( 'Permission denied.', 'martial-arts-club-manager' ) ) );
		}

		$clear_type = isset( $_POST['clear_type'] ) ? sanitize_text_field( wp_unslash( $_POST['clear_type'] ) ) : '';

		$args = array();

		switch ( $clear_type ) {
			case 'date':
				$date = isset( $_POST['date'] ) ? sanitize_text_field( wp_unslash( $_POST['date'] ) ) : '';
				if ( empty( $date ) ) {
					wp_send_json_error( array( 'message' => __( 'Date is required.', 'martial-arts-club-manager' ) ) );
				}
				$args['date'] = $date;
				break;

			case 'member':
				$member_id = isset( $_POST['member_id'] ) ? absint( wp_unslash( $_POST['member_id'] ) ) : 0;
				if ( ! $member_id ) {
					wp_send_json_error( array( 'message' => __( 'Member is required.', 'martial-arts-club-manager' ) ) );
				}
				$args['member_id'] = $member_id;
				break;

			case 'all':
				$args['all'] = true;
				break;

			default:
				wp_send_json_error( array( 'message' => __( 'Invalid clear type.', 'martial-arts-club-manager' ) ) );
		}

		// Delete records.
		$result = MACM_Attendance::delete( $args );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		}

		$message = '';
		if ( 'all' === $clear_type ) {
			$message = __( 'All attendance records have been cleared.', 'martial-arts-club-manager' );
		} elseif ( 'date' === $clear_type ) {
			/* translators: %s: date */
			$message = sprintf( __( 'All attendance records for %s have been cleared.', 'martial-arts-club-manager' ), $args['date'] );
		} elseif ( 'member' === $clear_type ) {
			$member  = MACM_Member::get( $args['member_id'] );
			$message = sprintf(
				/* translators: %s: member name */
				__( 'All attendance records for %s have been cleared.', 'martial-arts-club-manager' ),
				$member ? $member->full_name : 'this member'
			);
		}

		wp_send_json_success( array( 'message' => $message ) );
	}

	/**
	 * AJAX handler for saving permissions
	 *
	 * @since 1.0.0
	 */
	public function ajax_save_permissions() {
		check_ajax_referer( 'kcm-admin-attendance-nonce', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( array( 'message' => __( 'Permission denied.', 'martial-arts-club-manager' ) ) );
		}

		// Get user permissions.
		$user_permissions = isset( $_POST['user_permissions'] ) ? array_map( 'absint', wp_unslash( $_POST['user_permissions'] ) ) : array();

		// Get all users (excluding admins).
		$all_users = get_users( array( 'role__not_in' => array( 'administrator' ) ) );

		// Update permissions.
		foreach ( $all_users as $user ) {
			if ( in_array( $user->ID, $user_permissions, true ) ) {
				update_user_meta( $user->ID, 'macm_class_register_access', '1' );
			} else {
				delete_user_meta( $user->ID, 'macm_class_register_access' );
			}
		}

		wp_send_json_success( array( 'message' => __( 'Permissions saved successfully.', 'martial-arts-club-manager' ) ) );
	}

	/**
	 * Execute attendance list query with filter-based branching.
	 *
	 * Uses conditional execution with fully literal SQL strings to satisfy WordPress Plugin Check
	 * static analysis requirements. Each query branch uses a complete literal SQL string
	 * without any variable concatenation in the prepare() call.
	 *
	 * @since 1.0.290
	 * @since 1.0.294 Refactored to use filter-based branching for full WPCS compliance.
	 * @param wpdb   $wpdb                    Database object.
	 * @param string $table_name              Attendance table name.
	 * @param string $class_instructors_table Class instructors table name.
	 * @param string $instructors_table       Instructors table name.
	 * @param array  $where                   WHERE clause conditions (literal SQL with placeholders).
	 * @param array  $where_values            Values for WHERE placeholders.
	 * @param string $orderby                 Validated orderby column.
	 * @param string $order                   Validated order direction (ASC/DESC).
	 * @param bool   $need_instructor_join    Whether instructor join is needed.
	 * @param int    $per_page                Records per page.
	 * @param int    $query_offset            Query offset.
	 * @return array Array with 'total' and 'records' keys.
	 */
	private static function execute_attendance_list_query( $wpdb, $table_name, $class_instructors_table, $instructors_table, $where, $where_values, $orderby, $order, $need_instructor_join, $per_page, $query_offset ) {
		$use_join = $need_instructor_join || 'instructor' === $orderby;

		// Determine which filters are active based on $where array contents.
		// The $where array contains entries like '1=1', 'a.class_id = %d', 'a.member_id = %d', etc.
		$has_class_filter      = in_array( 'a.class_id = %d', $where, true );
		$has_member_filter     = in_array( 'a.member_id = %d', $where, true );
		$has_instructor_filter = in_array( 'ci.instructor_id = %d', $where, true );
		$has_date_filter       = in_array( 'a.attendance_date = %s', $where, true );

		// Build filter combination key for branching.
		$filter_key = ( $has_class_filter ? 'C' : '' ) .
						( $has_member_filter ? 'M' : '' ) .
						( $has_instructor_filter ? 'I' : '' ) .
						( $has_date_filter ? 'D' : '' );

		// Execute query based on filter combination and join requirement.
		if ( $use_join ) {
			$result = self::execute_join_query( $wpdb, $table_name, $class_instructors_table, $instructors_table, $filter_key, $where_values, $orderby, $order, $per_page, $query_offset );
		} else {
			$result = self::execute_simple_query( $wpdb, $table_name, $filter_key, $where_values, $orderby, $order, $per_page, $query_offset );
		}

		return $result;
	}

	/**
	 * Execute simple attendance query without instructor join.
	 *
	 * All SQL strings are literal constants - no variable interpolation.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb         Database object.
	 * @param string $table_name   Attendance table name.
	 * @param string $filter_key   Filter combination key (C=class, M=member, D=date).
	 * @param array  $where_values Values for WHERE placeholders.
	 * @param string $orderby      Validated orderby column.
	 * @param string $order        Validated order direction.
	 * @param int    $per_page     Records per page.
	 * @param int    $offset       Query offset.
	 * @return array Array with 'total' and 'records' keys.
	 */
	private static function execute_simple_query( $wpdb, $table_name, $filter_key, $where_values, $orderby, $order, $per_page, $offset ) {
		$total_items = 0;
		$records     = array();

		// Build sort key in format "column_DIRECTION" (e.g., "member_id_ASC", "attendance_date_DESC").
		$sort_key = $orderby . '_' . $order;

		// Execute count and select based on filter combination.
		// Each branch has fully literal SQL - no variable concatenation.
		switch ( $filter_key ) {
			case '': // No filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(*) FROM %i a WHERE 1=1', $table_name )
				);
				$records     = self::execute_simple_select_no_filter( $wpdb, $table_name, $sort_key, $per_page, $offset );
				break;

			case 'C': // Class filter only.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(*) FROM %i a WHERE 1=1 AND a.class_id = %d', $table_name, $where_values[0] )
				);
				$records     = self::execute_simple_select_class( $wpdb, $table_name, $sort_key, $where_values[0], $per_page, $offset );
				break;

			case 'M': // Member filter only.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(*) FROM %i a WHERE 1=1 AND a.member_id = %d', $table_name, $where_values[0] )
				);
				$records     = self::execute_simple_select_member( $wpdb, $table_name, $sort_key, $where_values[0], $per_page, $offset );
				break;

			case 'D': // Date filter only.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(*) FROM %i a WHERE 1=1 AND a.attendance_date = %s', $table_name, $where_values[0] )
				);
				$records     = self::execute_simple_select_date( $wpdb, $table_name, $sort_key, $where_values[0], $per_page, $offset );
				break;

			case 'CM': // Class + Member filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(*) FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d', $table_name, $where_values[0], $where_values[1] )
				);
				$records     = self::execute_simple_select_class_member( $wpdb, $table_name, $sort_key, $where_values[0], $where_values[1], $per_page, $offset );
				break;

			case 'CD': // Class + Date filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(*) FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s', $table_name, $where_values[0], $where_values[1] )
				);
				$records     = self::execute_simple_select_class_date( $wpdb, $table_name, $sort_key, $where_values[0], $where_values[1], $per_page, $offset );
				break;

			case 'MD': // Member + Date filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(*) FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s', $table_name, $where_values[0], $where_values[1] )
				);
				$records     = self::execute_simple_select_member_date( $wpdb, $table_name, $sort_key, $where_values[0], $where_values[1], $per_page, $offset );
				break;

			case 'CMD': // Class + Member + Date filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(*) FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s', $table_name, $where_values[0], $where_values[1], $where_values[2] )
				);
				$records     = self::execute_simple_select_class_member_date( $wpdb, $table_name, $sort_key, $where_values[0], $where_values[1], $where_values[2], $per_page, $offset );
				break;

			default:
				// Fallback for any unexpected combination.
				$total_items = 0;
				$records     = array();
				break;
		}

		return array(
			'total'   => $total_items,
			'records' => $records ? $records : array(),
		);
	}

	/**
	 * Execute simple select with no filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_simple_select_no_filter( $wpdb, $table_name, $sort_key, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $per_page, $offset ) );
		}
	}

	/**
	 * Execute simple select with class filter.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $class_id   Class ID.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_simple_select_class( $wpdb, $table_name, $sort_key, $class_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $class_id, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $class_id, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $class_id, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $class_id, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $class_id, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $class_id, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $class_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $class_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute simple select with member filter.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $member_id  Member ID.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_simple_select_member( $wpdb, $table_name, $sort_key, $member_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $member_id, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $member_id, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $member_id, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $member_id, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $member_id, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $member_id, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $member_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $member_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute simple select with date filter.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Table name.
	 * @param string $sort_key   Sort key.
	 * @param string $date       Date string.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_simple_select_date( $wpdb, $table_name, $sort_key, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.attendance_date = %s ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $date, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.attendance_date = %s ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $date, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.attendance_date = %s ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $date, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.attendance_date = %s ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $date, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.attendance_date = %s ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $date, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.attendance_date = %s ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $date, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.attendance_date = %s ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $date, $per_page, $offset ) );
		}
	}

	/**
	 * Execute simple select with class + member filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $class_id   Class ID.
	 * @param int    $member_id  Member ID.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_simple_select_class_member( $wpdb, $table_name, $sort_key, $class_id, $member_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute simple select with class + date filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $class_id   Class ID.
	 * @param string $date       Date string.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_simple_select_class_date( $wpdb, $table_name, $sort_key, $class_id, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $class_id, $date, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $class_id, $date, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $class_id, $date, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $class_id, $date, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $class_id, $date, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $class_id, $date, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $class_id, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $class_id, $date, $per_page, $offset ) );
		}
	}

	/**
	 * Execute simple select with member + date filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $member_id  Member ID.
	 * @param string $date       Date string.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_simple_select_member_date( $wpdb, $table_name, $sort_key, $member_id, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $member_id, $date, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $member_id, $date, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $member_id, $date, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $member_id, $date, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $member_id, $date, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $member_id, $date, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $member_id, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $member_id, $date, $per_page, $offset ) );
		}
	}

	/**
	 * Execute simple select with class + member + date filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $class_id   Class ID.
	 * @param int    $member_id  Member ID.
	 * @param string $date       Date string.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_simple_select_class_member_date( $wpdb, $table_name, $sort_key, $class_id, $member_id, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $date, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $date, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $date, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $date, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $date, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $date, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT a.* FROM %i a WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $class_id, $member_id, $date, $per_page, $offset ) );
		}
	}

	/**
	 * Execute attendance query with instructor join.
	 *
	 * All SQL strings are literal constants - no variable interpolation.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $filter_key    Filter combination key.
	 * @param array  $where_values  Values for WHERE placeholders.
	 * @param string $orderby       Validated orderby column.
	 * @param string $order         Validated order direction.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array Array with 'total' and 'records' keys.
	 */
	private static function execute_join_query( $wpdb, $table_name, $ci_table, $inst_table, $filter_key, $where_values, $orderby, $order, $per_page, $offset ) {
		$total_items = 0;
		$records     = array();

		// Build sort key in format "column_DIRECTION" (e.g., "member_id_ASC", "attendance_date_DESC").
		$sort_key = $orderby . '_' . $order;

		// Execute count and select based on filter combination with join.
		switch ( $filter_key ) {
			case '': // No filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1', $table_name, $ci_table, $inst_table )
				);
				$records     = self::execute_join_select_no_filter( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $per_page, $offset );
				break;

			case 'C': // Class filter only.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d', $table_name, $ci_table, $inst_table, $where_values[0] )
				);
				$records     = self::execute_join_select_class( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $per_page, $offset );
				break;

			case 'M': // Member filter only.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d', $table_name, $ci_table, $inst_table, $where_values[0] )
				);
				$records     = self::execute_join_select_member( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $per_page, $offset );
				break;

			case 'I': // Instructor filter only.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d', $table_name, $ci_table, $inst_table, $where_values[0] )
				);
				$records     = self::execute_join_select_instructor( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $per_page, $offset );
				break;

			case 'D': // Date filter only.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s', $table_name, $ci_table, $inst_table, $where_values[0] )
				);
				$records     = self::execute_join_select_date( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $per_page, $offset );
				break;

			case 'CI': // Class + Instructor filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d', $table_name, $ci_table, $inst_table, $where_values[0], $where_values[1] )
				);
				$records     = self::execute_join_select_class_instructor( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $where_values[1], $per_page, $offset );
				break;

			case 'MI': // Member + Instructor filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d AND ci.instructor_id = %d', $table_name, $ci_table, $inst_table, $where_values[0], $where_values[1] )
				);
				$records     = self::execute_join_select_member_instructor( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $where_values[1], $per_page, $offset );
				break;

			case 'ID': // Instructor + Date filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d AND a.attendance_date = %s', $table_name, $ci_table, $inst_table, $where_values[0], $where_values[1] )
				);
				$records     = self::execute_join_select_instructor_date( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $where_values[1], $per_page, $offset );
				break;

			case 'CMI': // Class + Member + Instructor filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND ci.instructor_id = %d', $table_name, $ci_table, $inst_table, $where_values[0], $where_values[1], $where_values[2] )
				);
				$records     = self::execute_join_select_class_member_instructor( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $where_values[1], $where_values[2], $per_page, $offset );
				break;

			case 'CID': // Class + Instructor + Date filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s', $table_name, $ci_table, $inst_table, $where_values[0], $where_values[1], $where_values[2] )
				);
				$records     = self::execute_join_select_class_instructor_date( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $where_values[1], $where_values[2], $per_page, $offset );
				break;

			case 'MID': // Member + Instructor + Date filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s', $table_name, $ci_table, $inst_table, $where_values[0], $where_values[1], $where_values[2] )
				);
				$records     = self::execute_join_select_member_instructor_date( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $where_values[1], $where_values[2], $per_page, $offset );
				break;

			case 'CMID': // All filters.
				$total_items = (int) $wpdb->get_var(
					$wpdb->prepare( 'SELECT COUNT(DISTINCT a.id) FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s', $table_name, $ci_table, $inst_table, $where_values[0], $where_values[1], $where_values[2], $where_values[3] )
				);
				$records     = self::execute_join_select_all_filters( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $where_values[0], $where_values[1], $where_values[2], $where_values[3], $per_page, $offset );
				break;

			default:
				// Fallback for non-instructor filter combinations - use simple query.
				return self::execute_simple_query( $wpdb, $table_name, $filter_key, $where_values, $orderby, $order, $per_page, $offset );
		}

		return array(
			'total'   => $total_items,
			'records' => $records ? $records : array(),
		);
	}

	/**
	 * Execute join select with no filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Attendance table name.
	 * @param string $ci_table   Class instructors table name.
	 * @param string $inst_table Instructors table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_no_filter( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with class filter.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Attendance table name.
	 * @param string $ci_table   Class instructors table name.
	 * @param string $inst_table Instructors table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $class_id   Class ID.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_class( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $class_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with member filter.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Attendance table name.
	 * @param string $ci_table   Class instructors table name.
	 * @param string $inst_table Instructors table name.
	 * @param string $sort_key   Sort key.
	 * @param int    $member_id  Member ID.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_member( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $member_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with instructor filter.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $sort_key      Sort key.
	 * @param int    $instructor_id Instructor ID.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_instructor( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $instructor_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with date filter.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb       Database object.
	 * @param string $table_name Attendance table name.
	 * @param string $ci_table   Class instructors table name.
	 * @param string $inst_table Instructors table name.
	 * @param string $sort_key   Sort key.
	 * @param string $date       Date string.
	 * @param int    $per_page   Records per page.
	 * @param int    $offset     Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_date( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			case 'member_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY a.member_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			case 'class_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY a.class_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			case 'class_id_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY a.class_id DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			case 'marked_by_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY a.marked_by ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			case 'marked_by_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY a.marked_by DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			case 'attendance_date_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY a.attendance_date ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $date, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with class + instructor filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $sort_key      Sort key.
	 * @param int    $class_id      Class ID.
	 * @param int    $instructor_id Instructor ID.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_class_instructor( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $class_id, $instructor_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'member_id_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d ORDER BY a.member_id ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $instructor_id, $per_page, $offset ) );
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $instructor_id, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $instructor_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $instructor_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with member + instructor filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $sort_key      Sort key.
	 * @param int    $member_id     Member ID.
	 * @param int    $instructor_id Instructor ID.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_member_instructor( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $member_id, $instructor_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d AND ci.instructor_id = %d ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $instructor_id, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d AND ci.instructor_id = %d ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $instructor_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d AND ci.instructor_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $instructor_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with instructor + date filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $sort_key      Sort key.
	 * @param int    $instructor_id Instructor ID.
	 * @param string $date          Date string.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_instructor_date( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $instructor_id, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $date, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $instructor_id, $date, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with class + member + instructor filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $sort_key      Sort key.
	 * @param int    $class_id      Class ID.
	 * @param int    $member_id     Member ID.
	 * @param int    $instructor_id Instructor ID.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_class_member_instructor( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $class_id, $member_id, $instructor_id, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND ci.instructor_id = %d ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $member_id, $instructor_id, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND ci.instructor_id = %d ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $member_id, $instructor_id, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND ci.instructor_id = %d ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $member_id, $instructor_id, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with class + instructor + date filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $sort_key      Sort key.
	 * @param int    $class_id      Class ID.
	 * @param int    $instructor_id Instructor ID.
	 * @param string $date          Date string.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_class_instructor_date( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $class_id, $instructor_id, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $instructor_id, $date, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $instructor_id, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $instructor_id, $date, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with member + instructor + date filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $sort_key      Sort key.
	 * @param int    $member_id     Member ID.
	 * @param int    $instructor_id Instructor ID.
	 * @param string $date          Date string.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_member_instructor_date( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $member_id, $instructor_id, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $instructor_id, $date, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $instructor_id, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.member_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $member_id, $instructor_id, $date, $per_page, $offset ) );
		}
	}

	/**
	 * Execute join select with all filters.
	 *
	 * @since 1.0.294
	 * @param wpdb   $wpdb          Database object.
	 * @param string $table_name    Attendance table name.
	 * @param string $ci_table      Class instructors table name.
	 * @param string $inst_table    Instructors table name.
	 * @param string $sort_key      Sort key.
	 * @param int    $class_id      Class ID.
	 * @param int    $member_id     Member ID.
	 * @param int    $instructor_id Instructor ID.
	 * @param string $date          Date string.
	 * @param int    $per_page      Records per page.
	 * @param int    $offset        Query offset.
	 * @return array|null Results.
	 */
	private static function execute_join_select_all_filters( $wpdb, $table_name, $ci_table, $inst_table, $sort_key, $class_id, $member_id, $instructor_id, $date, $per_page, $offset ) {
		switch ( $sort_key ) {
			case 'instructor_ASC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY i.full_name ASC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $member_id, $instructor_id, $date, $per_page, $offset ) );
			case 'instructor_DESC':
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY i.full_name DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $member_id, $instructor_id, $date, $per_page, $offset ) );
			default:
				return $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT a.* FROM %i a LEFT JOIN %i ci ON a.class_id = ci.class_id LEFT JOIN %i i ON ci.instructor_id = i.id WHERE 1=1 AND a.class_id = %d AND a.member_id = %d AND ci.instructor_id = %d AND a.attendance_date = %s ORDER BY a.attendance_date DESC LIMIT %d OFFSET %d', $table_name, $ci_table, $inst_table, $class_id, $member_id, $instructor_id, $date, $per_page, $offset ) );
		}
	}
}

// Initialize.
new MACM_Admin_Attendance();
