<?php
/**
 * Documented capability map for Free and Pro.
 *
 * @package SEOvaultSEO\Extension
 */

namespace SEOvaultSEO\Extension;

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

/**
 * Public capability helpers.
 *
 * Free continues to gate its own screens with manage_options (filterable via
 * seovaultseo_main_menu_capability). Pro should use the granular CAP_* keys
 * and map them through {@see self::get()} / FILTER_CAPABILITIES.
 */
class Capabilities {

	const CAP_MANAGE_PRO               = 'manage_seovault_pro';
	const CAP_MANAGE_LICENSE           = 'manage_seovault_license';
	const CAP_VIEW_REPORTS             = 'view_seovault_reports';
	const CAP_RUN_BULK_OPERATIONS      = 'run_seovault_bulk_operations';
	const CAP_MANAGE_SCHEMA_MONITORING = 'manage_seovault_schema_monitoring';
	const CAP_MANAGE_STORES            = 'manage_seovault_stores';

	/**
	 * Register default capability grants for administrators.
	 *
	 * @return void
	 */
	public static function init() {
		add_filter( 'user_has_cap', array( __CLASS__, 'grant_default_caps' ), 10, 4 );
	}

	/**
	 * Default capability key → WordPress capability mapping.
	 *
	 * Free maps all Pro-facing keys to manage_options until Pro is active and
	 * optionally remaps them. Pro should not invent undeclared keys without
	 * documenting them in ExtensionApi.
	 *
	 * @return array<string,string>
	 */
	public static function defaults() {
		return array(
			self::CAP_MANAGE_PRO               => 'manage_options',
			self::CAP_MANAGE_LICENSE           => 'manage_options',
			self::CAP_VIEW_REPORTS             => 'manage_options',
			self::CAP_RUN_BULK_OPERATIONS      => 'manage_options',
			self::CAP_MANAGE_SCHEMA_MONITORING => 'manage_options',
			self::CAP_MANAGE_STORES            => 'manage_options',
		);
	}

	/**
	 * Resolved capability map after filters.
	 *
	 * @return array<string,string>
	 */
	public static function all() {
		// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Hook name is a prefixed ExtensionApi public constant.
		$map = apply_filters( ExtensionApi::FILTER_CAPABILITIES, self::defaults() );
		if ( ! is_array( $map ) ) {
			return self::defaults();
		}

		$clean = array();
		foreach ( self::defaults() as $key => $default_cap ) {
			$cap = isset( $map[ $key ] ) ? sanitize_key( (string) $map[ $key ] ) : $default_cap;
			$clean[ $key ] = '' !== $cap ? $cap : $default_cap;
		}

		return $clean;
	}

	/**
	 * Resolve a documented capability key to a WordPress capability string.
	 *
	 * @param string $key Capability key (e.g. manage_seovault_pro).
	 * @return string
	 */
	public static function get( $key ) {
		$map = self::all();
		$key = (string) $key;
		if ( isset( $map[ $key ] ) ) {
			return $map[ $key ];
		}
		return 'manage_options';
	}

	/**
	 * Whether the current user can perform a documented capability.
	 *
	 * @param string $key Capability key.
	 * @return bool
	 */
	public static function current_user_can( $key ) {
		return current_user_can( self::get( $key ) );
	}

	/**
	 * Grant documented meta-capabilities to users who already have the mapped WP cap.
	 *
	 * This lets `current_user_can( 'manage_seovault_pro' )` succeed for admins
	 * without requiring a role migration before Pro ships.
	 *
	 * @param array   $allcaps All caps for the user.
	 * @param string[] $caps    Requested primitive caps.
	 * @param array   $args    Arguments passed to current_user_can.
	 * @param mixed   $user    WP_User (WP 5.8+) or unused.
	 * @return array
	 */
	public static function grant_default_caps( $allcaps, $caps, $args, $user = null ) {
		unset( $args, $user );

		if ( ! is_array( $allcaps ) || ! is_array( $caps ) ) {
			return $allcaps;
		}

		$map = self::all();
		foreach ( $caps as $cap ) {
			if ( ! is_string( $cap ) || ! isset( $map[ $cap ] ) ) {
				continue;
			}
			$mapped = $map[ $cap ];
			if ( ! empty( $allcaps[ $mapped ] ) ) {
				$allcaps[ $cap ] = true;
			}
		}

		return $allcaps;
	}
}
