<?php
/**
 * File Path: includes/data/class-mbs-genre-seeder.php
 *
 * Genre Seeder
 *
 * Populates the mbs_genre taxonomy with two parent groups — Fiction and
 * Non-Fiction — and a curated set of child terms under each. Runs once
 * per install, guarded by the 'mbs_genres_seeded' option so it never
 * repeats on subsequent requests.
 *
 * Seeding strategy:
 *   · wp_insert_term() is idempotent — if a term already exists it
 *     returns a WP_Error with code 'term_exists' whose error data is
 *     the existing term_id. The helpers below extract that ID so parent
 *     assignment works correctly regardless of prior state.
 *   · Bumping SEEDED_VERSION forces a re-seed on the next admin_init,
 *     allowing future term additions without manual DB intervention.
 *   · Only child terms that do not yet exist are inserted; existing
 *     custom genres added by the admin are never touched.
 *
 * Hooked by MBS_Plugin at init priority 20 (after taxonomy registration
 * at priority 10).
 *
 * @package    My_Book_Showroom
 * @subpackage Includes/Data
 * @since      1.0.0
 */

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

class MBS_Genre_Seeder {

	/**
	 * WordPress option key that records the completed seed version.
	 */
	const SEEDED_OPTION = 'mbs_genres_seeded';

	/**
	 * Bump this string to trigger a re-seed on the next load.
	 * History: '1' = initial seed (Fiction / Non-Fiction + 50 terms).
	 */
	const SEEDED_VERSION = '1';

	// -------------------------------------------------------------------------
	// Fiction genres (alphabetical within group)
	// -------------------------------------------------------------------------

	const FICTION = array(
		'Adventure / Action',
		'Coming-of-Age',
		'Contemporary Fiction',
		'Crime',
		'Dystopian',
		'Fantasy',
		'Graphic Novels',
		'Historical Fiction',
		'Horror',
		'Humor / Comedy',
		'Literary Fiction',
		'Magical Realism',
		'Mystery',
		'New Adult',
		'Paranormal',
		'Romance',
		'Romantasy',
		'Satire',
		'Science Fiction',
		'Speculative Fiction',
		'Suspense',
		'Thriller',
		'Western',
		'Women\'s Fiction',
		'Young Adult',
	);

	// -------------------------------------------------------------------------
	// Non-Fiction genres (alphabetical within group)
	// -------------------------------------------------------------------------

	const NONFICTION = array(
		'Art & Photography',
		'Biography & Autobiography',
		'Business & Finance',
		'Cookbooks',
		'Economics',
		'Education / Study Aids',
		'Environment & Sustainability',
		'Health & Wellness',
		'History',
		'Hobbies & Crafts',
		'Language Learning',
		'Memoir',
		'Parenting & Family',
		'Philosophy',
		'Politics & Current Affairs',
		'Psychology',
		'Quiz / Trivia / Games',
		'Relationships',
		'Religion & Spirituality / Inspirational',
		'Science & Popular Science',
		'Self-Help / Personal Development',
		'Sports',
		'Technology',
		'Travel',
		'True Crime',
	);

	// -------------------------------------------------------------------------
	// Public API
	// -------------------------------------------------------------------------

	/**
	 * Register the init p20 hook for genre seeding.
	 * Moved from MBS_Plugin::load_post_types_and_taxonomies() — REFACTOR-PLAN-ARCH-001 Batch C.
	 */
	public static function register_hooks(): void {
		add_action( 'init', array( __CLASS__, 'maybe_seed' ), 20 );
	}

	/**
	 * Seed only when the stored version is behind SEEDED_VERSION.
	 *
	 * Called by MBS_Plugin on init priority 20.
	 *
	 * @return void
	 */
	public static function maybe_seed(): void {
		if ( get_option( self::SEEDED_OPTION ) === self::SEEDED_VERSION ) {
			return;
		}
		self::seed();
	}

	/**
	 * Insert all parent + child terms unconditionally.
	 *
	 * Safe to call multiple times — wp_insert_term() skips duplicates.
	 * On success, updates the seeded-version option so maybe_seed()
	 * will not run again until SEEDED_VERSION is bumped.
	 *
	 * @return void
	 */
	public static function seed(): void {
		// ── Parent: Fiction ───────────────────────────────────────────────────
		$fiction_id = self::insert_or_get_term( 'Fiction', 0 );
		if ( ! $fiction_id ) {
			return; // Taxonomy not yet registered or DB error; abort cleanly.
		}

		// ── Parent: Non-Fiction ───────────────────────────────────────────────
		$nonfiction_id = self::insert_or_get_term( 'Non-Fiction', 0 );
		if ( ! $nonfiction_id ) {
			return;
		}

		// ── Fiction children ──────────────────────────────────────────────────
		foreach ( self::FICTION as $name ) {
			self::insert_or_get_term( $name, $fiction_id );
		}

		// ── Non-Fiction children ──────────────────────────────────────────────
		foreach ( self::NONFICTION as $name ) {
			self::insert_or_get_term( $name, $nonfiction_id );
		}

		update_option( self::SEEDED_OPTION, self::SEEDED_VERSION, false );
	}

	// -------------------------------------------------------------------------
	// Private helpers
	// -------------------------------------------------------------------------

	/**
	 * Insert a term (or retrieve it if it already exists) and return its ID.
	 *
	 * When wp_insert_term() returns a WP_Error with code 'term_exists',
	 * the error data holds the existing term_id — we extract that so
	 * parent assignment and idempotency both work correctly.
	 *
	 * @param string $name      Term display name.
	 * @param int    $parent_id Parent term ID (0 for top-level terms).
	 * @return int              Term ID on success, 0 on failure.
	 */
	private static function insert_or_get_term( string $name, int $parent_id ): int {
		$args = array( 'parent' => $parent_id );

		$result = wp_insert_term( $name, MBS_TAX_GENRE, $args );

		if ( ! is_wp_error( $result ) ) {
			return (int) $result['term_id'];
		}

		// Term already exists — wp_insert_term returns WP_Error( 'term_exists', …, $existing_term_id ).
		if ( 'term_exists' === $result->get_error_code() ) {
			return (int) $result->get_error_data( 'term_exists' );
		}

		// Unexpected error (e.g. taxonomy not registered yet) — return 0.
		return 0;
	}
}
