<?php
/**
 * Points list table view.
 *
 * @package Awesomemotive\Rewardswp\Views\Admin\ListTables
 * @since 1.0.0
 */

namespace Awesomemotive\Rewardswp\Views\Admin\ListTables;

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

use Awesomemotive\Rewardswp\Factories\IntegrationsFactory;
use Awesomemotive\Rewardswp\Interfaces\IntegrationWithCouponInterface;
use Awesomemotive\Rewardswp\Interfaces\ViewInterface;
use Awesomemotive\Rewardswp\Models\Point;
use Awesomemotive\Rewardswp\Services\PointsService;
use Awesomemotive\Rewardswp\Services\MembersService;
use Exception;
use WP_List_Table;
use function Awesomemotive\Rewardswp\Helpers\get_member;
use function Awesomemotive\Rewardswp\Helpers\get_human_readable_date;
use function Awesomemotive\Rewardswp\Helpers\get_activity_type_label;

class PointsListTableView extends WP_List_Table implements ViewInterface {
	/**
	 * The gravatar size.
	 *
	 * @since 1.0.0
	 *
	 * @var int
	 */
	const GRAVATAR_SIZE = 36;

	/**
	 * Points service instance.
	 *
	 * @since 1.0.0
	 *
	 * @var PointsService
	 */
	private PointsService $points_service;

	/**
	 * Integrations factory instance.
	 *
	 * @since 1.0.0
	 *
	 * @var IntegrationsFactory
	 */
	private IntegrationsFactory $integration_factory;

	/**
	 * Members service instance.
	 *
	 * @since 1.0.0
	 *
	 * @var MembersService
	 */
	private MembersService $members_service;

	/**
	 * Constructor.
	 *
	 * @since 1.0.0
	 *
	 * @param PointsService       $points_service
	 * @param IntegrationsFactory $integration_factory
	 * @param MembersService      $members_service
	 */
	public function __construct( PointsService $points_service, IntegrationsFactory $integration_factory, MembersService $members_service ) {
		$this->points_service      = $points_service;
		$this->integration_factory = $integration_factory;
		$this->members_service     = $members_service;

		parent::__construct(
			[
				'singular' => 'point',
				'plural'   => 'points',
				'ajax'     => false,
			]
		);
	}

	/**
	 * Get columns.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_columns(): array {
		return [
			'member'        => esc_html__( 'Member', 'rewardswp' ),
			'points'        => esc_html__( 'Points', 'rewardswp' ),
			'activity_type' => esc_html__( 'Activity Type', 'rewardswp' ),
			'order_total'   => esc_html__( 'Order Total', 'rewardswp' ),
			'created_at'    => esc_html__( 'Date', 'rewardswp' ),
		];
	}

	/**
	 * Get sortable columns.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_sortable_columns(): array {
		return [
			'points'     => [ 'points', false ],
			'created_at' => [ 'created_at', false ],
		];
	}

	/**
	 * Set hidden columns.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_hidden_columns(): array {
		return get_hidden_columns( $this->screen );
	}

	/**
	 * Get bulk actions.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_bulk_actions(): array {
		return [];
	}

	/**
	 * Renders the message to be displayed when there are no points.
	 *
	 * @since 1.0.0
	 */
	function no_items() {
		esc_html_e( 'No points activity found.', 'rewardswp' );
	}

	/**
	 * Parse query arguments for points search and filtering.
	 *
	 * @since 1.0.0
	 *
	 * @return array Parsed query arguments ready for PointsService.
	 */
	private function parse_query_args(): array {
		$args = array_filter(
			[
				'order'       => sanitize_text_field( filter_input( INPUT_GET, 'order' ) ?? 'desc' ),
				'orderby'     => sanitize_text_field( filter_input( INPUT_GET, 'orderby' ) ?? 'id' ),
				'type'        => sanitize_text_field( filter_input( INPUT_GET, 'type' ) ?? '' ),
				'status'      => sanitize_text_field( filter_input( INPUT_GET, 'status' ) ?? '' ),
				'source_type' => sanitize_text_field( filter_input( INPUT_GET, 'source_type' ) ?? '' ),
			]
		);

		// Handle search query by member
		$search_term = sanitize_text_field( filter_input( INPUT_GET, 's' ) ?? '' );
		if ( ! empty( $search_term ) ) {
			// Search for members using MembersService with like_query
			$members = $this->members_service->get_members(
				[
					'like_query' => [
						'search_term' => $search_term,
						'columns'     => [
							'first_name',
							'last_name',
							'email',
							"CONCAT(first_name, ' ', last_name)",
						],
						'compare'     => 'OR',
					],
				],
				9999, // Get all matching members
				1
			);

			if ( ! empty( $members ) ) {
				$args['member_id'] = array_map(
					function ( $member ) {
						return $member->id;
					},
					$members
				);
			} else {
				// No members found, set impossible condition to return empty results
				$args['member_id'] = [ 0 ];
			}
		}

		return $args;
	}

	/**
	 * Prepare items.
	 *
	 * @since 1.0.0
	 *
	 * @param int $per_page The number of items per page. Default 20.
	 *
	 * @return void
	 */
	public function prepare_items( int $per_page = 20 ): void {
		$this->_column_headers = [
			$this->get_columns(),
			$this->get_hidden_columns(),
			$this->get_sortable_columns(),
		];

		$current_page = $this->get_pagenum();
		$query_args   = $this->parse_query_args();

		// Get total count for pagination
		$total_count = $this->points_service->count_points_entries( $query_args );

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

		// Get the points data
		$this->items = $this->points_service->get_points_entries(
			$query_args,
			$per_page,
			$current_page
		);
	}

	/**
	 * Render the template.
	 *
	 * @since 1.0.0
	 *
	 * @param array $args {
	 *     @type string $page_title     The page title. Default to the current admin page title.
	 *     @type string $items_per_page The page content. Default ''.
	 * }
	 *
	 * @return void
	 */
	public function render( array $args = [] ): void {
		$args = wp_parse_args(
			$args,
			[
				'page_title'     => esc_html__( 'Points', 'rewardswp' ),
				'items_per_page' => $this->get_items_per_page( 'points_per_page' ),
			]
		);

		$this->prepare_items( $args['items_per_page'] );

		?>

		<div class="mt-6 mr-6 mb-0 ml-[2px] px-3">

			<div class="rewardswp-container sr-only">
				<div class="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
					<h1 class="text-2xl text-slate-900"><?php echo esc_html( $args['page_title'] ); ?></h1>
				</div>
			</div>

			<div class="mb-0" style="overflow: hidden;">
				<?php
				$this->views();
				?>
			</div>

			<form method="get">
				<?php
				// Preserve current page and other query parameters
				// This is an admin GET form for navigation purposes only, nonce verification is not required.
				// phpcs:ignore WordPress.Security.NonceVerification.Recommended
				foreach ( $_GET as $key => $value ) {
					if ( 'page' === $key || 'paged' === $key || 's' === $key ) {
						continue; // These will be handled by search_box() and pagination
					}
					// Sanitize the value for output in hidden field.
					$sanitized_key   = sanitize_text_field( wp_unslash( $key ) );
					$sanitized_value = sanitize_text_field( wp_unslash( $value ) );
					echo '<input type="hidden" name="' . esc_attr( $sanitized_key ) . '" value="' . esc_attr( $sanitized_value ) . '" />';
				}
				// phpcs:ignore WordPress.Security.NonceVerification.Recommended
				$current_page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
				echo '<input type="hidden" name="page" value="' . esc_attr( $current_page ) . '" />';
				?>
				<?php $this->search_box( esc_html__( 'Search Points', 'rewardswp' ), 'search_id' ); ?>
				<?php $this->display_tablenav( 'top' ); ?>

				<table class="wp-list-table <?php echo esc_attr( implode( ' ', $this->get_table_classes() ) ); ?>">

					<thead>
					<tr>
						<?php $this->print_column_headers(); ?>
					</tr>
					</thead>

					<tbody id="the-list" <?php echo $this->has_items() ? 'data-wp-lists="list:' . esc_attr( $this->_args['singular'] ) . '"' : ''; ?>>
					<?php $this->display_rows_or_placeholder(); ?>
					</tbody>

					<tfoot>
					<tr>
						<?php $this->print_column_headers( false ); ?>
					</tr>
					</tfoot>

				</table>

				<?php $this->display_tablenav( 'bottom' ); ?>

			</form>
		</div>

		<?php
	}

	/**
	 * Column default.
	 *
	 * @param Point  $item        Point object.
	 * @param string $column_name Column name.
	 *
	 * @return string
	 *
	 * @throws Exception
	 */
	public function column_default( $item, $column_name ): string {
		switch ( $column_name ) {
			case 'member':
				return $this->column_member( $item );
			case 'points':
				return $this->column_points( $item );
			case 'activity_type':
				// Expiration audit entries have type=spent but should show "Expired".
				if ( 'points_expiration' === $item->source_type ) {
					return esc_html__( 'Expired', 'rewardswp' );
				}
				return get_activity_type_label( $item->type );
			case 'order_total':
				return $this->column_order_total( $item );
			case 'created_at':
				return get_human_readable_date( $item->created_at, true );
			default:
				return $item->$column_name;
		}
	}

	/**
	 * Member column.
	 *
	 * @since 1.0.0
	 *
	 * @param Point $item Point object.
	 *
	 * @return string
	 */
	public function column_member( Point $item ): string {
		$member = get_member( $item->member_id );

		if ( ! $member ) {
			return esc_html__( 'N/A', 'rewardswp' );
		}

		$member_detail_url = esc_url(
			add_query_arg(
				[
					'page'      => 'rewardswp-members',
					'action'    => 'view_member',
					'member_id' => $member->id,
				],
				admin_url( 'admin.php' )
			)
		);

		return sprintf(
			'<div class="group block shrink-0">
				<div class="flex items-center">
					<a href="%s" class="shrink-0">
						<img class="inline-block size-9 rounded-full hover:opacity-80 transition-opacity" src="%s" alt="%s">
					</a>
					<div class="ml-3">
						<a href="%s" class="text-sm font-medium text-[#2271b1] hover:text-[#135e96]">
							%s
						</a>
					</div>
				</div>
			</div>',
			$member_detail_url,
			esc_url_raw(
				get_avatar_url(
					$member->email,
					[
						'size' => self::GRAVATAR_SIZE,
					]
				)
			),
			esc_attr( $member->get_full_name() ),
			$member_detail_url,
			esc_html( $member->get_full_name() )
		);
	}

	/**
	 * Points column.
	 *
	 * @since 1.0.0
	 *
	 * @param Point $item Point object.
	 *
	 * @return string
	 */
	public function column_points( Point $item ): string {
		$status = $item->status;

		// Pending: grey text + "(Pending)" label.
		if ( 'pending' === $status ) {
			return sprintf(
				'<span class="font-medium text-gray-500">%s</span> <span class="text-xs text-gray-500">(%s)</span>',
				esc_html( $item->get_formatted_points() ),
				esc_html__( 'Pending', 'rewardswp' )
			);
		}

		// Expired: grey strikethrough (no label — Activity Type column already says "Expired").
		// Check both status (declared but unused by cron) and source_type (actual expiration audit entries).
		if ( 'expired' === $status || 'points_expiration' === $item->source_type ) {
			return sprintf(
				'<span class="font-medium text-gray-500 line-through">%s</span>',
				esc_html( $item->get_formatted_points() )
			);
		}

		// Approved (default): green for earned, red for spent.
		$class = 'earned' === $item->type ? 'text-green-700' : 'text-red-700';

		return sprintf(
			'<span class="font-medium %s">%s</span>',
			esc_attr( $class ),
			esc_html( $item->get_formatted_points() )
		);
	}

	/**
	 * Order total column.
	 *
	 * @since 1.0.0
	 *
	 * @param Point $item Point object.
	 *
	 * @return string
	 */
	public function column_order_total( Point $item ): string {
		if ( empty( $item->source_id ) ) {
			return esc_html__( 'N/A', 'rewardswp' );
		}

		// Try to get integration from meta first, fallback to source_type.
		$integration_name = $item->get_meta( 'integration' );

		if ( empty( $integration_name ) ) {
			$integration_name = $item->source_type;
		}

		$integration = $this->integration_factory->get_integration( $integration_name );

		if ( ! is_subclass_of( $integration, IntegrationWithCouponInterface::class ) ) {
			return esc_html__( 'N/A', 'rewardswp' );
		}

		$order_total = $integration->get_order_total_formatted( $item->source_id );

		if ( empty( $order_total ) ) {
			return esc_html__( 'N/A', 'rewardswp' );
		}

		return sprintf(
			'<a href="%s" class="text-[#2271b1] hover:text-[#135e96]">%s</a>',
			esc_url_raw( $integration->get_order_link( $item->source_id ) ),
			wp_kses_post( $order_total )
		);
	}

	/**
	 * Generate the table navigation above or below the table.
	 *
	 * Adds a source type dropdown filter.
	 *
	 * @since 1.4.0
	 *
	 * @param string $which The location of the extra table nav: 'top' or 'bottom'.
	 */
	protected function extra_tablenav( $which ): void {
		if ( 'top' !== $which ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$current_source_type = sanitize_text_field( filter_input( INPUT_GET, 'source_type' ) ?? '' );

		$source_types = [
			'purchase'          => __( 'Purchase', 'rewardswp' ),
			'first_order'       => __( 'First Order', 'rewardswp' ),
			'referral'          => __( 'Referral', 'rewardswp' ),
			'signup'            => __( 'Signup', 'rewardswp' ),
			'birthday'          => __( 'Birthday', 'rewardswp' ),
			'product_review'    => __( 'Product Review', 'rewardswp' ),
			'manual_adjustment' => __( 'Manual Adjustment', 'rewardswp' ),
			'reward_redemption' => __( 'Reward Redemption', 'rewardswp' ),
			'points_expiration' => __( 'Points Expiration', 'rewardswp' ),
		];

		?>
		<div class="alignleft actions">
			<label for="filter-by-source-type" class="screen-reader-text">
				<?php esc_html_e( 'Filter by source type', 'rewardswp' ); ?>
			</label>
			<select name="source_type" id="filter-by-source-type">
				<option value=""><?php esc_html_e( 'All Sources', 'rewardswp' ); ?></option>
				<?php foreach ( $source_types as $value => $label ) : ?>
					<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $current_source_type, $value ); ?>>
						<?php echo esc_html( $label ); ?>
					</option>
				<?php endforeach; ?>
			</select>
			<?php submit_button( __( 'Filter', 'rewardswp' ), '', 'filter_action', false ); ?>
		</div>
		<?php
	}

	/**
	 * Get point type counts for filters.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	private function get_type_counts(): array {
		$all_count = $this->points_service->count_points_entries();

		return [
			'all'    => $all_count,
			'earned' => $this->points_service->count_points_entries( [ 'type' => 'earned' ] ),
			'spent'  => $this->points_service->count_points_entries( [ 'type' => 'spent' ] ),
		];
	}

	/**
	 * Get an associative array ( id => link ) with the list of views available on this table.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	protected function get_views(): array {
		$counts       = $this->get_type_counts();
		$current_type = sanitize_text_field( filter_input( INPUT_GET, 'type' ) ?? 'all' );

		$views = [
			'all'    => $this->get_view_link( 'all', esc_html__( 'All', 'rewardswp' ), $counts['all'], 'all' === $current_type ),
			'earned' => $this->get_view_link( 'earned', esc_html__( 'Earned', 'rewardswp' ), $counts['earned'], 'earned' === $current_type ),
			'spent'  => $this->get_view_link( 'spent', esc_html__( 'Spent', 'rewardswp' ), $counts['spent'], 'spent' === $current_type ),
		];

		return $views;
	}

	/**
	 * Helper method to generate view links.
	 *
	 * @since 1.0.0
	 *
	 * @param string $type    Type filter value.
	 * @param string $label   Display label.
	 * @param int    $count   Count of items.
	 * @param bool   $current Whether this is the current view.
	 *
	 * @return string
	 */
	private function get_view_link( string $type, string $label, int $count, bool $current = false ): string {
		$query_args = [ 'type' => $type === 'all' ? false : $type ];
		$url        = esc_url( add_query_arg( $query_args ) );
		$class      = $current ? 'current' : 'text-[#2271b1] hover:text-[#135e96]';

		return sprintf(
			'<a href="%s" class="%s">%s <span class="count">(%d)</span></a>',
			$url,
			$class,
			$label,
			$count
		);
	}

	/**
	 * Generates content for a single row of the table.
	 *
	 * Adds a unique ID attribute to each row for debugging/lookup.
	 *
	 * @since 1.1.0
	 *
	 * @param object|array $item The current item. Expects a Point object.
	 */
	public function single_row( $item ) {
		if ( $item instanceof \Awesomemotive\Rewardswp\Models\Point && isset( $item->id ) ) {
			echo '<tr id="point-' . esc_attr( $item->id ) . '">';
		} else {
			echo '<tr>';
		}
		$this->single_row_columns( $item );
		echo '</tr>';
	}
}
