<?php
/**
 * File Path: includes/class-mbs-capabilities.php
 *
 * My Book Showroom — Capability Grants
 *
 * The Books CPT is registered with 'capability_type' => 'mbs_book' and
 * 'map_meta_cap' => true (see class-mbs-book-post-type.php). This means
 * WordPress expects custom capabilities like edit_mbs_books,
 * publish_mbs_books, etc. to be granted to roles. No role has these
 * capabilities by default — including administrator — so the auto-generated
 * "All Books" and "Add New Book" admin submenus are suppressed until we
 * grant them explicitly.
 *
 * This class grants the ten primitive plural capabilities required by the
 * 'mbs_book' capability type to the administrator role. The singular meta
 * caps (edit_mbs_book, read_mbs_book, delete_mbs_book) are resolved by
 * WordPress's map_meta_cap() at runtime and do not need to be granted
 * directly.
 *
 * Invocation:
 *   · Plugin activation hook (bookshowroom.php) — covers fresh installs.
 *   · MBS_Capabilities::maybe_grant() called from MBS_Plugin bootstrap —
 *     covers existing installs that upgrade past this version without
 *     re-activating.
 *   · Plugin uninstall (uninstall.php) calls revoke_admin_caps() to clean
 *     up after deletion.
 *
 * @package    My_Book_Showroom
 * @since      1.0.0
 */

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

class MBS_Capabilities {

	/**
	 * Current capability-grant version. Bump this constant whenever the
	 * cap list returned by get_caps() changes; that triggers maybe_grant()
	 * to re-sync existing installs on their next admin request.
	 */
	private const CAPS_VERSION = '1.0';

	/** Option name that stores the caps version the site has synced to. */
	private const OPTION_KEY = 'mbs_caps_version';

	/**
	 * The ten primitive plural capabilities required by the 'mbs_book'
	 * capability type.
	 *
	 * @return string[]
	 */
	public static function get_caps(): array {
		return array(
			'edit_mbs_books',
			'edit_others_mbs_books',
			'edit_private_mbs_books',
			'edit_published_mbs_books',
			'publish_mbs_books',
			'read_private_mbs_books',
			'delete_mbs_books',
			'delete_private_mbs_books',
			'delete_published_mbs_books',
			'delete_others_mbs_books',
		);
	}

	/**
	 * Grant all mbs_book capabilities to the administrator role.
	 *
	 * Idempotent — WordPress's WP_Role::add_cap() is a no-op when the role
	 * already has the capability. Updates the caps-version option on
	 * completion so maybe_grant() knows this site is synced.
	 *
	 */
	public static function grant_admin_caps(): void {
		$role = get_role( 'administrator' );
		if ( ! $role ) {
			return;
		}

		foreach ( self::get_caps() as $cap ) {
			$role->add_cap( $cap );
		}

		update_option( self::OPTION_KEY, self::CAPS_VERSION );
	}

	/**
	 * Revoke all mbs_book capabilities from the administrator role.
	 *
	 * Called from uninstall.php. Safe to invoke independently — remove_cap()
	 * is a no-op when the capability is not present.
	 *
	 */
	public static function revoke_admin_caps(): void {
		$role = get_role( 'administrator' );
		if ( ! $role ) {
			return;
		}

		foreach ( self::get_caps() as $cap ) {
			$role->remove_cap( $cap );
		}

		delete_option( self::OPTION_KEY );
	}

	/**
	 * Sync capabilities if the stored version is lower than the current,
	 * OR if any cap is actually missing from the role even though the version
	 * option is current (e.g. after a database restore or role reset).
	 *
	 * Called during MBS_Plugin bootstrap so upgrade paths that do not fire
	 * the activation hook still receive the grant.
	 *
	 */
	public static function maybe_grant(): void {
		$current = get_option( self::OPTION_KEY, '0' );
		if ( version_compare( $current, self::CAPS_VERSION, '<' ) ) {
			self::grant_admin_caps();
			return;
		}
		// Version is current but caps may still be missing (e.g. DB restore
		// stripped the role entry). Verify at least one cap is present; if
		// not, re-run the full grant so the site self-heals.
		$role = get_role( 'administrator' );
		if ( $role && ! $role->has_cap( 'edit_mbs_books' ) ) {
			self::grant_admin_caps();
		}
	}
}
