<?php
/**
 * Search Filter AJAX Handler
 *
 * Handles AJAX requests from the CaptWiki Search Filter widget.
 * Reuses each listing widget's own style template — zero duplicate card HTML.
 *
 * How card rendering works:
 *  1. The listing widget stores `widget_type` (e.g. "grid-listing") in
 *     its data-widget-settings JSON.
 *  2. On AJAX, we loop WP_Query and include the SAME style-*.php template
 *     the listing widget uses on initial page load.
 *  3. Adding a new listing widget = add one entry to $template_map below.
 *
 * @package    captain-widgets-kit
 * @subpackage captain-widgets-kit/includes
 *
 * @since      1.2.0
 */

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

if ( ! class_exists( 'Captwiki_Search_Filter_Ajax' ) ) {

	/**
	 * Class Captwiki_Search_Filter_Ajax
	 *
	 * @since 1.2.0
	 */
	class Captwiki_Search_Filter_Ajax {

		/**
		 * Maps widget_type slug → relative path to its style folder.
		 * Path is relative to CAPTWIKI_PATH.
		 *
		 * To connect a new listing widget:
		 *  1. Add its widget_type slug and style folder here.
		 *  2. Make sure the listing widget stores widget_type in data-widget-settings.
		 *
		 * @var array
		 */
		private $template_map = array(
			'grid-listing' => 'includes/elementor/widget-style/grid-listing/',
			// Future widgets — just add here:
			// 'blog-card'    => 'includes/elementor/widget-style/blog-card/',
			// 'blog-listing' => 'includes/elementor/widget-style/blog-listing/',
		);

		/**
		 * Singleton instance.
		 *
		 * @var Captwiki_Search_Filter_Ajax
		 */
		private static $instance;

		/**
		 * Get singleton instance.
		 *
		 * @since 1.2.0
		 *
		 * @return Captwiki_Search_Filter_Ajax
		 */
		public static function get_instance() {
			if ( ! isset( self::$instance ) ) {
				self::$instance = new self();
			}
			return self::$instance;
		}

		/**
		 * Constructor — register AJAX hooks.
		 *
		 * @since 1.2.0
		 */
		public function __construct() {
			add_action( 'wp_ajax_captwiki_search_filter',        array( $this, 'handle_filter_request' ) );
			add_action( 'wp_ajax_nopriv_captwiki_search_filter', array( $this, 'handle_filter_request' ) );
		}

		/**
		 * Main AJAX handler.
		 *
		 * @since 1.2.0
		 */
		public function handle_filter_request() {

			// ── Security ──────────────────────────────────────────────────────
			check_ajax_referer( 'captwiki_search_filter_nonce', 'nonce' );

			// ── Sanitize Inputs ───────────────────────────────────────────────
			$post_type = isset( $_POST['post_type'] )
				? sanitize_text_field( wp_unslash( $_POST['post_type'] ) )
				: 'post';

			$search_keyword = isset( $_POST['search'] )
				? sanitize_text_field( wp_unslash( $_POST['search'] ) )
				: '';

			$posts_per_page = isset( $_POST['posts_per_page'] )
				? min( 100, max( 1, intval( $_POST['posts_per_page'] ) ) )
				: 8;

			$orderby = isset( $_POST['orderby'] )
				? sanitize_text_field( wp_unslash( $_POST['orderby'] ) )
				: 'date';

			$order = isset( $_POST['order'] ) && in_array( strtoupper( wp_unslash( $_POST['order'] ) ), array( 'ASC', 'DESC' ), true )
				? strtoupper( sanitize_text_field( wp_unslash( $_POST['order'] ) ) )
				: 'DESC';

			$offset = isset( $_POST['offset'] ) ? intval( $_POST['offset'] ) : 0;
			$paged  = isset( $_POST['paged'] ) ? max( 1, intval( $_POST['paged'] ) ) : 1;

			// Taxonomies array: [ { taxonomy, terms[] }, … ]
			$raw_taxonomies = isset( $_POST['taxonomies'] )
				? wp_unslash( $_POST['taxonomies'] ) // phpcs:ignore
				: array();

			$taxonomies = array();
			if ( ! empty( $raw_taxonomies ) && is_array( $raw_taxonomies ) ) {
				foreach ( $raw_taxonomies as $tax_group ) {
					$tax   = isset( $tax_group['taxonomy'] ) ? sanitize_text_field( $tax_group['taxonomy'] ) : '';
					$terms = isset( $tax_group['terms'] ) && is_array( $tax_group['terms'] )
						? array_map( 'sanitize_text_field', $tax_group['terms'] )
						: array();
					if ( ! empty( $tax ) && ! empty( $terms ) ) {
						$taxonomies[] = array( 'taxonomy' => $tax, 'terms' => $terms );
					}
				}
			}

			// Widget settings sent from listing widget's data-widget-settings.
			$raw_widget_settings = isset( $_POST['widget_settings'] )
				? wp_unslash( $_POST['widget_settings'] ) // phpcs:ignore
				: array();

			$widget_settings = is_array( $raw_widget_settings )
				? $this->sanitize_widget_settings( $raw_widget_settings )
				: array();

			// Override base params from listing widget settings.
			if ( ! empty( $widget_settings['post_type'] ) )      { $post_type      = $widget_settings['post_type']; }
			if ( ! empty( $widget_settings['display_number'] ) ) { $posts_per_page = intval( $widget_settings['display_number'] ); }
			if ( ! empty( $widget_settings['orderby'] ) )        { $orderby        = $widget_settings['orderby']; }
			if ( ! empty( $widget_settings['order'] ) )          { $order          = strtoupper( $widget_settings['order'] ); }
			if ( isset( $widget_settings['offset'] ) )           { $offset         = intval( $widget_settings['offset'] ); }

			// Widget type — tells us which style template to use.
			$widget_type  = ! empty( $widget_settings['widget_type'] ) ? $widget_settings['widget_type'] : 'grid-listing';
			$widget_style = ! empty( $widget_settings['widget_style'] ) ? $widget_settings['widget_style'] : 'style-1';

			// ── Build WP_Query Args ───────────────────────────────────────────
			$query_args = array(
				'post_type'           => $post_type,
				'post_status'         => 'publish',
				'ignore_sticky_posts' => true,
				'posts_per_page'      => $posts_per_page,
				'orderby'             => $orderby,
				'order'               => $order,
				'offset'              => $offset + ( ( $paged - 1 ) * $posts_per_page ),
			);

			if ( ! empty( $search_keyword ) ) {
				$query_args['s'] = $search_keyword;
			}

			if ( ! empty( $taxonomies ) ) {
				$tax_query = array( 'relation' => 'AND' );
				foreach ( $taxonomies as $tax_group ) {
					$tax_query[] = array(
						'taxonomy' => $tax_group['taxonomy'],
						'field'    => 'slug',
						'terms'    => $tax_group['terms'],
						'operator' => 'IN',
					);
				}
				$query_args['tax_query'] = $tax_query; // phpcs:ignore WordPress.DB.SlowDBQuery
			}

			// ── Run Query ─────────────────────────────────────────────────────
			$query = new \WP_Query( $query_args );

			if ( ! $query->have_posts() ) {
				wp_send_json_success(
					array(
						'html'      => $this->no_results_html(),
						'total'     => 0,
						'found'     => 0,
						'paged'     => $paged,
						'max_pages' => 0,
					)
				);
			}

			// ── Column classes (same logic as listing widget) ─────────────────
			$col_desktop = ! empty( $widget_settings['column_desktop'] ) ? intval( $widget_settings['column_desktop'] ) : 4;
			$col_tablet  = ! empty( $widget_settings['column_tablet'] )  ? intval( $widget_settings['column_tablet'] )  : 6;
			$col_mobile  = ! empty( $widget_settings['column_mobile'] )  ? intval( $widget_settings['column_mobile'] )  : 12;

			$desktop_class = 'dp-col-lg-' . $col_desktop;
			$tablet_class  = 'dp-col-md-' . $col_tablet;
			$mobile_class  = 'dp-col-sm-' . $col_mobile . ' dp-col-' . $col_mobile;

			// ── Resolve template path ─────────────────────────────────────────
			$template_path = $this->resolve_template_path( $widget_type, $widget_style );

			// ── Render Posts using the widget's own template ──────────────────
			ob_start();

			while ( $query->have_posts() ) {
				$query->the_post();

				// Variables available inside style-*.php templates
				// (same names as the listing widget uses).
				$title      = get_the_title();
				$excerpt    = get_the_excerpt();
				$post_image = wp_get_attachment_url( get_post_thumbnail_id( get_the_ID() ) );

				if ( $template_path && file_exists( $template_path ) ) {
					// Use the exact same template the listing widget uses.
					include $template_path;
				} else {
					// Fallback: basic card so page never breaks.
					echo $this->fallback_card_html(
						$title, $excerpt, get_permalink(), $post_image,
						$desktop_class, $tablet_class, $mobile_class
					);
				}
			}

			wp_reset_postdata();

			$html = ob_get_clean();

			wp_send_json_success(
				array(
					'html'      => $html,
					'total'     => $query->found_posts,
					'found'     => $query->post_count,
					'paged'     => $paged,
					'max_pages' => $query->max_num_pages,
				)
			);
		}

		/**
		 * Resolve the template file path for a given widget type + style.
		 *
		 * @since 1.2.0
		 *
		 * @param string $widget_type  e.g. "grid-listing".
		 * @param string $widget_style e.g. "style-1".
		 * @return string|null Absolute path, or null if not found.
		 */
		protected function resolve_template_path( string $widget_type, string $widget_style ): ?string {
			if ( ! isset( $this->template_map[ $widget_type ] ) ) {
				// Unknown widget type — try a generic path convention.
				$folder = 'includes/elementor/widget-style/' . sanitize_file_name( $widget_type ) . '/';
			} else {
				$folder = $this->template_map[ $widget_type ];
			}

			$file = CAPTWIKI_PATH . $folder . sanitize_file_name( $widget_style ) . '.php';

			return file_exists( $file ) ? $file : null;
		}

		/**
		 * Basic fallback card — used only when template file is missing.
		 * Matches the minimal HTML structure of style-1.php so CSS still applies.
		 *
		 * @since 1.2.0
		 */
		protected function fallback_card_html(
			string $title,
			string $excerpt,
			string $permalink,
			string $post_image,
			string $desktop_class,
			string $tablet_class,
			string $mobile_class
		): string {
			ob_start();
			?>
			<div class="captwiki-grid-item <?php echo esc_attr( "$desktop_class $tablet_class $mobile_class" ); ?>">
				<?php if ( $post_image ) : ?>
					<img src="<?php echo esc_url( $post_image ); ?>" alt="<?php echo esc_attr( $title ); ?>" loading="lazy">
				<?php endif; ?>
				<div class="captwiki-text">
					<h3><a href="<?php echo esc_url( $permalink ); ?>"><?php echo esc_html( $title ); ?></a></h3>
					<?php if ( $excerpt ) : ?>
						<p><?php echo esc_html( $excerpt ); ?></p>
					<?php endif; ?>
				</div>
			</div>
			<?php
			return ob_get_clean();
		}

		/**
		 * Return "no results" HTML.
		 *
		 * @since 1.2.0
		 *
		 * @return string
		 */
		protected function no_results_html(): string {
			return '<div class="captwiki-no-results" style="grid-column:1/-1;text-align:center;padding:40px 20px;">'
				. '<p>' . esc_html__( 'No posts found matching your filter.', 'captain-widgets-kit' ) . '</p>'
				. '</div>';
		}

		/**
		 * Sanitize widget_settings array received from frontend.
		 *
		 * @since 1.2.0
		 *
		 * @param array $settings Raw settings.
		 * @return array Sanitized.
		 */
		protected function sanitize_widget_settings( array $settings ): array {
			$allowed_keys = array(
				'post_type', 'display_number', 'orderby', 'order', 'offset',
				'column_desktop', 'column_tablet', 'column_mobile',
				'text_limit', 'limit_on', 'limit_count', 'more_text', 'less_text',
				'connection_id', 'widget_type', 'widget_style',
			);

			$int_keys = array(
				'display_number', 'offset', 'limit_count',
				'column_desktop', 'column_tablet', 'column_mobile',
			);

			$clean = array();
			foreach ( $allowed_keys as $key ) {
				if ( ! isset( $settings[ $key ] ) ) { continue; }
				$clean[ $key ] = in_array( $key, $int_keys, true )
					? intval( $settings[ $key ] )
					: sanitize_text_field( $settings[ $key ] );
			}
			return $clean;
		}
	}

	Captwiki_Search_Filter_Ajax::get_instance();
}
