<?php
/**
 * Consent Records List Table
 *
 * @package AmbassadorHub\Consent\Admin
 */

namespace AmbassadorHub\Consent\Admin;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use AmbassadorHub\Consent\ConsentManager;
use AmbassadorHub\Consent\ConsentModule;

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

/**
 * List table for the Consent Records admin screen.
 */
final class Consent_Records_List_Table extends \WP_List_Table {

	/** Nonce action/field for the search/filter form — see Menu.php's wp_nonce_field() call. */
	private const NONCE_ACTION = 'ambh_consent_records_filter';
	private const NONCE_FIELD  = 'ambh_consent_records_nonce';

	/**
	 * Current search term.
	 *
	 * @var string
	 */
	private string $search_term;

	/**
	 * Subject type filter (e.g. 'wp_user', 'donor'). Empty string = no filter.
	 *
	 * @var string
	 */
	private string $subject_type;

	/**
	 * Subject ID filter. 0 = no filter.
	 *
	 * @var int
	 */
	private int $subject_id;

	/**
	 * Active status tab ('all', 'granted', 'revoked').
	 *
	 * @var string
	 */
	private string $current_tab;

	/**
	 * Base URL for tab and pagination links.
	 *
	 * @var string
	 */
	private string $base_url;

	/**
	 * Active purpose_slug filter. Empty string = no filter.
	 *
	 * @var string
	 */
	private string $filter_purpose = '';

	/**
	 * Active channel filter. Empty string = no filter.
	 *
	 * @var string
	 */
	private string $filter_channel = '';

	/**
	 * Active source filter. Empty string = no filter.
	 *
	 * @var string
	 */
	private string $filter_source = '';

	/**
	 * Status counts for view tabs (populated by prepare_items()).
	 *
	 * @var int
	 */
	private int $count_all     = 0;
	private int $count_granted = 0;
	private int $count_revoked = 0;

	/**
	 * Constructor.
	 *
	 * @param string $subject_type Optional subject type to filter by (e.g. 'wp_user', 'donor').
	 * @param int    $subject_id   Optional subject ID to filter by.
	 * @param string $current_tab  Active status tab ('all', 'granted', 'revoked').
	 * @param string $base_url     Base URL for tab and pagination links.
	 */
	public function __construct( string $subject_type = '', int $subject_id = 0, string $current_tab = 'all', string $base_url = '' ) {
		parent::__construct(
			array(
				'singular' => 'consent-record',
				'plural'   => 'consent-records',
				'ajax'     => false,
			)
		);

		// The search/filter fields below only ever narrow what this
		// already-capability-gated admin screen displays (no state is
		// written), so an invalid/missing/expired nonce fails open to the
		// unfiltered default view rather than wp_die()-ing — a bookmarked or
		// shared filtered URL simply resets to unfiltered instead of breaking.
		$nonce_valid = isset( $_GET[ self::NONCE_FIELD ] )
			&& wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET[ self::NONCE_FIELD ] ) ), self::NONCE_ACTION );

		$this->search_term    = ( $nonce_valid && isset( $_REQUEST['s'] ) ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : '';
		$this->subject_type   = $subject_type;
		$this->subject_id     = $subject_id;
		$this->current_tab    = $current_tab;
		$this->base_url       = $base_url;
		$this->filter_purpose = ( $nonce_valid && isset( $_GET['filter_purpose'] ) ) ? sanitize_text_field( wp_unslash( $_GET['filter_purpose'] ) ) : '';
		$this->filter_channel = ( $nonce_valid && isset( $_GET['filter_channel'] ) ) ? sanitize_text_field( wp_unslash( $_GET['filter_channel'] ) ) : '';
		$this->filter_source  = ( $nonce_valid && isset( $_GET['filter_source'] ) ) ? sanitize_text_field( wp_unslash( $_GET['filter_source'] ) ) : '';
	}

	/**
	 * Define the columns.
	 *
	 * @return array<string,string>
	 */
	public function get_columns(): array {
		return array(
			'subject'      => __( 'Subject', 'ambassadorhub' ),
			'user_email'   => __( 'Email', 'ambassadorhub' ),
			'purpose_slug' => __( 'Purpose', 'ambassadorhub' ),
			'channel'      => __( 'Channel', 'ambassadorhub' ),
			'status'       => __( 'Status', 'ambassadorhub' ),
			'date'         => __( 'Date', 'ambassadorhub' ),
			'source'       => __( 'Source', 'ambassadorhub' ),
			'consent_text' => __( 'Consent Text', 'ambassadorhub' ),
		);
	}

	/**
	 * Define sortable columns.
	 *
	 * @return array<string,array>
	 */
	protected function get_sortable_columns(): array {
		return array(
			'subject'      => array( 'subject_id', false ),
			'purpose_slug' => array( 'purpose_slug', false ),
			'date'         => array( 'consented_at', true ),
		);
	}

	/**
	 * Primary column name.
	 *
	 * @return string
	 */
	protected function get_primary_column_name(): string {
		return 'subject';
	}

	/**
	 * No bulk actions — consent records are read-only in this view.
	 *
	 * @return array<string,string>
	 */
	protected function get_bulk_actions(): array {
		return array();
	}

	/**
	 * Render purpose / channel / source filter dropdowns in the table navigation bar.
	 *
	 * @param string $which 'top' or 'bottom'.
	 * @return void
	 */
	protected function extra_tablenav( $which ): void {
		if ( 'top' !== $which ) {
			return;
		}

		$purposes = ConsentManager::get_distinct_values( 'purpose_slug' );
		$channels = ConsentManager::get_distinct_values( 'channel' );
		$sources  = ConsentManager::get_distinct_values( 'source' );

		if ( ! $purposes && ! $channels && ! $sources ) {
			return;
		}

		echo '<div class="alignleft actions">';

		if ( $purposes ) {
			echo '<select name="filter_purpose">';
			echo '<option value="">' . esc_html__( '— All Purposes —', 'ambassadorhub' ) . '</option>';
			foreach ( $purposes as $purpose ) {
				printf(
					'<option value="%s"%s>%s</option>',
					esc_attr( $purpose ),
					selected( $this->filter_purpose, $purpose, false ),
					esc_html( $purpose )
				);
			}
			echo '</select>';
		}

		if ( $channels ) {
			echo '<select name="filter_channel">';
			echo '<option value="">' . esc_html__( '— All Channels —', 'ambassadorhub' ) . '</option>';
			foreach ( $channels as $channel ) {
				printf(
					'<option value="%s"%s>%s</option>',
					esc_attr( $channel ),
					selected( $this->filter_channel, $channel, false ),
					esc_html( $channel )
				);
			}
			echo '</select>';
		}

		if ( $sources ) {
			echo '<select name="filter_source">';
			echo '<option value="">' . esc_html__( '— All Sources —', 'ambassadorhub' ) . '</option>';
			foreach ( $sources as $source ) {
				printf(
					'<option value="%s"%s>%s</option>',
					esc_attr( $source ),
					selected( $this->filter_source, $source, false ),
					esc_html( $source )
				);
			}
			echo '</select>';
		}

		submit_button( __( 'Filter', 'ambassadorhub' ), 'button', 'filter_action', false );
		echo '</div>';
	}

	/**
	 * Fetch, enrich, and paginate consent records.
	 *
	 * @return void
	 */
	public function prepare_items(): void {
		$per_page        = $this->get_items_per_page( 'ambh_consent_per_page', 20 );
		$allowed_orderby = array( 'subject_id', 'purpose_slug', 'consented_at', 'withdrawn_at' );

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$raw_orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : 'consented_at';
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$raw_order = isset( $_GET['order'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_GET['order'] ) ) ) : 'DESC';

		$orderby      = in_array( $raw_orderby, $allowed_orderby, true ) ? $raw_orderby : 'consented_at';
		$order        = 'ASC' === $raw_order ? 'ASC' : 'DESC';
		$current_page = $this->get_pagenum();
		$offset       = ( $current_page - 1 ) * $per_page;

		// Status counts — not affected by search or current tab (WP convention). One aggregated query.
		$tab_counts          = ConsentManager::count_tabs( $this->subject_type, $this->subject_id, $this->filter_purpose, $this->filter_channel, $this->filter_source );
		$this->count_all     = $tab_counts['all'];
		$this->count_granted = $tab_counts['granted'];
		$this->count_revoked = $tab_counts['revoked'];

		// Derive pagination total from tab counts when no search is active; otherwise query.
		if ( '' === $this->search_term ) {
			$total_items = $tab_counts[ $this->current_tab ] ?? $tab_counts['all'];
		} else {
			$total_items = ConsentManager::count( $this->search_term, $this->subject_type, $this->subject_id, $this->current_tab, $this->filter_purpose, $this->filter_channel, $this->filter_source );
		}

		$rows = ConsentManager::get_all(
			array(
				'search'       => $this->search_term,
				'limit'        => $per_page,
				'offset'       => $offset,
				'orderby'      => $orderby,
				'order'        => $order,
				'subject_type' => $this->subject_type,
				'subject_id'   => $this->subject_id,
				'status'       => $this->current_tab,
				'purpose_slug' => $this->filter_purpose,
				'channel'      => $this->filter_channel,
				'source'       => $this->filter_source,
			)
		);

		// Enrich each row with the resolved email for all subject types.
		$this->items = array();
		foreach ( $rows as $row ) {
			$user_email    = ConsentModule::get_subject_email( $row->subject_type, (int) $row->subject_id ) ?? '';
			$this->items[] = (object) array(
				'id'           => $row->id,
				'subject_type' => $row->subject_type,
				'subject_id'   => (int) $row->subject_id,
				'user_email'   => $user_email,
				'purpose_slug' => $row->purpose_slug,
				'channel'      => $row->channel,
				'status'       => $row->withdrawn_at ? 'revoked' : 'granted',
				'date'         => $row->consented_at,
				'source'       => $row->source,
				'consent_text' => $row->consent_text,
			);
		}

		$this->set_pagination_args(
			array(
				'total_items' => $total_items,
				'per_page'    => $per_page,
				'total_pages' => (int) ceil( $total_items / $per_page ),
			)
		);

		$this->_column_headers = array(
			$this->get_columns(),
			$this->get_hidden_columns(),
			$this->get_sortable_columns(),
			$this->get_primary_column_name(),
		);
	}

	/**
	 * Filter tab links (All / Granted / Revoked).
	 *
	 * Counts are populated by prepare_items() and are not affected by the current
	 * tab or search term (matching WP convention).
	 *
	 * @return array<string,string>
	 */
	protected function get_views(): array {
		$labels = array(
			'all'     => __( 'All', 'ambassadorhub' ),
			'granted' => __( 'Granted', 'ambassadorhub' ),
			'revoked' => __( 'Revoked', 'ambassadorhub' ),
		);

		$counts = array(
			'all'     => $this->count_all,
			'granted' => $this->count_granted,
			'revoked' => $this->count_revoked,
		);

		$views = array();
		foreach ( $labels as $slug => $label ) {
			$url   = 'all' === $slug
				? remove_query_arg( 'tab', $this->base_url )
				: add_query_arg( 'tab', $slug, $this->base_url );
			$class = $slug === $this->current_tab ? ' class="current"' : '';

			$views[ $slug ] = '<a href="' . esc_url( $url ) . '"' . $class . '>'
				. esc_html( $label )
				. ' <span class="count">(' . absint( $counts[ $slug ] ) . ')</span></a>';
		}

		return $views;
	}

	/**
	 * Return the currently hidden columns for this screen.
	 *
	 * @return string[]
	 */
	public function get_hidden_columns(): array {
		$screen = get_current_screen();
		if ( ! $screen ) {
			return array();
		}
		$hidden = get_user_meta( get_current_user_id(), 'manage' . $screen->id . 'columnshidden', true );
		return is_array( $hidden ) ? $hidden : array();
	}

	/**
	 * Subject column (primary) — type + ID.
	 *
	 * @param object $item Enriched consent record.
	 * @return string
	 */
	public function column_subject( $item ): string {
		return '<strong class="row-title"><span>' . esc_html( $item->subject_type . ' #' . $item->subject_id ) . '</span></strong>';
	}

	/**
	 * Status column.
	 *
	 * @param object $item Enriched consent record.
	 * @return string
	 */
	public function column_status( $item ): string {
		$color = 'granted' === $item->status ? 'green' : 'grey';
		return '<span class="ambh-status-pill ambh-status-pill--' . esc_attr( $color ) . '">' . esc_html( ucfirst( $item->status ) ) . '</span>';
	}

	/**
	 * Date column — date + time.
	 *
	 * @param object $item Enriched consent record.
	 * @return string
	 */
	public function column_date( $item ): string {
		return esc_html( date_i18n( get_option( 'date_format' ) . ' H:i', strtotime( $item->date ) ) );
	}

	/**
	 * Fallback column renderer.
	 *
	 * @param object $item        Enriched consent record.
	 * @param string $column_name Column slug.
	 * @return string
	 */
	public function column_default( $item, $column_name ): string {
		switch ( $column_name ) {
			case 'user_email':
				return esc_html( $item->user_email );
			case 'purpose_slug':
				return esc_html( $item->purpose_slug );
			case 'channel':
				return esc_html( $item->channel );
			case 'consent_text':
				if ( empty( $item->consent_text ) ) {
					return '—';
				}
				$preview = mb_strlen( $item->consent_text ) > 50
					? mb_substr( $item->consent_text, 0, 50 ) . '…'
					: $item->consent_text;
				return esc_html( $preview )
					. '<span class="ambh-consent-text-icon" tabindex="0">'
					. '<span class="dashicons dashicons-info" aria-hidden="true"></span>'
					. '<span class="ambh-consent-text-tooltip">' . esc_html( $item->consent_text ) . '</span>'
					. '</span>';
			case 'source':
				return esc_html( $item->source );
		}
		return '';
	}
}
