<?php
/**
 * Hero post query — single request cache, no N+1 in components.
 *
 * @package Kutby
 * @since   2.8.0
 */

declare(strict_types=1);

namespace Kutby\Hero\Services;

use WP_Post;
use WP_Query;

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

/**
 * Class HeroQuery
 */
final class HeroQuery {

	/**
	 * @var array<int, WP_Post>|null
	 */
	private static ?array $pool = null;

	/**
	 * @var array<string, mixed>|null
	 */
	private static ?array $args_used = null;

	/**
	 * Resolve a shared post pool for the hero (memoized per request).
	 *
	 * @param array<string, mixed> $hero Hero option group.
	 * @return array<int, WP_Post>
	 */
	public static function pool( array $hero ): array {
		if ( null !== self::$pool ) {
			return self::$pool;
		}

		$count  = max( 1, min( 24, (int) ( $hero['posts_count'] ?? 5 ) ) );
		$source = sanitize_key( (string) ( $hero['featured_source'] ?? 'latest' ) );
		$need   = max( $count, 9 ); // Enough for classic (1+4) + magazine columns.

		$args = array(
			'post_type'           => 'post',
			'post_status'         => 'publish',
			'posts_per_page'      => $need,
			'ignore_sticky_posts' => true,
			'no_found_rows'       => true,
			'update_post_meta_cache' => true,
			'update_post_term_cache' => true,
		);

		$cat = (int) ( $hero['category'] ?? 0 );
		$tag = (int) ( $hero['tag'] ?? 0 );

		switch ( $source ) {
			case 'sticky':
				$sticky = get_option( 'sticky_posts', array() );
				if ( is_array( $sticky ) && ! empty( $sticky ) ) {
					$args['post__in'] = array_map( 'intval', $sticky );
					$args['orderby']  = 'post__in';
				}
				break;
			case 'category':
				if ( $cat > 0 ) {
					$args['cat'] = $cat;
				}
				break;
			case 'tag':
				if ( $tag > 0 ) {
					$args['tag_id'] = $tag;
				}
				break;
			case 'manual':
				$ids = self::parse_ids( (string) ( $hero['manual_ids'] ?? '' ) );
				if ( ! empty( $ids ) ) {
					$args['post__in'] = $ids;
					$args['orderby']  = 'post__in';
					$args['posts_per_page'] = count( $ids );
				}
				break;
			case 'latest':
			default:
				if ( $cat > 0 ) {
					$args['cat'] = $cat;
				}
				break;
		}

		/**
		 * Filter Hero WP_Query args (future query providers).
		 *
		 * @since 2.8.0
		 *
		 * @param array<string, mixed> $args Query args.
		 * @param array<string, mixed> $hero Hero options.
		 */
		$args = apply_filters( 'kutby_hero_query_args', $args, $hero );
		if ( ! is_array( $args ) ) {
			$args = array();
		}

		self::$args_used = $args;
		$query           = new WP_Query( $args );
		self::$pool      = $query->posts;

		return self::$pool;
	}

	/**
	 * @param array<string, mixed> $hero Options.
	 * @return WP_Post|null
	 */
	public static function featured( array $hero ): ?WP_Post {
		$pool = self::pool( $hero );
		return $pool[0] ?? null;
	}

	/**
	 * Secondary posts after featured.
	 *
	 * @param array<string, mixed> $hero  Options.
	 * @param int                  $limit Limit.
	 * @return array<int, WP_Post>
	 */
	public static function secondary( array $hero, int $limit = 4 ): array {
		$pool = self::pool( $hero );
		return array_slice( $pool, 1, max( 0, $limit ) );
	}

	/**
	 * Grid / uniform list.
	 *
	 * @param array<string, mixed> $hero Options.
	 * @return array<int, WP_Post>
	 */
	public static function grid( array $hero ): array {
		$count = max( 1, min( 24, (int) ( $hero['posts_count'] ?? 6 ) ) );
		return array_slice( self::pool( $hero ), 0, $count );
	}

	/**
	 * Trending column — sticky first, else highest comment_count within pool.
	 *
	 * @param array<string, mixed> $hero  Options.
	 * @param int                  $limit Limit.
	 * @return array<int, WP_Post>
	 */
	public static function trending( array $hero, int $limit = 5 ): array {
		$pool = self::pool( $hero );
		if ( empty( $pool ) ) {
			return array();
		}

		$sorted = $pool;
		usort(
			$sorted,
			static function ( WP_Post $a, WP_Post $b ): int {
				return (int) $b->comment_count <=> (int) $a->comment_count;
			}
		);

		return array_slice( $sorted, 0, max( 1, $limit ) );
	}

	/**
	 * Latest column — chronological from pool.
	 *
	 * @param array<string, mixed> $hero  Options.
	 * @param int                  $limit Limit.
	 * @return array<int, WP_Post>
	 */
	public static function latest( array $hero, int $limit = 5 ): array {
		return array_slice( self::pool( $hero ), 0, max( 1, $limit ) );
	}

	/**
	 * @param string $raw Comma-separated IDs.
	 * @return array<int, int>
	 */
	private static function parse_ids( string $raw ): array {
		$parts = preg_split( '/[\s,]+/', $raw ) ?: array();
		$ids   = array();
		foreach ( $parts as $part ) {
			$id = (int) $part;
			if ( $id > 0 ) {
				$ids[] = $id;
			}
		}
		return array_values( array_unique( $ids ) );
	}
}
