<?php
/**
 * Site-local Team access presets for existing WordPress users.
 *
 * @package SubmissionInbox
 */

namespace SUBIN\Security;

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

/**
 * Applies exact Inbox access presets without changing WordPress roles.
 */
final class TeamAccessService {
	const VERSION        = 1;
	const STATE_OPTION   = 'subin_team_access_v1';
	const LOCK_OPTION    = 'subin_team_access_lock';
	const JOURNAL_OPTION = 'subin_team_access_journal';
	const LOCK_TTL       = 300;

	const PRESET_NONE    = 'none';
	const PRESET_VIEWER  = 'viewer';
	const PRESET_MANAGER = 'manager';
	const PRESET_CUSTOM  = 'custom';
	const PRESET_ADMIN   = 'administrator';

	/**
	 * Capabilities owned by the Team access product surface.
	 *
	 * @return array<int,string>
	 */
	public static function team_capabilities() {
		return array(
			Capabilities::ACCESS,
			Capabilities::VIEW_LEADS,
			Capabilities::MANAGE_LEADS,
			Capabilities::RETRY_NOTIFICATIONS,
			Capabilities::EXPORT_LEADS,
			Capabilities::DELETE_LEADS,
		);
	}

	/**
	 * Resolve one preset to its exact effective capability allowlist.
	 *
	 * @param mixed $preset Preset key.
	 * @param mixed $export Whether lead CSV export is allowed.
	 * @return array<int,string>|\WP_Error
	 */
	public static function preset_capabilities( $preset, $export = false ) {
		if ( ! is_scalar( $preset ) || ! is_scalar( $export ) ) {
			return new \WP_Error( 'subin_team_access_invalid_preset', __( 'Choose a valid Inbox access preset.', 'submission-inbox' ) );
		}

		$preset = sanitize_key( (string) $preset );
		if ( ! in_array( $preset, array( self::PRESET_NONE, self::PRESET_VIEWER, self::PRESET_MANAGER ), true ) ) {
			return new \WP_Error( 'subin_team_access_invalid_preset', __( 'Choose a valid Inbox access preset.', 'submission-inbox' ) );
		}

		if ( self::PRESET_NONE === $preset ) {
			return array();
		}

		$capabilities = array( Capabilities::ACCESS, Capabilities::VIEW_LEADS );
		if ( self::PRESET_MANAGER === $preset ) {
			$capabilities[] = Capabilities::MANAGE_LEADS;
			$capabilities[] = Capabilities::RETRY_NOTIFICATIONS;
		}
		if ( rest_sanitize_boolean( $export ) ) {
			$capabilities[] = Capabilities::EXPORT_LEADS;
		}

		return $capabilities;
	}

	/**
	 * Read the effective and managed Team access state for one site user.
	 *
	 * @param mixed $user_id WordPress user id.
	 * @return array<string,mixed>|\WP_Error
	 */
	public static function state_for_user( $user_id ) {
		$user_id = is_scalar( $user_id ) ? absint( $user_id ) : 0;
		$user    = $user_id > 0 ? get_userdata( $user_id ) : false;
		if ( ! $user ) {
			return new \WP_Error( 'subin_team_access_user_missing', __( 'The selected WordPress user does not exist.', 'submission-inbox' ) );
		}
		if ( ! self::is_site_member( $user_id ) ) {
			return new \WP_Error( 'subin_team_access_user_not_member', __( 'The selected WordPress user is not a member of this site.', 'submission-inbox' ) );
		}

		$capabilities = self::effective_capability_map( $user_id );
		if ( user_can( $user_id, 'manage_options' ) ) {
			return self::public_state( $user_id, self::PRESET_ADMIN, true, false, $capabilities, array() );
		}

		$state      = self::read_state();
		$assignment = $state['users'][ $user_id ] ?? array();
		$managed    = ! empty( $assignment );
		$preset     = self::preset_from_capabilities( $capabilities );
		$export     = ! empty( $capabilities[ Capabilities::EXPORT_LEADS ] );
		if ( $managed ) {
			$expected = self::preset_capabilities( $assignment['preset'], $assignment['export'] );
			if ( is_wp_error( $expected ) || ! self::matches_capabilities( $capabilities, $expected ) ) {
				$preset = self::PRESET_CUSTOM;
			}
		}

		return self::public_state( $user_id, $preset, $export, $managed, $capabilities, $assignment );
	}

	/**
	 * Apply one exact Team preset to one existing user on the current site.
	 *
	 * @param mixed $user_id  WordPress user id.
	 * @param mixed $preset   Preset key.
	 * @param mixed $export   Whether lead CSV export is allowed.
	 * @param mixed $actor_id User performing the update.
	 * @return array<string,mixed>|\WP_Error
	 */
	public static function update_user( $user_id, $preset, $export = false, $actor_id = 0 ) {
		$user_id  = is_scalar( $user_id ) ? absint( $user_id ) : 0;
		$actor_id = is_scalar( $actor_id ) ? absint( $actor_id ) : 0;
		$desired  = self::preset_capabilities( $preset, $export );
		if ( is_wp_error( $desired ) ) {
			return $desired;
		}

		$preset = sanitize_key( (string) $preset );
		$export = self::PRESET_NONE !== $preset && rest_sanitize_boolean( $export );
		$user   = $user_id > 0 ? get_userdata( $user_id ) : false;
		if ( ! $user ) {
			return new \WP_Error( 'subin_team_access_user_missing', __( 'The selected WordPress user does not exist.', 'submission-inbox' ) );
		}
		if ( ! self::is_site_member( $user_id ) ) {
			return new \WP_Error( 'subin_team_access_user_not_member', __( 'The selected WordPress user is not a member of this site.', 'submission-inbox' ) );
		}
		if ( user_can( $user_id, 'manage_options' ) ) {
			return new \WP_Error( 'subin_team_access_administrator_protected', __( 'WordPress administrators always retain full InboxMend access.', 'submission-inbox' ) );
		}

		$lock = self::acquire_lock();
		if ( is_wp_error( $lock ) ) {
			return $lock;
		}

		try {
			$recovered = self::recover_journal();
			if ( is_wp_error( $recovered ) ) {
				return $recovered;
			}

			$user = self::fresh_user( $user_id );
			if ( ! $user ) {
				return new \WP_Error( 'subin_team_access_user_missing', __( 'The selected WordPress user is no longer available.', 'submission-inbox' ) );
			}
			if ( ! self::is_site_member( $user_id ) ) {
				return new \WP_Error( 'subin_team_access_user_not_member', __( 'The selected WordPress user is no longer a member of this site.', 'submission-inbox' ) );
			}
			if ( user_can( $user_id, 'manage_options' ) ) {
				return new \WP_Error( 'subin_team_access_administrator_protected', __( 'WordPress administrators always retain full InboxMend access.', 'submission-inbox' ) );
			}

			$previous_raw = get_option( self::STATE_OPTION, false );
			$state        = self::normalize_state( $previous_raw );
			$existing     = $state['users'][ $user_id ] ?? array();
			$before       = self::direct_capability_snapshot( $user );
			$baseline     = isset( $existing['baseline'] ) && is_array( $existing['baseline'] ) ? $existing['baseline'] : $before;
			$journal      = array(
				'user_id'        => $user_id,
				'before'         => $before,
				'previous_state' => $previous_raw,
			);

			if ( ! add_option( self::JOURNAL_OPTION, $journal, '', false ) ) {
				return new \WP_Error( 'subin_team_access_journal_failed', __( 'InboxMend could not start the access update safely. Please try again.', 'submission-inbox' ) );
			}

			$applied = self::apply_exact_capabilities( $user_id, $desired );
			if ( is_wp_error( $applied ) ) {
				return self::rollback_update( $applied, $journal );
			}

			$state['version']                  = self::VERSION;
			$state['revision']                 = absint( $state['revision'] ) + 1;
			$state['users'][ $user_id ]        = array(
				'preset'     => $preset,
				'export'     => $export,
				'baseline'   => self::normalize_snapshot( $baseline ),
				'updated_by' => $actor_id,
				'updated_at' => current_time( 'mysql' ),
			);

			if ( ! self::write_state( $state ) ) {
				return self::rollback_update(
					new \WP_Error( 'subin_team_access_state_failed', __( 'InboxMend could not save the access update. Previous access was restored.', 'submission-inbox' ) ),
					$journal
				);
			}

			delete_option( self::JOURNAL_OPTION );
			if ( false !== get_option( self::JOURNAL_OPTION, false ) ) {
				return self::rollback_update(
					new \WP_Error( 'subin_team_access_journal_cleanup_failed', __( 'InboxMend could not finish the access update safely. Previous access was restored.', 'submission-inbox' ) ),
					$journal
				);
			}

			return self::state_for_user( $user_id );
		} catch ( \Throwable $throwable ) {
			$journal = get_option( self::JOURNAL_OPTION, false );
			$error   = new \WP_Error(
				'subin_team_access_update_exception',
				__( 'InboxMend encountered an unexpected access update error. Previous access was restored.', 'submission-inbox' ),
				array( 'exception' => sanitize_text_field( $throwable->getMessage() ) )
			);

			return is_array( $journal ) ? self::rollback_update( $error, $journal ) : $error;
		} finally {
			self::release_lock( $lock );
		}
	}

	/**
	 * Remove one stale site-local assignment without touching user capabilities.
	 *
	 * @param mixed $user_id Removed user id.
	 * @return true|\WP_Error
	 */
	public static function forget_user( $user_id ) {
		$user_id = is_scalar( $user_id ) ? absint( $user_id ) : 0;
		if ( $user_id <= 0 ) {
			return new \WP_Error( 'subin_team_access_user_missing', __( 'The selected WordPress user does not exist.', 'submission-inbox' ) );
		}

		$lock = self::acquire_lock();
		if ( is_wp_error( $lock ) ) {
			return $lock;
		}

		try {
			$recovered = self::recover_journal();
			if ( is_wp_error( $recovered ) ) {
				return $recovered;
			}

			$state = self::read_state();
			if ( ! isset( $state['users'][ $user_id ] ) ) {
				return true;
			}
			unset( $state['users'][ $user_id ] );
			$state['revision'] = absint( $state['revision'] ) + 1;
			if ( ! self::write_state( $state ) ) {
				return new \WP_Error( 'subin_team_access_state_failed', __( 'InboxMend could not remove stale Team access state.', 'submission-inbox' ) );
			}

			return true;
		} finally {
			self::release_lock( $lock );
		}
	}

	/**
	 * Restore pre-Team direct overrides and remove site-local service state.
	 *
	 * @return true|\WP_Error
	 */
	public static function remove_current_site() {
		$lock = self::acquire_lock();
		if ( is_wp_error( $lock ) ) {
			return $lock;
		}

		try {
			$recovered = self::recover_journal();
			if ( is_wp_error( $recovered ) ) {
				return $recovered;
			}

			$state  = self::read_state();
			$errors = array();
			foreach ( $state['users'] as $user_id => $assignment ) {
				$user = get_userdata( $user_id );
				if ( ! $user || ! self::is_site_member( $user_id ) ) {
					continue;
				}
				$restored = self::apply_direct_snapshot( $user_id, $assignment['baseline'] );
				if ( is_wp_error( $restored ) ) {
					$errors[ $user_id ] = $restored->get_error_message();
				}
			}

			if ( ! empty( $errors ) ) {
				return new \WP_Error(
					'subin_team_access_cleanup_failed',
					__( 'InboxMend could not restore every plugin-managed user permission.', 'submission-inbox' ),
					array( 'users' => $errors )
				);
			}

			delete_option( self::STATE_OPTION );
			delete_option( self::JOURNAL_OPTION );
			if ( false !== get_option( self::STATE_OPTION, false ) || false !== get_option( self::JOURNAL_OPTION, false ) ) {
				return new \WP_Error(
					'subin_team_access_cleanup_failed',
					__( 'InboxMend restored user permissions but could not remove its Team access recovery data.', 'submission-inbox' )
				);
			}

			return true;
		} finally {
			self::release_lock( $lock );
		}
	}

	/**
	 * Build the public state response.
	 *
	 * @param int                 $user_id User id.
	 * @param string              $preset Preset.
	 * @param bool                $export Export access.
	 * @param bool                $managed Whether a saved assignment exists.
	 * @param array<string,bool>  $capabilities Effective Team capabilities.
	 * @param array<string,mixed> $assignment Stored assignment.
	 * @return array<string,mixed>
	 */
	private static function public_state( $user_id, $preset, $export, $managed, array $capabilities, array $assignment ) {
		return array(
			'user_id'      => absint( $user_id ),
			'preset'       => sanitize_key( (string) $preset ),
			'export'       => (bool) $export,
			'managed'      => (bool) $managed,
			'capabilities' => $capabilities,
			'updated_by'   => absint( $assignment['updated_by'] ?? 0 ),
			'updated_at'   => sanitize_text_field( (string) ( $assignment['updated_at'] ?? '' ) ),
		);
	}

	/**
	 * Read a normalized state option.
	 *
	 * @return array{version:int,revision:int,users:array<int,array<string,mixed>>}
	 */
	private static function read_state() {
		return self::normalize_state( get_option( self::STATE_OPTION, array() ) );
	}

	/**
	 * Normalize service state while discarding unknown records.
	 *
	 * @param mixed $raw Raw option value.
	 * @return array{version:int,revision:int,users:array<int,array<string,mixed>>}
	 */
	private static function normalize_state( $raw ) {
		$state = array(
			'version'  => self::VERSION,
			'revision' => 0,
			'users'    => array(),
		);
		if ( ! is_array( $raw ) ) {
			return $state;
		}

		$state['revision'] = absint( $raw['revision'] ?? 0 );
		$users             = isset( $raw['users'] ) && is_array( $raw['users'] ) ? $raw['users'] : array();
		foreach ( $users as $raw_user_id => $assignment ) {
			$user_id = absint( $raw_user_id );
			$preset  = is_array( $assignment ) ? sanitize_key( (string) ( $assignment['preset'] ?? '' ) ) : '';
			if ( $user_id <= 0 || ! in_array( $preset, array( self::PRESET_NONE, self::PRESET_VIEWER, self::PRESET_MANAGER ), true ) ) {
				continue;
			}
			$state['users'][ $user_id ] = array(
				'preset'     => $preset,
				'export'     => self::PRESET_NONE !== $preset && ! empty( $assignment['export'] ),
				'baseline'   => self::normalize_snapshot( $assignment['baseline'] ?? array() ),
				'updated_by' => absint( $assignment['updated_by'] ?? 0 ),
				'updated_at' => sanitize_text_field( (string) ( $assignment['updated_at'] ?? '' ) ),
			);
		}

		return $state;
	}

	/**
	 * Persist normalized state and verify the stored value.
	 *
	 * @param array<string,mixed> $state State.
	 * @return bool
	 */
	private static function write_state( array $state ) {
		$state = self::normalize_state( $state );
		if ( false === get_option( self::STATE_OPTION, false ) ) {
			add_option( self::STATE_OPTION, $state, '', false );
		} else {
			update_option( self::STATE_OPTION, $state, false );
		}

		return $state === self::normalize_state( get_option( self::STATE_OPTION, array() ) );
	}

	/**
	 * Capture exact direct overrides for the managed capability set.
	 *
	 * @param object $user WP_User-like object.
	 * @return array<string,bool|null>
	 */
	private static function direct_capability_snapshot( $user ) {
		$snapshot = array();
		$direct   = isset( $user->caps ) && is_array( $user->caps ) ? $user->caps : array();
		foreach ( self::team_capabilities() as $capability ) {
			$snapshot[ $capability ] = array_key_exists( $capability, $direct ) ? (bool) $direct[ $capability ] : null;
		}

		return $snapshot;
	}

	/**
	 * Normalize a direct capability snapshot.
	 *
	 * @param mixed $snapshot Raw snapshot.
	 * @return array<string,bool|null>
	 */
	private static function normalize_snapshot( $snapshot ) {
		$snapshot = is_array( $snapshot ) ? $snapshot : array();
		$clean    = array();
		foreach ( self::team_capabilities() as $capability ) {
			$clean[ $capability ] = array_key_exists( $capability, $snapshot ) && null !== $snapshot[ $capability ]
				? (bool) $snapshot[ $capability ]
				: null;
		}

		return $clean;
	}

	/**
	 * Apply exact effective capabilities, using false overrides when a role grants more.
	 *
	 * @param int               $user_id User id.
	 * @param array<int,string> $desired Desired true capabilities.
	 * @return true|\WP_Error
	 */
	private static function apply_exact_capabilities( $user_id, array $desired ) {
		foreach ( self::team_capabilities() as $capability ) {
			if ( in_array( $capability, $desired, true ) ) {
				continue;
			}
			$user = self::fresh_user( $user_id );
			if ( ! $user ) {
				return new \WP_Error( 'subin_team_access_user_missing', __( 'The selected WordPress user is no longer available.', 'submission-inbox' ) );
			}
			$user->add_cap( $capability, false );
		}

		foreach ( $desired as $capability ) {
			$user = self::fresh_user( $user_id );
			if ( ! $user ) {
				return new \WP_Error( 'subin_team_access_user_missing', __( 'The selected WordPress user is no longer available.', 'submission-inbox' ) );
			}
			$user->add_cap( $capability, true );
		}

		$effective = self::effective_capability_map( $user_id );
		if ( ! self::matches_capabilities( $effective, $desired ) ) {
			return new \WP_Error( 'subin_team_access_verification_failed', __( 'InboxMend could not verify the saved user permissions.', 'submission-inbox' ) );
		}

		return true;
	}

	/**
	 * Restore an exact direct override snapshot.
	 *
	 * @param int                     $user_id User id.
	 * @param array<string,bool|null> $snapshot Snapshot.
	 * @return true|\WP_Error
	 */
	private static function apply_direct_snapshot( $user_id, array $snapshot ) {
		$snapshot = self::normalize_snapshot( $snapshot );
		foreach ( $snapshot as $capability => $value ) {
			$user = self::fresh_user( $user_id );
			if ( ! $user ) {
				return new \WP_Error( 'subin_team_access_user_missing', __( 'The selected WordPress user is no longer available.', 'submission-inbox' ) );
			}
			if ( null === $value ) {
				$user->remove_cap( $capability );
			} else {
				$user->add_cap( $capability, $value );
			}
		}

		$user = self::fresh_user( $user_id );
		if ( ! $user || $snapshot !== self::direct_capability_snapshot( $user ) ) {
			return new \WP_Error( 'subin_team_access_rollback_failed', __( 'InboxMend could not restore the previous user permissions.', 'submission-inbox' ) );
		}

		return true;
	}

	/**
	 * Roll back capability and state changes described by a journal.
	 *
	 * @param \WP_Error          $error Original error.
	 * @param array<string,mixed> $journal Journal.
	 * @return \WP_Error
	 */
	private static function rollback_update( \WP_Error $error, array $journal ) {
		$user_id       = absint( $journal['user_id'] ?? 0 );
		$restored      = $user_id > 0 ? self::apply_direct_snapshot( $user_id, $journal['before'] ?? array() ) : false;
		$state_restored = self::restore_state_option( $journal['previous_state'] ?? false );

		if ( is_wp_error( $restored ) || ! $state_restored ) {
			return new \WP_Error(
				'subin_team_access_rollback_failed',
				__( 'InboxMend could not restore the previous access state. The recovery record was preserved; reload the page and try again.', 'submission-inbox' ),
				array( 'original_error' => $error->get_error_code() )
			);
		}

		delete_option( self::JOURNAL_OPTION );
		if ( false !== get_option( self::JOURNAL_OPTION, false ) ) {
			return new \WP_Error(
				'subin_team_access_rollback_failed',
				__( 'InboxMend restored the previous access state but could not finish its recovery record cleanup.', 'submission-inbox' ),
				array( 'original_error' => $error->get_error_code() )
			);
		}

		return $error;
	}

	/**
	 * Recover an interrupted update before accepting another operation.
	 *
	 * @return true|\WP_Error
	 */
	private static function recover_journal() {
		$journal = get_option( self::JOURNAL_OPTION, false );
		if ( false === $journal ) {
			return true;
		}
		if ( ! is_array( $journal ) ) {
			return new \WP_Error( 'subin_team_access_journal_invalid', __( 'InboxMend found invalid access recovery data.', 'submission-inbox' ) );
		}

		$user_id = absint( $journal['user_id'] ?? 0 );
		if ( $user_id > 0 && get_userdata( $user_id ) && self::is_site_member( $user_id ) ) {
			$restored = self::apply_direct_snapshot( $user_id, $journal['before'] ?? array() );
			if ( is_wp_error( $restored ) ) {
				return $restored;
			}
		}

		if ( ! self::restore_state_option( $journal['previous_state'] ?? false ) ) {
			return new \WP_Error( 'subin_team_access_state_recovery_failed', __( 'InboxMend could not restore its previous Team access ledger. The recovery record was preserved.', 'submission-inbox' ) );
		}
		delete_option( self::JOURNAL_OPTION );

		return false === get_option( self::JOURNAL_OPTION, false )
			? true
			: new \WP_Error( 'subin_team_access_journal_cleanup_failed', __( 'InboxMend could not finish recovering an interrupted access update.', 'submission-inbox' ) );
	}

	/**
	 * Restore the raw state option snapshot.
	 *
	 * @param mixed $previous_state Previous raw option value.
	 * @return bool Whether the exact raw value was restored.
	 */
	private static function restore_state_option( $previous_state ) {
		if ( false === $previous_state ) {
			delete_option( self::STATE_OPTION );

			return false === get_option( self::STATE_OPTION, false );
		}

		update_option( self::STATE_OPTION, $previous_state, false );

		return $previous_state === get_option( self::STATE_OPTION, null );
	}

	/**
	 * Read effective Team capability booleans.
	 *
	 * @param int $user_id User id.
	 * @return array<string,bool>
	 */
	private static function effective_capability_map( $user_id ) {
		$map = array();
		foreach ( self::team_capabilities() as $capability ) {
			$map[ $capability ] = (bool) user_can( $user_id, $capability );
		}

		return $map;
	}

	/**
	 * Derive a stable preset or custom state from effective capabilities.
	 *
	 * @param array<string,bool> $capabilities Effective capabilities.
	 * @return string
	 */
	private static function preset_from_capabilities( array $capabilities ) {
		$export = ! empty( $capabilities[ Capabilities::EXPORT_LEADS ] );
		foreach ( array( self::PRESET_NONE, self::PRESET_VIEWER, self::PRESET_MANAGER ) as $preset ) {
			$expected = self::preset_capabilities( $preset, $export );
			if ( ! is_wp_error( $expected ) && self::matches_capabilities( $capabilities, $expected ) ) {
				return $preset;
			}
		}

		return self::PRESET_CUSTOM;
	}

	/**
	 * Whether an effective map exactly matches an allowlist.
	 *
	 * @param array<string,bool> $capabilities Effective capabilities.
	 * @param array<int,string>  $desired Desired true capabilities.
	 * @return bool
	 */
	private static function matches_capabilities( array $capabilities, array $desired ) {
		foreach ( self::team_capabilities() as $capability ) {
			$has_capability = ! empty( $capabilities[ $capability ] );
			$should_have    = in_array( $capability, $desired, true );
			if ( $has_capability !== $should_have ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Resolve a fresh WP_User after capability metadata changes.
	 *
	 * @param int $user_id User id.
	 * @return object|false
	 */
	private static function fresh_user( $user_id ) {
		if ( function_exists( 'clean_user_cache' ) ) {
			clean_user_cache( absint( $user_id ) );
		}

		return get_userdata( absint( $user_id ) );
	}

	/**
	 * Whether a user belongs to the current site.
	 *
	 * @param int $user_id User id.
	 * @return bool
	 */
	private static function is_site_member( $user_id ) {
		return ! function_exists( 'is_user_member_of_blog' ) || is_user_member_of_blog( absint( $user_id ), get_current_blog_id() );
	}

	/**
	 * Acquire a short per-site mutation lock.
	 *
	 * @return string|\WP_Error
	 */
	private static function acquire_lock() {
		$current = get_option( self::LOCK_OPTION, false );
		if ( false !== $current ) {
			$acquired_at = is_array( $current ) ? absint( $current['acquired_at'] ?? 0 ) : 0;
			if ( $acquired_at > 0 && ( time() - $acquired_at ) < self::LOCK_TTL ) {
				return new \WP_Error( 'subin_team_access_locked', __( 'Another Team access update is running. Please try again.', 'submission-inbox' ) );
			}
			if ( ! self::compare_and_delete_lock( $current ) ) {
				return new \WP_Error( 'subin_team_access_locked', __( 'Another Team access update is running. Please try again.', 'submission-inbox' ) );
			}
		}

		$token = wp_generate_uuid4();
		if ( ! add_option(
			self::LOCK_OPTION,
			array(
				'token'       => $token,
				'acquired_at' => time(),
			),
			'',
			false
		) ) {
			return new \WP_Error( 'subin_team_access_locked', __( 'Another Team access update is running. Please try again.', 'submission-inbox' ) );
		}

		return $token;
	}

	/**
	 * Atomically reclaim exactly the stale lock value that was inspected.
	 *
	 * @param mixed $expected Expected stored lock value.
	 * @return bool
	 */
	private static function compare_and_delete_lock( $expected ) {
		global $wpdb;

		if ( isset( $wpdb->options ) && is_string( $wpdb->options ) && method_exists( $wpdb, 'query' ) && method_exists( $wpdb, 'prepare' ) ) {
			$deleted = $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Option API has no compare-and-delete primitive.
				$wpdb->prepare(
					"DELETE FROM {$wpdb->options} WHERE option_name = %s AND option_value = %s",
					self::LOCK_OPTION,
					maybe_serialize( $expected )
				)
			);
			if ( 1 !== (int) $deleted ) {
				return false;
			}
			if ( function_exists( 'wp_cache_delete' ) ) {
				wp_cache_delete( self::LOCK_OPTION, 'options' );
				wp_cache_delete( 'alloptions', 'options' );
			}

			return true;
		}

		if ( $expected !== get_option( self::LOCK_OPTION, false ) ) {
			return false;
		}

		return (bool) delete_option( self::LOCK_OPTION );
	}

	/**
	 * Release only this process's mutation lock.
	 *
	 * @param string $token Lock token.
	 * @return void
	 */
	private static function release_lock( $token ) {
		$current       = get_option( self::LOCK_OPTION, array() );
		$current_token = is_array( $current ) ? (string) ( $current['token'] ?? '' ) : '';
		if ( '' !== $current_token && hash_equals( $current_token, (string) $token ) ) {
			self::compare_and_delete_lock( $current );
		}
	}
}
