<?php
/**
 * Persona Detector.
 *
 * Maps the current user to one or more of four personas, by CAPABILITY (not by
 * hardcoded email or a coupled role). A user can hold several personas at once
 * (e.g. an owner who is both manager and community manager) — this is a
 * first-class case, not an edge case. `primary()` returns the highest-ranked
 * persona for single-view contexts; `all()` returns every persona that applies.
 *
 *   - manager   — can manage_options (site owner / admin). Highest rank.
 *   - editor    — can edit_others_posts (editors).
 *   - author    — can publish_posts but not others' (authors/contributors).
 *   - community — holds a community-management role/capability (filterable;
 *                 matches common membership/community plugins when present).
 *
 * Detection is one current-user lookup, memoised per request.
 *
 * @package Platform\Helm\Modules\Persona_Dashboards
 */

declare( strict_types = 1 );

namespace Platform\Helm\Modules\Persona_Dashboards;

use WP_User;

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

final class PersonaDetector {

	/** @var string[]|null Memoised list of personas for the current user. */
	private ?array $cached = null;
	private ?WP_User $cached_user = null;

	/**
	 * All personas that apply to the current user, ranked highest-first.
	 *
	 * @return string[]
	 */
	public function all(): array {
		if ( null !== $this->cached ) {
			return $this->cached;
		}
		$user = wp_get_current_user();
		if ( ! $user instanceof WP_User || ! $user->exists() ) {
			$this->cached = [];
			return $this->cached;
		}
		$this->cached_user = $user;

		$personas = [];

		if ( user_can( $user, 'manage_options' ) ) {
			$personas[] = PersonaDashboardsModule::PERSONA_MANAGER;
		}
		if ( user_can( $user, 'edit_others_posts' ) ) {
			$personas[] = PersonaDashboardsModule::PERSONA_EDITOR;
		}
		if ( user_can( $user, 'publish_posts' ) ) {
			$personas[] = PersonaDashboardsModule::PERSONA_AUTHOR;
		}
		if ( $this->is_community_manager( $user ) ) {
			$personas[] = PersonaDashboardsModule::PERSONA_COMMUNITY;
		}

		// Rank order: manager > editor > community > author.
		$rank = [
			PersonaDashboardsModule::PERSONA_MANAGER   => 0,
			PersonaDashboardsModule::PERSONA_EDITOR    => 1,
			PersonaDashboardsModule::PERSONA_COMMUNITY => 2,
			PersonaDashboardsModule::PERSONA_AUTHOR    => 3,
		];
		$personas = array_values( array_unique( $personas ) );
		usort( $personas, static fn( $a, $b ) => ( $rank[ $a ] ?? 99 ) <=> ( $rank[ $b ] ?? 99 ) );

		$this->cached = $personas;
		return $this->cached;
	}

	/**
	 * The single highest-ranked persona, or PERSONA_DEFAULT if none apply.
	 */
	public function primary(): string {
		$all = $this->all();
		return $all[0] ?? PersonaDashboardsModule::PERSONA_DEFAULT;
	}

	public function has( string $persona ): bool {
		return in_array( $persona, $this->all(), true );
	}

	/**
	 * Whether the user holds more than one persona (multi-role).
	 */
	public function is_multi_role(): bool {
		return count( $this->all() ) > 1;
	}

	/**
	 * Whether we customise this user's dashboard at all.
	 */
	public function should_customise(): bool {
		return ! empty( $this->all() );
	}

	public function current_user_id(): int {
		if ( null === $this->cached_user ) {
			$this->all();
		}
		return $this->cached_user instanceof WP_User ? (int) $this->cached_user->ID : 0;
	}

	/**
	 * Community-manager detection. Capability-first, with a filter so sites
	 * running membership/community plugins can map their own role. Never a
	 * hard dependency on any specific plugin.
	 */
	private function is_community_manager( WP_User $user ): bool {
		/**
		 * Filter the capabilities/roles that mark a community manager.
		 *
		 * @param string[] $markers Capabilities or role slugs.
		 */
		$markers = apply_filters( 'platform_helm_community_manager_markers', [
			'moderate_comments', // Core capability most community managers have.
		] );
		if ( ! is_array( $markers ) ) {
			return false;
		}
		$roles = (array) $user->roles;
		foreach ( $markers as $marker ) {
			$marker = (string) $marker;
			if ( '' === $marker ) {
				continue;
			}
			if ( in_array( $marker, $roles, true ) || user_can( $user, $marker ) ) {
				// moderate_comments alone is held by admins/editors too; only
				// count it as a *distinct* community persona when the user is
				// NOT already a manager or editor, so it doesn't fire for
				// everyone. The community widgets are additive anyway.
				if ( 'moderate_comments' === $marker
					&& ( user_can( $user, 'manage_options' ) || user_can( $user, 'edit_others_posts' ) ) ) {
					continue;
				}
				return true;
			}
		}
		return false;
	}

	public function reset(): void {
		$this->cached      = null;
		$this->cached_user = null;
	}
}
