<?php
/**
 * File Path: includes/admin/class-mbs-dashboard-catalog-stats.php
 *
 * Dashboard Catalog Stats
 *
 * Owns catalog statistics, aggregate health, and release pipeline queries.
 * Extracted from MBS_Admin_Dashboard (REF-008).
 *
 * @package MyBookShowroom
 * @since   1.1.0
 */

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

class MBS_Dashboard_Catalog_Stats { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- mbs_/MBS_ prefix is correct; scanner not configured with plugin prefix

	/**
	 * Return cached catalog stats for the dashboard stats bar.
	 *
	 * Transient: mbs_catalog_stats, 1-hour TTL.
	 *
	 * @return array{total:int, published:int, drafts:int, featured:int}
	 * @since 1.1.0
	 */
	public function get_catalog_stats(): array {
		$cached = get_transient( 'mbs_catalog_stats' );
		if ( is_array( $cached ) ) {
			return $cached;
		}

		$counts    = wp_count_posts( MBS_POST_TYPE );
		$published = isset( $counts->publish ) ? (int) $counts->publish : 0;
		$drafts    = isset( $counts->draft )   ? (int) $counts->draft   : 0;
		$total     = $published + $drafts;

		// Featured count — direct index scan on mbs_book_meta replaces WP_Query meta_query.
		$featured = count( MBS_Book_Meta_DAO::get_featured_ids() );

		$stats = compact( 'total', 'published', 'drafts', 'featured' );
		set_transient( 'mbs_catalog_stats', $stats, HOUR_IN_SECONDS );
		return $stats;
	}

	/**
	 * Return aggregate catalog health across all published books.
	 *
	 * Delegates per-book scoring to MBS_Dashboard_Health_Score.
	 * Transient: mbs_catalog_health_aggregate, 1-hour TTL.
	 *
	 * @return array{score:float, scored:int}
	 * @since 1.1.0
	 */
	public function get_aggregate_health(): array {
		$cached = get_transient( 'mbs_catalog_health_aggregate' );
		if ( is_array( $cached ) ) {
			return $cached;
		}

		$post_ids = get_posts(
			array(
				'post_type'      => MBS_POST_TYPE,
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'no_found_rows'  => true, // V6-E-009: eliminates unnecessary COUNT(*) query.
			)
		);

		$scorer      = new MBS_Dashboard_Health_Score();
		$total_score = 0;
		$scored      = 0;

		foreach ( $post_ids as $post_id ) {
			$total_score += $scorer->calculate_health_score( (int) $post_id );
			$scored++;
		}

		$result = array(
			'score'  => $scored > 0 ? round( $total_score / $scored, 1 ) : 0.0,
			'scored' => $scored,
		);

		set_transient( 'mbs_catalog_health_aggregate', $result, HOUR_IN_SECONDS );
		return $result;
	}

	/**
	 * Return upcoming books grouped by lifecycle state for the Release Pipeline.
	 *
	 * @return array{preorder:WP_Post[], coming_soon:WP_Post[], future:WP_Post[]}
	 * @since 1.1.0
	 */
	public function get_release_pipeline(): array {
		// Lifecycle-state lists — direct index scans on mbs_book_meta replace meta_query.
		$preorder_ids = MBS_Book_Meta_DAO::get_lifecycle_state_ids( 'preorder' );
		$preorder     = empty( $preorder_ids ) ? array() : get_posts(
			array(
				'post_type'      => MBS_POST_TYPE,
				'post_status'    => 'publish',
				'posts_per_page' => 5,
				'no_found_rows'  => true,
				'post__in'       => $preorder_ids,
			)
		);

		$coming_soon_ids = MBS_Book_Meta_DAO::get_lifecycle_state_ids( 'coming_soon' );
		$coming_soon     = empty( $coming_soon_ids ) ? array() : get_posts(
			array(
				'post_type'      => MBS_POST_TYPE,
				'post_status'    => 'publish',
				'posts_per_page' => 5,
				'no_found_rows'  => true,
				'post__in'       => $coming_soon_ids,
			)
		);

		$future = get_posts(
			array(
				'post_type'      => MBS_POST_TYPE,
				'post_status'    => 'future',
				'posts_per_page' => 5,
				'no_found_rows'  => true,
				'orderby'        => 'date',
				'order'          => 'ASC',
			)
		);

		return compact( 'preorder', 'coming_soon', 'future' );
	}
}
