<?php
/**
 * Banks List Table class
 *
 * Extends WP_List_Table to display question banks.
 *
 * @package PressPrimer_Quiz
 * @subpackage Admin
 * @since 1.0.0
 */

// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Load WP_List_Table if not loaded
if ( ! class_exists( 'WP_List_Table' ) ) {
	require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}

/**
 * Banks List Table class
 *
 * @since 1.0.0
 */
class PressPrimer_Quiz_Banks_List_Table extends WP_List_Table {

	/**
	 * Constructor
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		parent::__construct(
			[
				'singular' => 'ppq-bank',
				'plural'   => 'pressprimer-quiz-banks',
				'ajax'     => false,
			]
		);
	}

	/**
	 * Get table columns
	 *
	 * @since 1.0.0
	 *
	 * @return array Column headers.
	 */
	public function get_columns() {
		$columns = [
			'cb'          => '<input type="checkbox" />',
			'name'        => __( 'Name', 'pressprimer-quiz' ),
			'description' => __( 'Description', 'pressprimer-quiz' ),
			'questions'   => __( 'Questions', 'pressprimer-quiz' ),
			'author'      => __( 'Author', 'pressprimer-quiz' ),
			'date'        => __( 'Date', 'pressprimer-quiz' ),
		];

		/**
		 * Filter the columns displayed in the banks list table.
		 *
		 * Premium addons can add custom columns to display additional data.
		 *
		 * Example usage:
		 * ```php
		 * add_filter( 'pressprimer_quiz_bank_list_columns', function( $columns ) {
		 *     // Add "Shared With" column before date
		 *     $date = $columns['date'];
		 *     unset( $columns['date'] );
		 *     $columns['shared_with'] = __( 'Shared With', 'pressprimer-quiz-groups' );
		 *     $columns['date'] = $date;
		 *     return $columns;
		 * } );
		 * ```
		 *
		 * @since 2.0.0
		 *
		 * @param array $columns Column headers array.
		 */
		return apply_filters( 'pressprimer_quiz_bank_list_columns', $columns );
	}

	/**
	 * Get sortable columns
	 *
	 * @since 1.0.0
	 *
	 * @return array Sortable columns.
	 */
	protected function get_sortable_columns() {
		return [
			'name'      => [ 'name', false ],
			'questions' => [ 'question_count', false ],
			'author'    => [ 'owner_id', false ],
			'date'      => [ 'created_at', true ],
		];
	}

	/**
	 * Get bulk actions
	 *
	 * @since 1.0.0
	 *
	 * @return array Bulk actions.
	 */
	protected function get_bulk_actions() {
		$actions = [];

		if ( current_user_can( 'pressprimer_quiz_manage_all' ) ) {
			$actions['delete'] = __( 'Delete', 'pressprimer-quiz' );
		}

		return $actions;
	}

	/**
	 * Prepare items for display
	 *
	 * @since 1.0.0
	 */
	public function prepare_items() {
		global $wpdb;

		// Process bulk actions
		$this->process_bulk_action();

		// Columns
		$columns               = $this->get_columns();
		$hidden                = get_hidden_columns( $this->screen );
		$sortable              = $this->get_sortable_columns();
		$this->_column_headers = [ $columns, $hidden, $sortable ];

		// Pagination
		$per_page     = $this->get_items_per_page( 'pressprimer_quiz_banks_per_page', 20 );
		$current_page = $this->get_pagenum();
		$offset       = ( $current_page - 1 ) * $per_page;

		// Build query
		$table = $wpdb->prefix . 'ppq_banks';

		$where        = [ '1=1' ];
		$where_values = [];

		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- List table filter params, not form processing
		// Search
		if ( isset( $_REQUEST['s'] ) && '' !== $_REQUEST['s'] ) {
			$search         = '%' . $wpdb->esc_like( sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) ) . '%';
			$where[]        = '(name LIKE %s OR description LIKE %s)';
			$where_values[] = $search;
			$where_values[] = $search;
		}

		// Owner filter (only show user's own if not admin)
		$get_author = isset( $_REQUEST['author'] ) ? absint( wp_unslash( $_REQUEST['author'] ) ) : 0;
		if ( ! current_user_can( 'pressprimer_quiz_manage_all' ) ) {
			$where[]        = 'owner_id = %d';
			$where_values[] = get_current_user_id();
		} elseif ( $get_author > 0 ) {
			$where[]        = 'owner_id = %d';
			$where_values[] = $get_author;
		}

		// No deleted banks
		$where[] = 'deleted_at IS NULL';

		$where_sql = implode( ' AND ', $where );

		// Ordering
		$orderby = isset( $_REQUEST['orderby'] ) && '' !== $_REQUEST['orderby'] ? sanitize_key( wp_unslash( $_REQUEST['orderby'] ) ) : 'created_at';
		$order   = isset( $_REQUEST['order'] ) && '' !== $_REQUEST['order'] ? sanitize_key( wp_unslash( $_REQUEST['order'] ) ) : 'DESC';
		// phpcs:enable WordPress.Security.NonceVerification.Recommended

		// Validate orderby
		$allowed_orderby = [ 'name', 'question_count', 'owner_id', 'created_at' ];
		if ( ! in_array( $orderby, $allowed_orderby, true ) ) {
			$orderby = 'created_at';
		}

		// Validate order
		$order = ( 'ASC' === strtoupper( $order ) ) ? 'ASC' : 'DESC';

		// Build ORDER BY with sanitize_sql_orderby
		$order_sql = sanitize_sql_orderby( "{$orderby} {$order}" );
		$order_sql = $order_sql ? "ORDER BY {$order_sql}" : 'ORDER BY created_at DESC';

		// Get total count
		if ( ! empty( $where_values ) ) {
			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name safely constructed from $wpdb->prefix
			$total_query = "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}";
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- List table pagination, not suitable for caching
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared with placeholders
			$total_items = absint( $wpdb->get_var( $wpdb->prepare( $total_query, $where_values ) ) );
		} else {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- List table pagination, not suitable for caching
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- No user input in query, table name safely constructed from $wpdb->prefix
			$total_items = absint( $wpdb->get_var( "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}" ) );
		}

		// Get items
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name and validated clauses safely constructed
		$items_query  = "SELECT * FROM {$table} WHERE {$where_sql} {$order_sql} LIMIT %d OFFSET %d";
		$query_values = array_merge( $where_values, [ $per_page, $offset ] );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- List table pagination, not suitable for caching
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Query is prepared with placeholders
		$results = $wpdb->get_results( $wpdb->prepare( $items_query, $query_values ) );

		// Convert to bank objects
		$this->items = [];
		if ( ! empty( $results ) && class_exists( 'PressPrimer_Quiz_Bank' ) ) {
			foreach ( $results as $row ) {
				$bank = new PressPrimer_Quiz_Bank();
				foreach ( $row as $key => $value ) {
					$bank->$key = $value;
				}
				$this->items[] = $bank;
			}
		}

		// Pagination
		$this->set_pagination_args(
			[
				'total_items' => $total_items,
				'per_page'    => $per_page,
				'total_pages' => ceil( $total_items / $per_page ),
			]
		);
	}

	/**
	 * Process bulk actions
	 *
	 * @since 1.0.0
	 */
	protected function process_bulk_action() {
		// Check for delete action
		if ( 'delete' !== $this->current_action() ) {
			return;
		}

		// Check capability
		if ( ! current_user_can( 'pressprimer_quiz_manage_all' ) ) {
			return;
		}

		// Get bank IDs
		$bank_ids = isset( $_REQUEST['bank'] ) ? array_map( 'absint', (array) $_REQUEST['bank'] ) : [];

		if ( empty( $bank_ids ) ) {
			return;
		}

		// Verify nonce
		if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'bulk-' . $this->_args['plural'] ) ) {
			wp_die( esc_html__( 'Security check failed.', 'pressprimer-quiz' ) );
		}

		// Delete banks
		if ( class_exists( 'PressPrimer_Quiz_Bank' ) ) {
			foreach ( $bank_ids as $bank_id ) {
				$bank = PressPrimer_Quiz_Bank::get( $bank_id );
				if ( $bank ) {
					$bank->delete();
				}
			}
		}

		// Redirect
		wp_safe_redirect(
			add_query_arg(
				[
					'page'    => 'pressprimer-quiz-banks',
					'message' => 'banks_deleted',
				],
				admin_url( 'admin.php' )
			)
		);
		exit;
	}

	/**
	 * Default column display
	 *
	 * @since 1.0.0
	 *
	 * @param object $item Bank object.
	 * @param string $column_name Column name.
	 * @return string Column content.
	 */
	protected function column_default( $item, $column_name ) {
		switch ( $column_name ) {
			case 'questions':
				return absint( $item->question_count );
			default:
				/**
				 * Fires for custom columns in the banks list table.
				 *
				 * Premium addons should hook here to output content for their custom columns.
				 *
				 * Example usage:
				 * ```php
				 * add_action( 'pressprimer_quiz_bank_list_column', function( $column_name, $bank ) {
				 *     if ( 'shared_with' === $column_name ) {
				 *         echo esc_html( $this->get_shared_groups( $bank->id ) );
				 *     }
				 * }, 10, 2 );
				 * ```
				 *
				 * @since 2.0.0
				 *
				 * @param string $column_name The column name/key.
				 * @param object $item        The bank object.
				 */
				do_action( 'pressprimer_quiz_bank_list_column', $column_name, $item );
				return '';
		}
	}

	/**
	 * Checkbox column
	 *
	 * @since 1.0.0
	 *
	 * @param object $item Bank object.
	 * @return string Checkbox HTML.
	 */
	protected function column_cb( $item ) {
		return sprintf( '<input type="checkbox" name="bank[]" value="%d" />', absint( $item->id ) );
	}

	/**
	 * Name column
	 *
	 * @since 1.0.0
	 *
	 * @param object $item Bank object.
	 * @return string Name column HTML.
	 */
	protected function column_name( $item ) {
		$view_url = add_query_arg(
			[
				'page'    => 'pressprimer-quiz-banks',
				'action'  => 'view',
				'bank_id' => $item->id,
			],
			admin_url( 'admin.php' )
		);

		$edit_url = add_query_arg(
			[
				'page'    => 'pressprimer-quiz-banks',
				'action'  => 'edit',
				'bank_id' => $item->id,
			],
			admin_url( 'admin.php' )
		);

		$delete_url = wp_nonce_url(
			add_query_arg(
				[
					'page'    => 'pressprimer-quiz-banks',
					'action'  => 'ppq_delete_bank',
					'bank_id' => $item->id,
				],
				admin_url( 'admin-post.php' )
			),
			'pressprimer_quiz_delete_bank_' . $item->id
		);

		$title = '<strong><a href="' . esc_url( $view_url ) . '">' . esc_html( $item->name ) . '</a></strong>';

		// Row actions.
		$actions         = [];
		$actions['view'] = '<a href="' . esc_url( $view_url ) . '">' . __( 'Questions', 'pressprimer-quiz' ) . '</a>';

		// Check ownership for edit/delete.
		if ( current_user_can( 'pressprimer_quiz_manage_all' ) || absint( $item->owner_id ) === get_current_user_id() ) {
			$actions['edit']   = '<a href="' . esc_url( $edit_url ) . '">' . __( 'Edit', 'pressprimer-quiz' ) . '</a>';
			$actions['delete'] = '<a href="' . esc_url( $delete_url ) . '" class="ppq-delete-confirm">' . __( 'Delete', 'pressprimer-quiz' ) . '</a>';
		}

		/**
		 * Filters the row actions displayed for each bank.
		 *
		 * Premium addons can add custom actions like Export or Share.
		 *
		 * Example usage:
		 * ```php
		 * add_filter( 'pressprimer_quiz_bank_row_actions', function( $actions, $bank ) {
		 *     $actions['export'] = '<a href="...">Export</a>';
		 *     return $actions;
		 * }, 10, 2 );
		 * ```
		 *
		 * @since 2.0.0
		 *
		 * @param array  $actions Row actions array.
		 * @param object $bank    Bank object.
		 */
		$actions = apply_filters( 'pressprimer_quiz_bank_row_actions', $actions, $item );

		return $title . $this->row_actions( $actions );
	}

	/**
	 * Description column
	 *
	 * @since 1.0.0
	 *
	 * @param object $item Bank object.
	 * @return string Description column HTML.
	 */
	protected function column_description( $item ) {
		if ( empty( $item->description ) ) {
			return '<span class="ppq-text-muted">' . esc_html__( '(no description)', 'pressprimer-quiz' ) . '</span>';
		}

		return esc_html( wp_trim_words( $item->description, 20 ) );
	}

	/**
	 * Author column
	 *
	 * @since 1.0.0
	 *
	 * @param object $item Bank object.
	 * @return string Author column HTML.
	 */
	protected function column_author( $item ) {
		$author = get_userdata( $item->owner_id );
		if ( ! $author ) {
			return '<span class="ppq-text-muted">' . esc_html__( '(unknown)', 'pressprimer-quiz' ) . '</span>';
		}

		// If admin, make it a filter link
		if ( current_user_can( 'pressprimer_quiz_manage_all' ) ) {
			$filter_url = add_query_arg(
				[
					'page'   => 'pressprimer-quiz-banks',
					'author' => $item->owner_id,
				],
				admin_url( 'admin.php' )
			);
			return '<a href="' . esc_url( $filter_url ) . '">' . esc_html( $author->display_name ) . '</a>';
		}

		return esc_html( $author->display_name );
	}

	/**
	 * Date column
	 *
	 * @since 1.0.0
	 *
	 * @param object $item Bank object.
	 * @return string Date column HTML.
	 */
	protected function column_date( $item ) {
		$timestamp = strtotime( $item->created_at );
		$date      = wp_date( get_option( 'date_format' ), $timestamp );
		$time      = wp_date( get_option( 'time_format' ), $timestamp );

		return sprintf(
			'%s<br><span class="ppq-text-muted">%s</span>',
			esc_html( $date ),
			esc_html( $time )
		);
	}

	/**
	 * Message when no items found
	 *
	 * @since 1.0.0
	 */
	public function no_items() {
		esc_html_e( 'No question banks found.', 'pressprimer-quiz' );
	}

	/**
	 * Display search box (overridden to always show, even with 0 items)
	 *
	 * @since 1.0.0
	 *
	 * @param string $text     The 'submit' button label.
	 * @param string $input_id ID attribute value for the search input field.
	 */
	public function search_box( $text, $input_id ) {
		$input_id = $input_id . '-search-input';

		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only hidden form fields
		if ( isset( $_REQUEST['orderby'] ) && '' !== $_REQUEST['orderby'] ) {
			echo '<input type="hidden" name="orderby" value="' . esc_attr( sanitize_key( wp_unslash( $_REQUEST['orderby'] ) ) ) . '" />';
		}
		if ( isset( $_REQUEST['order'] ) && '' !== $_REQUEST['order'] ) {
			echo '<input type="hidden" name="order" value="' . esc_attr( sanitize_key( wp_unslash( $_REQUEST['order'] ) ) ) . '" />';
		}
		// phpcs:enable WordPress.Security.NonceVerification.Recommended
		?>
		<p class="search-box">
			<label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo esc_html( $text ); ?>:</label>
			<input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
			<?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
		</p>
		<?php
	}
}
