<?php
/**
 * Public-facing functionality
 *
 * @package WebEquipe\PDFSearch
 * @since 1.0.0
 */

namespace WebEquipe\PDFSearch;

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

/**
 * Public class for shortcodes and template tags
 */
class Public_Display {

	/**
	 * Flag to prevent duplicate pagination output.
	 *
	 * @since 1.0.0
	 * @var bool
	 */
	private $pagination_output = false;

	/**
	 * Initialize hooks
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_public_assets' ) );
		add_shortcode( 'webequipe_pdf_search_form', array( $this, 'render_search_form_shortcode' ) );
		add_filter( 'query_vars', array( $this, 'register_query_vars' ) );
		add_action( 'pre_get_posts', array( $this, 'set_pdf_search_query_params' ), 999 );
		add_filter( 'posts_pre_query', array( $this, 'override_pdf_search_results' ), 999, 2 );
		add_filter( 'the_posts', array( $this, 'limit_pdf_search_posts' ), 999, 2 );
		add_filter( 'body_class', array( $this, 'add_pdf_search_body_class' ) );
		// Inject our own pagination UI so it works consistently across themes.
		add_action( 'loop_end', array( $this, 'inject_pdf_search_pagination' ) );
		add_action( 'astra_content_after', array( $this, 'inject_pdf_search_pagination_astra' ), 10 );
		add_action( 'wp_footer', array( $this, 'inject_pdf_search_pagination_fallback' ), 999 );
	}

	/**
	 * Enqueue public assets
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function enqueue_public_assets() {
		if ( is_admin() ) {
			return;
		}

		$should_enqueue = false;
		if ( is_search() ) {
			$should_enqueue = true;
		} else {
			global $post;
			if ( is_singular() && $post instanceof \WP_Post && has_shortcode( $post->post_content, 'webequipe_pdf_search_form' ) ) {
				$should_enqueue = true;
			}
		}

		if ( ! $should_enqueue ) {
			return;
		}

		wp_enqueue_style(
			'webequipe-pdf-search-public',
			WEBEQUIPE_PDF_SEARCH_URL . 'public/css/public-style.css',
			array(),
			WEBEQUIPE_PDF_SEARCH_VERSION
		);

		wp_enqueue_script(
			'webequipe-pdf-search-public',
			WEBEQUIPE_PDF_SEARCH_URL . 'public/js/public-script.js',
			array( 'jquery' ),
			WEBEQUIPE_PDF_SEARCH_VERSION,
			true
		);

		webequipe_pdf_search_add_script_strategy( 'webequipe-pdf-search-public', 'public' );
	}

	/**
	 * Register custom query vars for shortcode searches.
	 *
	 * @since 1.0.0
	 * @param array $vars Query vars.
	 * @return array
	 */
	public function register_query_vars( $vars ) {
		$vars[] = 'webequipe_pdf_search_term';
		$vars[] = 'webequipe_pdf_search_type';
		$vars[] = 'posts_per_page';
		$vars[] = 'paged';
		return $vars;
	}

	/**
	 * Get a sanitized integer query parameter.
	 *
	 * @param string $key    Query key.
	 * @param int    $fallback Default value.
	 * @return int
	 */
	private function get_query_int( $key, $fallback = 0 ) {
		$value = filter_input( INPUT_GET, $key, FILTER_VALIDATE_INT );
		if ( false === $value || null === $value ) {
			return $fallback;
		}
		return $value;
	}

	/**
	 * Get a sanitized string query parameter.
	 *
	 * @param string $key    Query key.
	 * @param string $fallback Default value.
	 * @return string
	 */
	private function get_query_string( $key, $fallback = '' ) {
		// Read-only search/query params; sanitized and used in prepare(). No state change — nonce not required.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_GET[ $key ] ) || ! is_string( $_GET[ $key ] ) ) {
			return $fallback;
		}
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only, sanitized, used in prepare().
		return sanitize_text_field( wp_unslash( $_GET[ $key ] ) );
	}

	/**
	 * Check if current request is PDF-only search.
	 *
	 * @return bool
	 */
	private function is_pdf_only_search() {
		return 1 === $this->get_query_int( 'webequipe_pdf_search', 0 );
	}

	/**
	 * Override WordPress query for PDF-only searches from shortcode.
	 *
	 * @since 1.0.0
	 * @param \WP_Query $query WordPress query object.
	 * @return void
	 */
	public function override_pdf_search_query( $query ) {
		// Only on frontend search queries.
		if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
			return;
		}

		// Check if this is a PDF-only search from shortcode.
		if ( ! $this->is_pdf_only_search() ) {
			return;
		}

		// Respect posts_per_page from shortcode.
		$posts_per_page = $this->get_query_int( 'posts_per_page', 0 );
		if ( $posts_per_page > 0 ) {
			$query->set( 'posts_per_page', $posts_per_page );
		}

		// Ensure we're only getting PDF attachments.
		$query->set( 'post_type', 'attachment' );
		$query->set( 'post_mime_type', 'application/pdf' );
	}

	/**
	 * Set query parameters for PDF-only searches before WordPress runs the query.
	 *
	 * @since 1.0.0
	 * @param \WP_Query $query WordPress query object.
	 * @return void
	 */
	public function set_pdf_search_query_params( $query ) {
		// Only on frontend search queries.
		if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
			return;
		}

		// Check if this is a PDF-only search from shortcode.
		if ( ! $this->is_pdf_only_search() ) {
			return;
		}

		// Don't set post_type/post_mime_type here - it causes 404s.
		// We override the entire query results via posts_pre_query filter instead.
		// Respect posts_per_page from shortcode.
		$results_per_page = $this->get_query_int( 'posts_per_page', 0 );
		if ( $results_per_page > 0 ) {
			$query->set( 'posts_per_page', $results_per_page );
		}
	}

	/**
	 * Override WordPress query results with our paginated PDF results.
	 *
	 * @since 1.0.0
	 * @param array|null $posts Posts array or null.
	 * @param \WP_Query  $query WordPress query object.
	 * @return array|null Modified posts array
	 */
	public function override_pdf_search_results( $posts, $query ) {
		// Only on frontend search queries.
		if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
			return $posts;
		}

		// Check if this is a PDF-only search from shortcode.
		if ( ! $this->is_pdf_only_search() ) {
			return $posts;
		}

		$search_term = get_search_query();
		if ( empty( $search_term ) || strlen( $search_term ) < 2 ) {
			return array();
		}

		// Get pagination settings.
		$results_per_page = $this->get_query_int( 'posts_per_page', 10 );
		$results_per_page = $results_per_page > 0 ? $results_per_page : 10;

		$paged = 1;
		// Prioritize 'paged' for search pagination; fall back to 'page'.
		$paged_param = $this->get_query_int( 'paged', 0 );
		$page_param  = $this->get_query_int( 'page', 0 );
		if ( $paged_param > 0 ) {
			$paged = $paged_param;
		} elseif ( $page_param > 0 ) {
			$paged = $page_param;
		} else {
			$paged = get_query_var( 'paged' );
			if ( empty( $paged ) ) {
				$paged = get_query_var( 'page' );
			}
			$paged = absint( $paged );
		}
		$paged = $paged > 0 ? $paged : 1;

		// Get our paginated PDF results.
		$results_data = $this->get_pdf_search_results( $search_term, $results_per_page, $paged );
		$items        = $results_data['items'];
		$total        = $results_data['total'];

		if ( empty( $items ) ) {
			$query->found_posts   = 0;
			$query->max_num_pages = 0;
			$query->post_count    = 0;
			return array();
		}

		// Convert to post objects.
		$post_objects = array();
		foreach ( $items as $item ) {
			$post = get_post( $item->post_id );
			if ( ! $post ) {
				continue;
			}

			// Mark as PDF result.
			$post->is_pdf_result = true;
			$post->pdf_data      = $item;

			// Set permalink to PDF URL.
			if ( ! empty( $item->pdf_url ) ) {
				$post->guid = $item->pdf_url;
			}

			$post_objects[] = $post;
		}

		// Set query properties for pagination.
		$query->found_posts   = $total;
		$query->max_num_pages = (int) ceil( $total / $results_per_page );
		$query->post_count    = count( $post_objects );

		return $post_objects;
	}

	/**
	 * Limit posts array for PDF-only searches (backup method).
	 *
	 * @since 1.0.0
	 * @param array     $posts Posts array.
	 * @param \WP_Query $query WordPress query object.
	 * @return array Modified posts array
	 */
	public function limit_pdf_search_posts( $posts, $query ) {
		// Only on frontend search queries.
		if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
			return $posts;
		}

		// Check if this is a PDF-only search from shortcode.
		if ( ! $this->is_pdf_only_search() ) {
			return $posts;
		}

		// If posts_pre_query already handled it, return as-is.
		// Otherwise, limit the posts array.
		$results_per_page = $this->get_query_int( 'posts_per_page', 10 );
		$results_per_page = $results_per_page > 0 ? $results_per_page : 10;

		// Only limit if we have more posts than requested.
		if ( count( $posts ) > $results_per_page ) {
			$paged       = 1;
			$paged_param = $this->get_query_int( 'paged', 0 );
			$page_param  = $this->get_query_int( 'page', 0 );
			if ( $paged_param > 0 ) {
				$paged = $paged_param;
			} elseif ( $page_param > 0 ) {
				$paged = $page_param;
			} else {
				$paged = get_query_var( 'paged' );
				if ( empty( $paged ) ) {
					$paged = get_query_var( 'page' );
				}
				$paged = absint( $paged );
			}
			$paged = $paged > 0 ? $paged : 1;

			$offset = ( $paged - 1 ) * $results_per_page;
			$posts  = array_slice( $posts, $offset, $results_per_page );

			// Update query properties.
			$query->found_posts   = count( $posts );
			$query->max_num_pages = (int) ceil( count( $posts ) / $results_per_page );
			$query->post_count    = count( $posts );
		}

		return $posts;
	}

	/**
	 * Check if theme has a search page/template.
	 *
	 * @since 1.0.0
	 * @return bool True if search page exists
	 */
	private function has_search_page() {
		// Check if search template exists in theme.
		$search_template = locate_template( array( 'search.php', 'search-*.php' ) );
		if ( ! empty( $search_template ) ) {
			return true;
		}

		// Check if WordPress search is enabled (it always is, but some themes might disable it).
		// WordPress always supports ?s= parameter, so we assume search is available.
		return true;
	}


	/**
	 * Render search form shortcode
	 *
	 * @since 1.0.0
	 * @param array $atts Shortcode attributes.
	 * @return string HTML output
	 */
	public function render_search_form_shortcode( $atts ) {
		global $post;

		$atts = shortcode_atts(
			array(
				'placeholder'      => __( 'Search PDFs...', 'webequipe-pdf-search' ),
				'button_text'      => __( 'Search', 'webequipe-pdf-search' ),
				'show_results'     => 'true',
				'results_per_page' => '10',
				'search_type'      => 'all',
				'classes'          => '',
			),
			$atts,
			'webequipe_pdf_search_form'
		);

		$show_results = filter_var( $atts['show_results'], FILTER_VALIDATE_BOOLEAN );

		// Read results_per_page: from URL if on search page, otherwise from shortcode.
		if ( is_search() ) {
			$results_per_page = $this->get_query_int( 'posts_per_page', 0 );
			if ( $results_per_page < 1 ) {
				$results_per_page = absint( $atts['results_per_page'] );
			}
		} else {
			$results_per_page = absint( $atts['results_per_page'] );
		}

		// Read search term: prioritize 's' if on search page, otherwise use custom param.
		$search_term = '';
		if ( is_search() ) {
			$search_term = $this->get_query_string( 's', '' );
		}
		if ( '' === $search_term ) {
			$search_term = $this->get_query_string( 'webequipe_pdf_search_term', '' );
		}

		// Read page number: prioritize 'page' over 'paged' since 'page' works correctly.
		$paged       = 1;
		$paged_param = $this->get_query_int( 'paged', 0 );
		$page_param  = $this->get_query_int( 'page', 0 );
		if ( $paged_param > 0 ) {
			$paged = $paged_param;
		} elseif ( $page_param > 0 ) {
			$paged = $page_param;
		} elseif ( is_search() ) {
			// On search page, also check get_query_var.
			$paged = get_query_var( 'paged' );
			if ( empty( $paged ) ) {
				$paged = get_query_var( 'page' );
			}
			$paged = absint( $paged );
		}
		$paged = $paged > 0 ? $paged : 1;

		// Get search type from URL or use shortcode default.
		// On search page, check for PDF-only flag.
		if ( is_search() && $this->is_pdf_only_search() ) {
			$search_type = 'pdf_only';
		} else {
			$search_type_param = $this->get_query_string( 'webequipe_pdf_search_type', '' );
			$search_type       = '' !== $search_type_param ? sanitize_key( $search_type_param ) : $atts['search_type'];
		}

		// Determine form action: use search page if available, otherwise same page.
		$use_search_page = $this->has_search_page();
		$form_action     = $use_search_page ? home_url( '/' ) : '';
		if ( empty( $form_action ) && $post instanceof \WP_Post ) {
			$form_action = get_permalink( $post->ID );
		}
		if ( empty( $form_action ) ) {
			$form_action = home_url( '/' );
		}

		// Determine where to show results: on search page or same page.
		$is_on_search_page = is_search();
		// Always show results on search page if PDF-only search, regardless of shortcode location.
		$show_on_search_page = $is_on_search_page && $this->is_pdf_only_search();
		$show_on_same_page   = ! $is_on_search_page && ! empty( $search_term );

		ob_start();
		?>
		<div class="webequipe-pdf-search-form-wrapper <?php echo esc_attr( $atts['classes'] ); ?>">
			<form role="search" method="get" class="webequipe-pdf-search-form" action="<?php echo esc_url( $form_action ); ?>">
				<?php if ( $use_search_page ) : ?>
					<input type="search"
						class="webequipe-pdf-search-input" 
						placeholder="<?php echo esc_attr( $atts['placeholder'] ); ?>" 
						value="<?php echo esc_attr( $search_term ); ?>" 
						name="s" 
						title="<?php echo esc_attr__( 'Search for:', 'webequipe-pdf-search' ); ?>">
					<?php if ( 'pdf_only' === $atts['search_type'] ) : ?>
						<input type="hidden" name="webequipe_pdf_search" value="1">
					<?php endif; ?>
					<?php if ( $results_per_page > 0 ) : ?>
						<input type="hidden" name="posts_per_page" value="<?php echo esc_attr( $results_per_page ); ?>">
					<?php endif; ?>
					<input type="hidden" name="paged" value="1">
				<?php else : ?>
					<input type="search"
						class="webequipe-pdf-search-input" 
						placeholder="<?php echo esc_attr( $atts['placeholder'] ); ?>" 
						value="<?php echo esc_attr( $search_term ); ?>" 
						name="webequipe_pdf_search_term" 
						title="<?php echo esc_attr__( 'Search for:', 'webequipe-pdf-search' ); ?>">
					<?php if ( $results_per_page > 0 ) : ?>
						<input type="hidden" name="posts_per_page" value="<?php echo esc_attr( $results_per_page ); ?>">
					<?php endif; ?>
					<input type="hidden" name="webequipe_pdf_search_type" value="<?php echo esc_attr( $atts['search_type'] ); ?>">
				<?php endif; ?>
				<button type="submit" class="webequipe-pdf-search-submit">
					<?php echo esc_html( $atts['button_text'] ); ?>
				</button>
			</form>
		</div>
		<?php
		// Always show results below the form when search term exists and show_results is enabled.
		if ( $show_results && ! empty( $search_term ) ) {
			if ( $show_on_search_page ) {
				// On search page: check if it's a PDF-only search.
				$is_pdf_only_search = ( 'pdf_only' === $search_type ) || $this->is_pdf_only_search();
				if ( $is_pdf_only_search ) {
					echo wp_kses_post( $this->render_shortcode_results( $search_term, $results_per_page, $paged, 'pdf_only' ) );
				}
			} elseif ( $show_on_same_page ) {
				// Same page fallback.
				echo wp_kses_post( $this->render_shortcode_results( $search_term, $results_per_page, $paged, $search_type ) );
			}
		}
		?>
		<?php
		$output = ob_get_clean();
		return $output;
	}

	/**
	 * Render shortcode search results and pagination.
	 *
	 * @since 1.0.0
	 * @param string $search_term Search query.
	 * @param int    $results_per_page Results per page.
	 * @param int    $paged Current page number.
	 * @param string $search_type Search type.
	 * @return string
	 */
	private function render_shortcode_results( $search_term, $results_per_page, $paged, $search_type ) {
		if ( ! class_exists( __NAMESPACE__ . '\Database' ) ) {
			return '';
		}

		$results_per_page = $results_per_page > 0 ? $results_per_page : 10;
		$paged            = $paged > 0 ? $paged : 1;
		$search_type      = sanitize_key( $search_type );

		$results_data = $this->get_pdf_search_results( $search_term, $results_per_page, $paged );
		$items        = $results_data['items'];
		$total        = $results_data['total'];

		if ( empty( $items ) ) {
			return '<div class="webequipe-pdf-no-results">' . esc_html__( 'No PDF results found.', 'webequipe-pdf-search' ) . '</div>';
		}

		$settings = Database::get_settings();
		ob_start();
		?>
		<div class="webequipe-pdf-results">
			<ul class="webequipe-pdf-results-list">
				<?php foreach ( $items as $item ) : ?>
					<?php
					$post     = get_post( $item->post_id );
					$pdf_data = $item;
					if ( ! $post ) {
						continue;
					}
					$permalink = ! empty( $pdf_data->pdf_url ) ? $pdf_data->pdf_url : wp_get_attachment_url( $post->ID );
					?>
					<li class="webequipe-pdf-result">
						<h3 class="webequipe-pdf-title">
							<span class="webequipe-pdf-title-line">
								<?php if ( ! empty( $settings['show_pdf_icon'] ) ) : ?>
									<?php $icon = webequipe_pdf_search_get_icon(); ?>
									<?php if ( $icon ) : ?>
										<span class="webequipe-pdf-icon webequipe-pdf-icon--inline"><?php echo wp_kses_post( $icon ); ?></span>
									<?php endif; ?>
								<?php endif; ?>
								<a href="<?php echo esc_url( $permalink ); ?>">
									<?php echo esc_html( webequipe_pdf_search_get_display_title( get_the_title( $post->ID ) ) ); ?>
								</a>
							</span>
						</h3>
						<?php echo wp_kses_post( $this->render_pdf_meta_line( $post, $pdf_data, $settings ) ); ?>
						<?php echo wp_kses_post( $this->render_pdf_preview( $post, $settings ) ); ?>
					</li>
				<?php endforeach; ?>
			</ul>
			<?php
			// Only render pagination if NOT on search page (theme hooks will handle search page pagination).
			if ( ! is_search() ) {
				$total_pages = (int) ceil( $total / $results_per_page );
				if ( $total_pages > 1 ) :
					// Same page: use current page URL.
					global $post;
					$base_url = '';
					if ( $post instanceof \WP_Post ) {
						$base_url = get_permalink( $post->ID );
					}
					if ( empty( $base_url ) ) {
						$base_url = home_url( '/' );
					}
					$base_url = remove_query_arg( array( 'paged', 'page' ), $base_url );
					$base_url = trailingslashit( $base_url ) . '%_%';

					$add_args = array(
						'webequipe_pdf_search_term' => $search_term,
						'posts_per_page'            => $results_per_page,
						'webequipe_pdf_search_type' => $search_type,
					);
					$format   = '?paged=%#%';

					$pagination = paginate_links(
						array(
							'base'      => $base_url,
							'format'    => $format,
							'current'   => $paged,
							'total'     => $total_pages,
							'type'      => 'list',
							'add_args'  => $add_args,
							'prev_text' => __( 'Previous', 'webequipe-pdf-search' ),
							'next_text' => __( 'Next', 'webequipe-pdf-search' ),
						)
					);
					if ( $pagination ) :
						?>
						<nav class="webequipe-pdf-pagination" aria-label="<?php echo esc_attr__( 'Search results pages', 'webequipe-pdf-search' ); ?>">
							<?php echo wp_kses_post( $pagination ); // Escaped late: HTML from paginate_links(). ?>
						</nav>
						<?php
					endif;
				endif;
			}
			?>
		</div>
		<?php
		return ob_get_clean();
	}

	/**
	 * Query PDF results from the index table.
	 *
	 * @since 1.0.0
	 * @param string $search_term Search query.
	 * @param int    $results_per_page Results per page.
	 * @param int    $paged Current page number.
	 * @return array{items: array, total: int}
	 */
	private function get_pdf_search_results( $search_term, $results_per_page, $paged ) {
		$total = Database::search_index(
			$search_term,
			array(
				'count_only' => true,
			)
		);

		if ( $total < 1 ) {
			return array(
				'items' => array(),
				'total' => 0,
			);
		}

		$offset = ( $paged - 1 ) * $results_per_page;
		$items  = Database::search_index(
			$search_term,
			array(
				'limit'  => $results_per_page,
				'offset' => $offset,
			)
		);

		return array(
			'items' => $items,
			'total' => $total,
		);
	}

	/**
	 * Render meta line for a PDF result.
	 *
	 * @since 1.0.0
	 * @param \WP_Post $post Post object.
	 * @param object   $pdf_data Indexed PDF data.
	 * @param array    $settings Plugin settings.
	 * @return string
	 */
	private function render_pdf_meta_line( $post, $pdf_data, $settings ) {
		$meta_items = array();

		if ( ! empty( $settings['show_author'] ) && $settings['show_author'] ) {
			$author_id = (int) $post->post_author;
			if ( $author_id > 0 ) {
				$author_name = get_the_author_meta( 'display_name', $author_id );
				if ( '' !== $author_name ) {
					$meta_items[] = '<span class="webequipe-pdf-meta-item">' . esc_html__( 'Author', 'webequipe-pdf-search' ) . ': ' . esc_html( $author_name ) . '</span>';
				}
			}
		}

		if ( ! empty( $settings['show_file_size'] ) && $settings['show_file_size'] && isset( $pdf_data->pdf_filesize ) ) {
			$file_size = size_format( (int) $pdf_data->pdf_filesize );
			if ( $file_size ) {
				$meta_items[] = '<span class="webequipe-pdf-meta-item">' . esc_html__( 'Size', 'webequipe-pdf-search' ) . ': ' . esc_html( $file_size ) . '</span>';
			}
		}

		if ( ! empty( $settings['show_page_count'] ) && $settings['show_page_count'] && isset( $pdf_data->page_count ) ) {
			$page_count = (int) $pdf_data->page_count;
			if ( $page_count > 0 ) {
				$meta_items[] = '<span class="webequipe-pdf-meta-item">' . esc_html__( 'Page', 'webequipe-pdf-search' ) . ': ' . esc_html( $page_count ) . '</span>';
			}
		}

		if ( ! empty( $settings['show_modified_date'] ) && $settings['show_modified_date'] ) {
			$date_value = get_the_modified_date( '', $post->ID );
			if ( $date_value ) {
				$meta_items[] = '<span class="webequipe-pdf-meta-item">' . esc_html__( 'Date', 'webequipe-pdf-search' ) . ': ' . esc_html( $date_value ) . '</span>';
			}
		}

		if ( empty( $meta_items ) ) {
			return '';
		}

		$meta_line = implode( ' <span class="webequipe-pdf-meta-sep">|</span> ', $meta_items );
		return '<div class="webequipe-pdf-meta">' . $meta_line . '</div>';
	}

	/**
	 * Render preview thumbnail for a PDF result.
	 *
	 * @since 1.0.0
	 * @param \WP_Post $post Post object.
	 * @param array    $settings Plugin settings.
	 * @return string
	 */
	private function render_pdf_preview( $post, $settings ) {
		if ( empty( $settings['show_preview'] ) || true !== $settings['show_preview'] ) {
			return '';
		}

		$preview_url = webequipe_pdf_search_get_preview_image_url( $post->ID );
		if ( '' === $preview_url ) {
			return '';
		}

		$thumbnail = '<img class="webequipe-pdf-preview" src="' . esc_url( $preview_url ) . '" alt="' . esc_attr__( 'PDF preview', 'webequipe-pdf-search' ) . '" loading="lazy" decoding="async" />';

		return '<div class="webequipe-pdf-preview-wrapper">' . $thumbnail . '</div>';
	}

	/**
	 * Add body class for PDF-only searches.
	 *
	 * @since 1.0.0
	 * @param array $classes Body classes.
	 * @return array Modified classes
	 */
	public function add_pdf_search_body_class( $classes ) {
		if ( is_search() && $this->is_pdf_only_search() ) {
			$classes[] = 'webequipe-pdf-search-active';
		}
		// When "Display PDF thumbnail preview" is off on search, themes (e.g. Avada) may still
		// show the featured image via their own template. This class allows CSS to hide it.
		if ( is_search() && ! is_admin() ) {
			$settings = \WebEquipe\PDFSearch\Database::get_settings();
			if ( empty( $settings['show_preview'] ) || true !== $settings['show_preview'] ) {
				$classes[] = 'webequipe-pdf-preview-off';
			}
		}
		return $classes;
	}

	/**
	 * Inject pagination for PDF-only searches after the loop.
	 *
	 * @since 1.0.0
	 * @param \WP_Query $query WordPress query object.
	 * @return void
	 */
	public function inject_pdf_search_pagination( $query ) {
		// Only on frontend search queries.
		if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
			return;
		}

		// Check if this is a PDF-only search from shortcode.
		if ( ! $this->is_pdf_only_search() ) {
			return;
		}

		// Prevent duplicate pagination output.
		if ( $this->pagination_output ) {
			return;
		}

		// Use shared method to render pagination.
		$this->render_pagination_output( $query );
		$this->pagination_output = true;
	}

	/**
	 * Inject pagination for Astra theme (after content).
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function inject_pdf_search_pagination_astra() {
		if ( ! is_search() || ! $this->is_pdf_only_search() ) {
			return;
		}

		global $wp_query;
		if ( ! $wp_query || ! $wp_query->is_main_query() ) {
			return;
		}

		// Prevent duplicate pagination output.
		if ( $this->pagination_output ) {
			return;
		}

		$this->render_pagination_output( $wp_query );
		$this->pagination_output = true;
	}

	/**
	 * Fallback pagination injection via wp_footer.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function inject_pdf_search_pagination_fallback() {
		if ( ! is_search() || ! $this->is_pdf_only_search() ) {
			return;
		}

		global $wp_query;
		if ( ! $wp_query ) {
			return;
		}

		// Check if pagination was already output by other hooks.
		if ( $this->pagination_output ) {
			return;
		}

		// Output pagination directly so it is visible even in themes
		// that don't have a convenient hook location.
		echo '<div id="webequipe-pdf-pagination-container">';
		$this->render_pagination_output( $wp_query );
		echo '</div>';
		$this->pagination_output = true;
	}

	/**
	 * Render pagination output (shared logic).
	 *
	 * @since 1.0.0
	 * @param \WP_Query $query WordPress query object.
	 * @return void
	 */
	private function render_pagination_output( $query ) {
		$search_term = get_search_query();
		if ( empty( $search_term ) || strlen( $search_term ) < 2 ) {
			return;
		}

		// Get pagination settings.
		$results_per_page = $this->get_query_int( 'posts_per_page', 10 );
		$results_per_page = $results_per_page > 0 ? $results_per_page : 10;

		$paged = 1;
		// Prioritize 'paged' for search pagination; fall back to 'page'.
		$paged_param = $this->get_query_int( 'paged', 0 );
		$page_param  = $this->get_query_int( 'page', 0 );
		if ( $paged_param > 0 ) {
			$paged = $paged_param;
		} elseif ( $page_param > 0 ) {
			$paged = $page_param;
		} else {
			$paged = get_query_var( 'paged' );
			if ( empty( $paged ) ) {
				$paged = get_query_var( 'page' );
			}
			$paged = absint( $paged );
		}
		$paged = $paged > 0 ? $paged : 1;

		// Get total count from query.
		$total_pages = (int) $query->max_num_pages;

		// If max_num_pages seems wrong, recalculate from found_posts.
		if ( $total_pages < 1 && $query->found_posts > 0 ) {
			$total_pages = (int) ceil( $query->found_posts / $results_per_page );
		}

		// Double-check: if still 1 or less but we have results, recalculate from actual posts.
		if ( $total_pages < 2 && $query->post_count > 0 ) {
			// Get total from our database query to be sure.
			$results_data = $this->get_pdf_search_results( $search_term, 9999, 1 ); // Get all to count.
			$total        = $results_data['total'];
			if ( $total > $results_per_page ) {
				$total_pages = (int) ceil( $total / $results_per_page );
			}
		}

		if ( $total_pages > 1 ) {
			// Build pagination.
			$base_url = home_url( '/' );
			$base_url = remove_query_arg( array( 'paged', 'page', 'post_type', 'post_mime_type' ), $base_url );
			$base_url = trailingslashit( $base_url ) . '%_%';

			$add_args = array(
				's'                    => $search_term,
				'posts_per_page'       => $results_per_page,
				'webequipe_pdf_search' => 1,
			);

			$pagination = paginate_links(
				array(
					'base'      => $base_url,
					'format'    => '?paged=%#%',
					'current'   => $paged,
					'total'     => $total_pages,
					'type'      => 'list',
					'add_args'  => $add_args,
					'prev_text' => __( 'Previous', 'webequipe-pdf-search' ),
					'next_text' => __( 'Next', 'webequipe-pdf-search' ),
				)
			);

			if ( $pagination ) {
				echo '<nav class="webequipe-pdf-pagination" aria-label="' . esc_attr__( 'Search results pages', 'webequipe-pdf-search' ) . '">';
				echo wp_kses_post( $pagination ); // Escaped late: HTML from paginate_links().
				echo '</nav>';
			}
		}
	}
}
