<?php
/**
 * Rewards 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\Factories\RewardTypesFactory;
use Awesomemotive\Rewardswp\Interfaces\RewardTypeInterface;
use Awesomemotive\Rewardswp\Interfaces\ViewInterface;
use Awesomemotive\Rewardswp\Models\Referral;
use Awesomemotive\Rewardswp\Models\Reward;
use Awesomemotive\Rewardswp\Services\RewardsService;
use Awesomemotive\Rewardswp\Services\MembersService;
use Exception;
use WP_List_Table;
use function Awesomemotive\Rewardswp\Helpers\get_member;
use function Awesomemotive\Rewardswp\Helpers\get_member_by;
use function Awesomemotive\Rewardswp\Helpers\get_human_readable_date;

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

	/**
	 * Rewards service instance.
	 *
	 * @since 1.0.0
	 *
	 * @var RewardsService
	 */
	private RewardsService $rewards_service;

	/**
	 * Reward types factory instance.
	 *
	 * @since 1.0.0
	 *
	 * @var RewardTypesFactory
	 */
	private RewardTypesFactory $reward_types_factory;

	/**
	 * 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;

	/**
	 * Status labels for list table.
	 *
	 * @since 1.0.0
	 *
	 * @var array
	 */
	private array $statuses = [];

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

		$this->statuses = [
			'pending'  => esc_html__( 'Pending', 'rewardswp' ),
			'issued'   => esc_html__( 'Issued', 'rewardswp' ),
			'canceled' => esc_html__( 'Canceled', 'rewardswp' ),
			'redeemed' => esc_html__( 'Used', 'rewardswp' ),
		];

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

	/**
	 * Get columns.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_columns(): array {
		return [
			'recipient'  => esc_html__( 'Recipient', 'rewardswp' ),
			'amount'     => esc_html__( 'Reward', 'rewardswp' ),
			'status'     => esc_html__( 'Status', 'rewardswp' ),
			'created_at' => esc_html__( 'Date Created', 'rewardswp' ),
			'applied_at' => esc_html__( 'Date Used', 'rewardswp' ),
		];
	}

	/**
	 * Get sortable columns.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	public function get_sortable_columns(): array {
		return [
			'amount'     => [ 'amount', false ],
			'status'     => [ 'status', false ],
			'created_at' => [ 'created_at', false ],
			'applied_at' => [ 'applied_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 rewards.
	 *
	 * @since 1.0.0
	 */
	function no_items() {
		esc_html_e( 'No Rewards found.', 'rewardswp' );
	}

	/**
	 * Parse query arguments for rewards search and filtering.
	 *
	 * @since 1.0.0
	 *
	 * @return array Parsed query arguments ready for RewardsService.
	 */
	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' ),
				'status'  => sanitize_text_field( filter_input( INPUT_GET, 'status' ) ?? '' ),
			]
		);

		// Handle search query by recipient (advocate) or reward type
		$search_term = sanitize_text_field( filter_input( INPUT_GET, 's' ) ?? '' );
		if ( ! empty( $search_term ) ) {
			// Search for members (recipients/advocates) using MembersService
			$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 ) ) {
				// Found members, search by advocate_id
				$member_ids          = array_map(
					function ( $member ) {
						return $member->id;
					},
					$members
				);
				$args['advocate_id'] = $member_ids;
			} else {
				// No members found, try searching by reward_type
				$reward_type_keys = $this->search_reward_types_by_term( $search_term );

				if ( ! empty( $reward_type_keys ) ) {
					// Found reward types by label/key, search by exact keys
					$args['reward_type'] = $reward_type_keys;
				} else {
					// Fallback: search directly in reward_type column with normalized term
					$normalized_term    = str_replace( ' ', '_', $search_term );
					$args['like_query'] = [
						'search_term' => $normalized_term,
						'columns'     => [
							'reward_type',
						],
						'compare'     => 'OR',
					];
				}
			}
		}

		return $args;
	}

	/**
	 * Search reward types by term (supports friendly names and keys).
	 *
	 * @since 1.0.0
	 *
	 * @param string $search_term The search term.
	 *
	 * @return array Array of reward type keys that match the search term.
	 */
	private function search_reward_types_by_term( string $search_term ): array {
		$matching_keys = [];

		// Normalize search term (lowercase and replace spaces with underscores)
		$normalized_term = strtolower( str_replace( ' ', '_', $search_term ) );

		// Get all registered reward types
		$all_reward_types = $this->reward_types_factory->get_all_reward_types();

		foreach ( $all_reward_types as $key => $reward_type ) {
			// Check if key matches (normalized)
			$normalized_key = strtolower( $key );
			if ( false !== strpos( $normalized_key, $normalized_term ) ) {
				$matching_keys[] = $key;
				continue;
			}

			// Check if label/name matches
			if ( method_exists( $reward_type, 'get_name' ) ) {
				$label = strtolower( $reward_type->get_name() );
				// Remove special characters and compare
				$label_normalized = str_replace( [ ' ', '-', '_' ], '', $label );
				$term_normalized  = str_replace( [ ' ', '-', '_' ], '', strtolower( $search_term ) );

				if ( false !== strpos( $label, strtolower( $search_term ) ) ||
					false !== strpos( $label_normalized, $term_normalized ) ) {
					$matching_keys[] = $key;
				}
			}
		}

		return array_unique( $matching_keys );
	}

	/**
	 * 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->rewards_service->count_rewards( $query_args );

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

		// Get the rewards data
		$this->items = $this->rewards_service->get_rewards(
			$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__( 'Rewards', 'rewardswp' ),
				'items_per_page' => $this->get_items_per_page( 'rewards_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 Rewards', '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 cb.
	 *
	 * @since 1.0.0
	 *
	 * @param Reward $item Reward object.
	 *
	 * @return string
	 */
	public function column_cb( $item ): string {
		return sprintf(
			'<input type="checkbox" name="bulk-delete[]" value="%s" />',
			$item->id
		);
	}

	/**
	 * Column default.
	 *
	 * @param Reward $item        Reward object.
	 * @param string $column_name Column name.
	 *
	 * @return string
	 *
	 * @throws Exception
	 */
	public function column_default( $item, $column_name ): string {
		switch ( $column_name ) {
			case 'amount':
				$reward_type = $this->reward_types_factory->get_reward_type( $item->reward_type );

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

				// Default formatted amount
				$label = $reward_type->get_reward_formatted_amount( $item->$column_name );

				// For free product, append product name if available via reward meta
				if ( 'free_product' === $item->reward_type ) {
					$product_name = (string) ( $item->get_meta( 'free_product_name' ) ?? '' );
					if ( '' !== $product_name ) {
						$label = sprintf(
							/* translators: %s: Free product name. */
							esc_html__( 'Free %s', 'rewardswp' ),
							$product_name
						);
					}
				}

				// Truncate long labels for table layout and show full text as title
				$max_len = 36;
				$display = mb_strlen( $label ) > $max_len ? mb_substr( $label, 0, $max_len - 1 ) . '…' : $label;
				return sprintf( '<span title="%s">%s</span>', esc_attr( $label ), esc_html( $display ) );
			case 'status':
				$status_classes = [
					'pending'  => 'bg-yellow-50 text-yellow-700 ring-yellow-600/20',
					'issued'   => 'bg-blue-50 text-blue-700 ring-blue-600/20',
					'canceled' => 'bg-gray-50 text-gray-600 ring-gray-500/20',
					'redeemed' => 'bg-green-50 text-green-700 ring-green-600/20',
				];

				$status_key = strtolower( $item->status );
				$classes    = $status_classes[ $status_key ] ?? 'bg-gray-50 text-gray-600 ring-gray-500/20';

				return sprintf(
					'<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ring-1 ring-inset %s">%s</span>',
					esc_attr( $classes ),
					esc_html( $this->statuses[ $item->status ] )
				);
			case 'created_at':
			case 'applied_at':
				if ( $item->$column_name === '0000-00-00 00:00:00' || empty( $item->$column_name ) ) {
					return 'N/A';
				}

				return get_human_readable_date( $item->$column_name, true );
			default:
				return $item->$column_name;
		}
	}

	/**
	 * Recipient column.
	 *
	 * @since 1.0.0
	 *
	 * @param Reward $item
	 *
	 * @return string
	 */
	public function column_recipient( Reward $item ): string {
		$recipient_name  = '';
		$recipient_email = '';
		$member_id       = null;

		if ( 'advocate' === $item->recipient_type && ! empty( $item->advocate_id ) ) {
			$advocate = get_member( $item->advocate_id );

			if ( ! is_null( $advocate ) ) {
				$recipient_name  = $advocate->get_full_name();
				$recipient_email = $advocate->email;
				$member_id       = $advocate->id;
			}
		} elseif ( 'friend' === $item->recipient_type ) {
			$referral = $item->get_referral();

			if ( ! is_a( $referral, Referral::class ) || empty( $referral->order_id ) ) {
				return esc_html__( 'N/A', 'rewardswp' );
			}

			$integration = $this->integration_factory->get_integration( $referral->source );

			if ( is_null( $integration ) ) {
				return esc_html__( 'N/A', 'rewardswp' );
			}

			$recipient_name  = (string) $integration->get_customer_name( $referral->order_id );
			$recipient_email = (string) $integration->get_customer_email( $referral->order_id );

			// Try to get member by user_id
			if ( ! empty( $referral->referred_user_id ) ) {
				$friend_member = get_member( $referral->referred_user_id );
				if ( $friend_member && $friend_member->id ) {
					$member_id = $friend_member->id;
				}
			}

			// Fallback: try to get member by email (for guest members).
			if ( ! $member_id && ! empty( $recipient_email ) ) {
				$friend_member = get_member_by( 'email', $recipient_email );
				if ( $friend_member && $friend_member->id ) {
					$member_id = $friend_member->id;
				}
			}
		}

		$recipient_name = empty( $recipient_name )
			? esc_html__( 'N/A', 'rewardswp' )
			: $recipient_name;

		// If we have a member_id, create link to member detail page
		if ( $member_id ) {
			$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(
						$recipient_email,
						[
							'size' => self::GRAVATAR_SIZE,
						]
					)
				),
				esc_attr( $recipient_name ),
				$member_detail_url,
				esc_html( $recipient_name )
			);
		}

		// No member link available
		return sprintf(
			'<div class="group block shrink-0">
				<div class="flex items-center">
					<img class="inline-block size-9 rounded-full" src="%s" alt="%s">
					<div class="ml-3">
						<p class="text-sm font-medium text-gray-700">%s</p>
					</div>
				</div>
			</div>',
			esc_url_raw(
				get_avatar_url(
					$recipient_email,
					[
						'size' => self::GRAVATAR_SIZE,
					]
				)
			),
			esc_attr( $recipient_name ),
			esc_html( $recipient_name )
		);
	}

	/**
	 * 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_status_counts();
		$current_status = sanitize_text_field( filter_input( INPUT_GET, 'status' ) ?? 'all' );

		$views = [
			'all' => $this->get_view_link( 'all', esc_html__( 'All', 'rewardswp' ), $counts['all'], 'all' === $current_status ),
		];

		foreach ( $this->statuses as $status => $label ) {
			$views[ $status ] = $this->get_view_link(
				$status,
				$label,
				$counts[ $status ] ?? 0,
				$current_status === $status
			);
		}

		return $views;
	}

	/**
	 * Helper method to generate view links.
	 *
	 * @param string $status
	 * @param string $label
	 * @param int    $count
	 * @param bool   $current
	 *
	 * @return string
	 */
	private function get_view_link( string $status, string $label, int $count, bool $current = false ): string {
		$query_args = [ 'status' => $status === 'all' ? false : $status ];
		$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
		);
	}

	/**
	 * Get referral counts by status.
	 *
	 * @since 1.0.0
	 *
	 * @return array
	 */
	private function get_status_counts(): array {
		$all_count = $this->rewards_service->count_rewards();
		$counts    = [
			'all' => $all_count,
		];

		foreach ( $this->statuses as $status_key => $status_label ) {
			$counts[ $status_key ] = $this->rewards_service->count_rewards( [ 'status' => strtoupper( $status_key ) ] );
		}

		return $counts;
	}

	/**
	 * 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 Reward object.
	 */
	public function single_row( $item ) {
		if ( $item instanceof Reward && isset( $item->id ) ) {
			echo '<tr id="reward-' . esc_attr( $item->id ) . '">';
		} else {
			echo '<tr>';
		}
		$this->single_row_columns( $item );
		echo '</tr>';
	}
}
