<?php
/**
 * Custom List Table for displaying data in a table format in WordPress plugin.
 *
 * @link       https://personalizewp.com/
 * @since      3.2.0 Merged into PWP
 * @since      1.0.0
 *
 * @package    PersonalizeWP
 * @subpackage PersonalizeWP/Admin
 */

namespace PersonalizeWP\Admin;

use PersonalizeWP\Admin\PageSegments;

defined( 'ABSPATH' ) || exit; // Exit if accessed directly.

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

/**
 * Custom List Table for displaying data in a table format in WordPress plugin.
 */
class ListTableSegments extends \WP_List_Table {

	/**
	 * The screen name.
	 *
	 * @var string
	 */
	public $screen_id = 'pwp_segments';

	/**
	 * Default number of items per page
	 */
	const ITEMS_PER_PAGE = 20;

	/**
	 * Class constructor
	 */
	public function __construct() {
		parent::__construct(
			[
				'singular' => 'segment',
				'plural'   => 'segments',
				'ajax'     => false,
				'screen'   => $this->screen_id,
			]
		);
	}

	/**
	 * Retrieve the table columns.
	 *
	 * @return array Associative array of table columns with column name as key and column title as value.
	 */
	public function get_columns() {
		return [
			'name'          => __( 'Name', 'personalizewp' ),
			'type'          => __( 'Type', 'personalizewp' ),
			'conditions'    => __( 'Reason', 'personalizewp' ),
			'contact_count' => __( 'Visitor Profiles', 'personalizewp' ),
		];
	}

	/**
	 * Get sortable columns
	 *
	 * @return array
	 */
	protected function get_sortable_columns() {
		return [
			'name'          => 'name',
			'contact_count' => 'contact_count',
		];
	}

	/**
	 * Gets the name of the default primary column.
	 *
	 * @return string Name of the default primary column, in this case, 'name'.
	 */
	protected function get_default_primary_column_name() {
		return 'name';
	}

	/**
	 * Handles the segment type column output.
	 *
	 * @param array $item Segment
	 *
	 * @return string
	 */
	protected function column_type( $item ) {
		return esc_html( PageSegments::translate_type( $item['type'] ) );
	}

	/**
	 * Handles the Segment conditions column output.
	 *
	 * @param array $item Segment
	 *
	 * @return string
	 */
	protected function column_conditions( $item ) {
		return esc_html( PageSegments::translate_conditions( $item['conditions'], $item['type'] ) );
	}

	/**
	 * Generates and displays row action links.
	 *
	 * @param array  $item        Item being acted upon.
	 * @param string $column_name Current column name.
	 * @param string $primary     Primary column name.
	 *
	 * @return string Row actions output for Segment, or an empty string if the current column is not the primary column.
	 */
	protected function handle_row_actions( $item, $column_name, $primary ) {
		if ( $primary !== $column_name ) {
			return '';
		}

		$actions = array();

		$actions['view'] = sprintf(
			'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
			admin_url( PageSegments::$view_url . $item['ID'] ),
			/* translators: %s: Segment name. */
			esc_attr( sprintf( _x( 'View &#8220;%s&#8221;', 'Segment name', 'personalizewp' ), $item['name'] ) ),
			esc_html__( 'View', 'personalizewp' )
		);

		$actions['edit'] = sprintf(
			'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
			admin_url( PageSegments::$edit_url . $item['ID'] ),
			/* translators: %s: Segment name. */
			esc_attr( sprintf( _x( 'Edit &#8220;%s&#8221;', 'Segment name', 'personalizewp' ), $item['name'] ) ),
			esc_html__( 'Edit', 'personalizewp' )
		);

		if ( (int) $item['is_active'] ) {
			$actions['deactivate'] = sprintf(
				'<a href="%s" aria-label="%s" rel="bookmark" data-show-modal="#deactivateSegmentModal">%s</a>',
				wp_nonce_url( admin_url( PageSegments::$deactivate_url . $item['ID'] ), 'deactivate-segment-' . $item['ID'] ),
				/* translators: %s: Segment name. */
				esc_attr( sprintf( _x( 'Deactivate &#8220;%s&#8221;', 'Segment name', 'personalizewp' ), $item['name'] ) ),
				esc_html__( 'Deactivate', 'personalizewp' )
			);
		} else {
			$actions['status'] = sprintf(
				'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
				wp_nonce_url( admin_url( PageSegments::$activate_url . $item['ID'] ), 'activate-segment-' . $item['ID'] ),
				/* translators: %s: Segment name. */
				esc_attr( sprintf( _x( 'Activate &#8220;%s&#8221;', 'Segment name', 'personalizewp' ), $item['name'] ) ),
				esc_html__( 'Activate', 'personalizewp' )
			);
		}

		$actions['delete'] = sprintf(
			'<a class="trash" data-show-modal="#deleteSegmentModal" href="%s" aria-label="%s">%s</a>',
			wp_nonce_url( admin_url( PageSegments::$delete_url . $item['ID'] ), 'delete-segment-' . $item['ID'] ),
			/* translators: %s: Segment name. */
			esc_attr( sprintf( _x( 'Delete &#8220;%s&#8221;', 'Segment name', 'personalizewp' ), $item['name'] ) ),
			esc_html__( 'Delete', 'personalizewp' )
		);

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

	/**
	 * Default column renderer
	 *
	 * @param array  $item        Segment
	 * @param string $column_name List table column
	 *
	 * @return mixed
	 */
	protected function column_default( $item, $column_name ) {
		$col = '';
		switch ( $column_name ) {
			case 'name':
				$col = sprintf(
					'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
					admin_url( PageSegments::$view_url . $item['ID'] ),
					/* translators: %s: Segment name. */
					esc_attr( sprintf( _x( 'View &#8220;%s&#8221;', 'Segment name', 'personalizewp' ), $item['name'] ) ),
					esc_html( $item['name'] )
				);
				break;

			default:
				$col = esc_html( $item[ $column_name ] );
				break;
		}
		return $col;
	}

	/**
	 * Prepares the view status links.
	 *
	 * @return array
	 */
	protected function get_views() {

		$num_posts    = PageSegments::count_segments();
		$statuses     = array(
			'active'   => __( 'Active', 'personalizewp' ),
			'inactive' => __( 'Inactive', 'personalizewp' ),
		);
		$status_links = array();

		foreach ( $statuses as $status => $label ) {

			$status_args = array(
				'status' => $status,
			);

			$status_label = sprintf(
				'%s <span class="count">(%s)</span>',
				$label,
				number_format_i18n( $num_posts[ $status ] )
			);

			$default = empty( $_REQUEST['status'] ) && 'active' === $status; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: used as flag
			$current = ! empty( $_REQUEST['status'] ) && sanitize_text_field( wp_unslash( $_REQUEST['status'] ) ) === $status; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: used as flag

			$status_links[ $status ] = array(
				'url'     => esc_url( add_query_arg( $status_args, admin_url( PageSegments::$index_url ) ) ),
				'label'   => $status_label,
				'current' => $default || $current,
			);
		}

		return $this->get_views_links( $status_links );
	}

	/**
	 * Adds table navigation
	 *
	 * @param string $which Top or bottom of table
	 *
	 * @return void
	 */
	public function extra_tablenav( $which ) {
		if ( 'top' !== $which ) {
			return;
		}

		$views = $this->get_views();

		if ( ! empty( $views ) ) {
			echo "<ul class='subsubsub'>";
			foreach ( $views as $class => $view ) {
				$views[ $class ] = "<li class='$class'>$view";
			}
			echo wp_kses_post( implode( '</li>', $views ) . '</li>' );
			echo '</ul>';
		}
	}

	/**
	 * Generates the table navigation above or below the table
	 *
	 * Overridden to remove bulk actions and nonces
	 *
	 * @param string $which Top or bottom of table
	 *
	 * @return void
	 */
	protected function display_tablenav( $which ) {
		?>
	<div class="tablenav <?php echo esc_attr( $which ); ?>">

		<?php
		$this->extra_tablenav( $which );
		$this->pagination( $which );
		?>

	</div>
		<?php
	}

	/**
	 * Displays the search box.
	 *
	 * @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 ) {

		$page   = isset( $_REQUEST['page'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: nonce verification not required
		$search = isset( $_REQUEST['search'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: nonce verification not required
		?>
		<p class="search-box">
			<input type="hidden" name="page" value="<?php echo esc_attr( $page ); ?>" />
			<label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo esc_html( $text ); ?>:</label>
			<input type="text" class="search-icon" id="<?php echo esc_attr( $input_id ); ?>" name="search" value="<?php echo esc_attr( $search ); ?>"
				placeholder="<?php esc_html_e( 'Search name or type', 'personalizewp' ); ?>" />
			<input type="submit" class="secondary btn" value="<?php echo esc_html( $text ); ?>" />
		</p>
		<?php
	}

	/**
	 * Override parent views so we can display within the tablenav.
	 */
	public function views() {}

	/**
	 * Prepare the table items. Retrieves and sets the data for the table.
	 *
	 * @return void
	 */
	public function prepare_items() {
		global $wpdb;

		$where = '';
		// Status
		$status = ! empty( $_REQUEST['status'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['status'] ) ) : 'active'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: used as flag
		switch ( $status ) {
			case 'inactive':
				$where .= ' AND is_active = 0 ';
				break;

			case 'active':
			default:
				$where .= ' AND is_active = 1 ';
				break;
		}
		// Process Searching
		$search = isset( $_REQUEST['search'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['search'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: Verified elsewhere
		if ( $search ) {
			$search_cols    = array(
				'name',
				'type',
				'conditions',
			);
			$search_clauses = array();

			foreach ( $search_cols as $col ) {
				$search_clauses[] = $wpdb->prepare( '%i LIKE %s', $col, '%' . $wpdb->esc_like( $search ) . '%' );
			}
			$where .= ' AND (' . implode( ' OR ', $search_clauses ) . ')';
		}

		// Process Ordering
		$orderby = 'name'; // Default when not custom
		if ( ! empty( $_GET['orderby'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$_orderby = sanitize_text_field( wp_unslash( $_GET['orderby'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: verified elsewhere
			$parsed   = $this->parse_orderby( $_orderby );
			if ( $parsed ) {
				$orderby = $parsed;
			}
		}
		$order   = ! empty( $_GET['order'] ) ? sanitize_text_field( wp_unslash( $_GET['order'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: verified elsewhere
		$order   = $this->parse_order( $order );
		$orderby = 'ORDER BY ' . $orderby . ' ' . $order;

		// Process pagination
		$per_page = $this->get_items_per_page( "edit_{$this->screen_id}_per_page" );
		$page     = $this->get_pagenum();
		$offset   = absint( ( $page - 1 ) * $per_page ) . ', ';
		$limits   = 'LIMIT ' . $offset . $per_page;

		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reason: custom db table
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reason: inputs sanitised/validated above
		// Get the results from the options above
		$this->items = $wpdb->get_results(
			"SELECT SQL_CALC_FOUND_ROWS * FROM {$wpdb->prefix}pwp_segments WHERE 1=1 $where $orderby $limits", // nosemgrep: audit.php.wp.security.sqli.input-in-sinks -- Reason: inputs sanitised/validated above
			ARRAY_A
		);
		// phpcs:enable

		$total_items = (int) $wpdb->get_var( 'SELECT FOUND_ROWS()' );

		$this->set_pagination_args(
			array(
				'total_items' => $total_items,
				'per_page'    => $per_page,
			)
		);
	}

	/**
	 * Validates that the 'orderby' is valid
	 *
	 * @param string $orderby The 'orderby' query variable.
	 * @return string The sanitized 'orderby' query variable.
	 */
	protected function parse_orderby( $orderby ) {

		$orderby = strtolower( $orderby );

		// Used to filter values.
		$allowed_keys = array_keys( $this->get_sortable_columns() );

		if ( ! in_array( $orderby, $allowed_keys, true ) ) {
			return false;
		}

		return $orderby;
	}

	/**
	 * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
	 *
	 * @param string $order The 'order' query variable.
	 * @return string The sanitized 'order' query variable.
	 */
	protected function parse_order( $order ) {
		if ( ! is_string( $order ) || empty( $order ) ) {
			return 'ASC';
		}

		if ( 'ASC' === strtoupper( $order ) ) {
			return 'ASC';
		} else {
			return 'DESC';
		}
	}
}
