<?php
/**
 * Roles and Capabilities for Accounts
 *
 * Registers the Ambassador role and assigns capabilities to roles
 * for managing custom post types used by the platform.
 *
 * @package AmbassadorHub\Accounts
 */

namespace AmbassadorHub\Accounts;

use WP_Role;
use function get_role;
use function add_role;
use function apply_filters;

/**
 * Roles helper.
 */
class Roles {
	public const ROLE_AMBASSADOR            = 'ambassador';
	public const ROLE_ADMIN                 = 'administrator';
	private const TREAT_ADMIN_AS_AMBASSADOR = true; // Whether to give WP admins the same caps as ambassadors (can be overridden by filters).

	/**
	 * Register roles and capabilities on activation.
	 *
	 * @return void
	 */
	public static function register(): void {

		// Ensure Ambassador role exists with minimal capabilities.
		$ambassador = get_role( self::ROLE_AMBASSADOR );
		if ( ! $ambassador instanceof WP_Role ) {
			$label = (string) apply_filters( 'ambh_ambassador_role_label', __( 'Ambassador', 'ambassadorhub' ) );
			add_role( self::ROLE_AMBASSADOR, $label, self::base_caps() );
			$ambassador = get_role( self::ROLE_AMBASSADOR );
		}

		// Allow modules to extend ambassador capabilities.
		if ( $ambassador instanceof WP_Role ) {
			self::apply_caps( $ambassador, self::filtered_ambassador_caps() );
		}

		// Ensure WordPress administrator has core plugin capabilities.
		$admin = get_role( self::ROLE_ADMIN );
		if ( $admin instanceof WP_Role ) {
			self::apply_caps( $admin, self::core_admin_caps() );
		}

		/**
		 * Fires after core roles are registered/updated during activation.
		 *
		 * @since 0.1.0
		 */
		do_action( 'ambh_roles_registered' );
	}

	/**
	 * Ensure roles exist and have required caps (idempotent at init).
	 *
	 * @return void
	 */
	public static function ensure_roles(): void {

		// Ensure Ambassador role exists.
		$ambassador = get_role( self::ROLE_AMBASSADOR );
		if ( ! $ambassador instanceof WP_Role ) {
			$label = (string) apply_filters( 'ambh_ambassador_role_label', __( 'Ambassador', 'ambassadorhub' ) );
			add_role( self::ROLE_AMBASSADOR, $label, self::base_caps() );
			$ambassador = get_role( self::ROLE_AMBASSADOR );
		}

		// Apply any additional capabilities provided via filters.
		if ( $ambassador instanceof WP_Role ) {
			self::apply_caps( $ambassador, self::filtered_ambassador_caps() );
		}

		// Ensure WordPress administrator has core plugin capabilities.
		$admin = get_role( self::ROLE_ADMIN );
		if ( $admin instanceof WP_Role ) {
			self::apply_caps( $admin, self::core_admin_caps() );
		}

		/**
		 * Fires on init after ensuring roles and applying filtered caps.
		 *
		 * @since 0.1.0
		 */
		do_action( 'ambh_roles_ensured' );
	}

	/**
	 * Base capabilities for the ambassador role.
	 *
	 * @return array<string,bool>
	 */
	private static function base_caps(): array {
		$caps = array( 'read' => true );
		/**
		 * Filter the base ambassador capabilities map at role creation.
		 * This filter expects a capability map: [ 'cap_name' => true, ... ].
		 * Use 'ambh_ambassador_caps' to add more caps later as a list.
		 *
		 * @since 0.1.0
		 *
		 * @param array<string,bool> $caps Base capability map.
		 */
		$caps = (array) apply_filters( 'ambh_ambassador_base_caps', $caps );
		// Normalize values to booleans.
		foreach ( $caps as $k => $v ) {
			$caps[ $k ] = (bool) $v;
		}
		return $caps;
	}

	/**
	 * Capabilities to add to ambassador role from external modules.
	 * Accepts either a list of capability names or a map of cap=>true.
	 * Returns a normalized list of capability names.
	 *
	 * @return string[]
	 */
	private static function filtered_ambassador_caps(): array {
		/**
		 * Filter the additional ambassador capabilities to add.
		 * Modules can add domain-specific caps here (e.g., 'read_fundraiser').
		 * Supports either ['cap_a','cap_b'] or ['cap_a'=>true].
		 *
		 * @since 0.1.0
		 *
		 * @param array $caps Capability list or map.
		 */
		$caps = apply_filters( 'ambh_ambassador_caps', array() );

		// Normalize to a flat list of capability names.
		$list = array();
		foreach ( (array) $caps as $key => $value ) {
			if ( is_int( $key ) ) {
				$list[] = (string) $value;
			} elseif ( $value ) {
				$list[] = (string) $key;
			}
		}
		return array_values( array_unique( array_filter( $list ) ) );
	}

	/**
	 * Core capabilities required for WordPress administrators to access plugin UI.
	 *
	 * @return string[]
	 */
	private static function core_admin_caps(): array {
		$caps = array(
			'ambh_manage_hub',
		);

		/**
		 * Filter the core capabilities that WordPress administrators receive.
		 * Use this to add capabilities required to access new admin features.
		 *
		 * @since 0.1.0
		 *
		 * @param string[] $caps List of capability names.
		 */
		return apply_filters( 'ambh_admin_caps', $caps );
	}

	/**
	 * Add capabilities to a role (idempotent: only adds missing caps).
	 *
	 * @param WP_Role $role Role to modify.
	 * @param string[] $caps Capability names to add.
	 * @return void
	 */
	private static function apply_caps( WP_Role $role, array $caps ): void {
		foreach ( $caps as $cap ) {
			if ( ! $role->has_cap( $cap ) ) {
				$role->add_cap( $cap );
			}
		}
	}

	private static function get_ambassador_roles() {

		$roles = array( self::ROLE_AMBASSADOR );

		if ( self::TREAT_ADMIN_AS_AMBASSADOR ) {
			$roles[] = self::ROLE_ADMIN;
		}

		/**
		 * Filter the list of roles considered as ambassadors for capability checks.
		 *
		 * @since 0.1.0
		 *
		 * @param string[] $roles List of role slugs to treat as ambassadors.
		 */
		return apply_filters( 'ambh_ambassador_roles', $roles );
	}

	/**
	 * Check if a user has the ambassador role.
	 * Returns false if user does not exist or has no roles.
	 *
	 * @param int|null $user_id User ID to check, or null for current user.
	 * @return bool True if user has the ambassador role, false otherwise.
	 */
	public static function user_is_ambassador( ?int $user_id = null ): bool {

		// If no user supplied use the current user.
		if ( null === $user_id ) {
			$user_id = get_current_user_id();
		}

		$user = get_user_by( 'id', $user_id );
		if ( ! $user || empty( $user->roles ) ) {
			return false;
		}

		return count( array_intersect( $user->roles, self::get_ambassador_roles() ) ) > 0;
	}

	public static function validate_caps( array $all_caps, array $caps, array $args, \WP_User $user ): array {

		// If user has no roles, return early.
		if ( empty( $all_caps ) ) {
			return $all_caps;
		}

		// If user is an ambassador, grant them core admin caps.
		if ( ! self::user_is_ambassador( $user->ID ) ) {
			return $all_caps;
		}

		$requested_cap = $args[0] ?? '';

		// Get caps that are blocked when not verified.
		$blocked_caps = (array) apply_filters( 'ambh_unverified_ambassador_blocked_caps', array() );

		if ( ! in_array( $requested_cap, $blocked_caps, true ) ) {
			return $all_caps;
		}

		if ( ! EmailVerification::user_is_verified( $user->ID ) ) {
			$all_caps[ $requested_cap ] = false;
		}

		return $all_caps;
	}
}
