<?php
/**
 * File Path: includes/admin/class-mbs-dashboard-health-score.php
 *
 * Dashboard Health Score
 *
 * Calculates per-book and aggregate catalog health scores.
 * Extracted from MBS_Admin_Dashboard (REF-008).
 *
 * @package MyBookShowroom
 * @since   1.1.0
 */

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

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

	/**
	 * Calculate the catalog health score (0-100) for a single published book.
	 *
	 * FREE dimensions (9):
	 *   cover image, synopsis, author term, genre term, word count, teaser,
	 *   2+ format rows, lifecycle state set, at least one buy link.
	 *
	 * PRO dimensions (3, added when mbs_is_pro() is true):
	 *   audio sample on any format row, ASIN on any format row, PA-API partner tag.
	 *
	 * Transient per-book: mbs_catalog_health_{post_id}, 1-hour TTL.
	 *
	 * @param int $post_id
	 * @return int Score 0-100.
	 * @since 1.1.0
	 */
	public function calculate_health_score( int $post_id ): int {
		$cached = get_transient( 'mbs_catalog_health_' . $post_id );
		if ( false !== $cached ) {
			return (int) $cached;
		}

		$format_rows = MBS_Format_Row_DAO::get_by_post( $post_id );
		$base        = $this->collect_base_health_dimensions( $post_id, $format_rows );
		$pro         = mbs_is_pro()
			? $this->collect_pro_health_dimensions( $post_id, $format_rows )
			: array( 'score' => 0, 'count' => 0 );

		$total_dims  = $base['count'] + $pro['count'];
		$total_score = $base['score'] + $pro['score'];
		$result      = $total_dims > 0 ? (int) round( ( $total_score / $total_dims ) * 100 ) : 0;
		set_transient( 'mbs_catalog_health_' . $post_id, $result, HOUR_IN_SECONDS );
		return $result;
	}

	/**
	 * Collect the 9 FREE-tier health score dimensions for a book.
	 *
	 * @param int   $post_id     Book post ID.
	 * @param array $format_rows Format rows from MBS_Format_Row_DAO::get_by_post().
	 * @return array{score:int, count:int}
	 * @since 1.1.0
	 */
	private function collect_base_health_dimensions( int $post_id, array $format_rows ): array {
		$score = 0;
		$count = 0;

		// 1. Cover image.
		$count++;
		if ( has_post_thumbnail( $post_id ) ) { $score++; }

		// 2. Synopsis.
		$count++;
		if ( ! empty( get_post_meta( $post_id, 'mbs_synopsis', true ) ) ) { $score++; }

		// 3. Author taxonomy term.
		$count++;
		$author_terms = wp_get_object_terms( $post_id, MBS_TAX_AUTHOR, array( 'fields' => 'ids' ) );
		if ( ! is_wp_error( $author_terms ) && ! empty( $author_terms ) ) { $score++; }

		// 4. Genre taxonomy term.
		$count++;
		$genre_terms = wp_get_object_terms( $post_id, MBS_TAX_GENRE, array( 'fields' => 'ids' ) );
		if ( ! is_wp_error( $genre_terms ) && ! empty( $genre_terms ) ) { $score++; }

		// 5. Word count.
		$count++;
		if ( (int) MBS_Book_Meta_DAO::get( $post_id, 'mbs_word_count', 0 ) > 0 ) { $score++; }

		// 6. Teaser.
		$count++;
		if ( ! empty( get_post_meta( $post_id, 'mbs_teaser', true ) ) ) { $score++; }

		// 7. 2+ format rows.
		$count++;
		if ( count( $format_rows ) >= 2 ) { $score++; }

		// 8. Lifecycle state set.
		$count++;
		if ( '' !== (string) MBS_Book_Meta_DAO::get( $post_id, 'mbs_lifecycle_state', '' ) ) { $score++; }

		// 9. At least one buy link.
		$count++;
		if ( ! empty( MBS_Buy_Link_DAO::get_all_for_post( $post_id ) ) ) { $score++; }

		return array( 'score' => $score, 'count' => $count );
	}

	/**
	 * Collect the 3 PRO-only health score dimensions for a book.
	 *
	 * @param int   $post_id     Book post ID.
	 * @param array $format_rows Format rows from MBS_Format_Row_DAO::get_by_post().
	 * @return array{score:int, count:int}
	 * @since 1.1.0
	 */
	private function collect_pro_health_dimensions( int $post_id, array $format_rows ): array {
		$score = 0;
		$count = 0;

		// 10. Audio sample on any format row.
		$count++;
		foreach ( $format_rows as $row ) {
			if ( ! empty( $row->audio_sample_id ) ) { $score++; break; }
		}

		// 11. ASIN on any format row.
		$count++;
		foreach ( $format_rows as $row ) {
			if ( ! empty( $row->asin ) ) { $score++; break; }
		}

		// 12. Amazon partner tag configured.
		$count++;
		if ( ! empty( get_option( 'mbs_setting_amazon_partner_tag', '' ) ) ) { $score++; }

		return array( 'score' => $score, 'count' => $count );
	}
	/**
	 * Clear per-book and aggregate health score transients when a book is saved.
	 *
	 * Hooked to save_post_mbs_book so scores stay fresh after any edit.
	 * V6-E-011.
	 *
	 * @param int $post_id
	 * @return void
	 */
	public static function clear_for_post( int $post_id ): void {
		delete_transient( 'mbs_catalog_health_' . $post_id );
		delete_transient( 'mbs_catalog_health_aggregate' );
	}

}
// V6-E-011: invalidate health score transients whenever a book is saved.
add_action( 'save_post_' . MBS_POST_TYPE, array( 'MBS_Dashboard_Health_Score', 'clear_for_post' ) );
