<?php

namespace Waypoint\Agent\Auth;

use Waypoint\Agent\Base\Thing;

class Role_Manager extends Thing {

	const ROLE       = 'waypoint_super_admin';
	const CAP        = 'set_waypoint_helper_settings';
	const ROLE_LABEL = 'Waypoint Super Admin';

	const TRUSTED_DOMAINS = [
		'waypoint.agency',
		'waypointagency.com',
		'statenweb.com',
	];

	// One-time sweep option — bump suffix to force a re-run on all sites after role changes.
	const SWEEP_DONE_OPTION = 'waypoint_trusted_domain_sweep_v1';

	public function attach_hooks(): void {
		add_action( 'user_register', [ $this, 'maybe_assign_first_super_admin' ] );
		add_action( 'user_register', [ $this, 'maybe_assign_trusted_domain_user' ] );
		add_action( 'profile_update', [ $this, 'maybe_assign_trusted_domain_user' ] );
		add_action( 'set_user_role', [ $this, 'maybe_reassign_after_role_change' ], 10, 1 );
		add_action( 'waypoint_monitor_daily_heartbeat', [ __CLASS__, 'assign_trusted_domain_users' ] );
		add_action( 'admin_init', [ $this, 'maybe_run_initial_sweep' ] );
	}

	/**
	 * Runs once per site — sweeps all existing users and grants the trusted-domain
	 * role to anyone who qualifies. Uses a versioned option so bumping the suffix
	 * on SWEEP_DONE_OPTION forces a fresh sweep after role-definition changes.
	 */
	public function maybe_run_initial_sweep(): void {
		if ( get_option( self::SWEEP_DONE_OPTION ) ) {
			return;
		}
		self::assign_trusted_domain_users();
		update_option( self::SWEEP_DONE_OPTION, true, false );
	}

	public static function register_role(): void {
		if ( ! get_role( self::ROLE ) ) {
			add_role( self::ROLE, self::ROLE_LABEL, [ self::CAP => true ] );
		}
	}

	public static function assign( int $user_id ): bool {
		$user = get_user_by( 'id', $user_id );
		if ( ! $user ) {
			return false;
		}
		$user->add_role( self::ROLE );
		return true;
	}

	public static function has_any_super_admin(): bool {
		$users = get_users( [
			'role'   => self::ROLE,
			'number' => 1,
			'fields' => 'ids',
		] );
		return ! empty( $users );
	}

	public function maybe_assign_first_super_admin( int $user_id ): void {
		if ( self::has_any_super_admin() ) {
			return;
		}
		self::assign( $user_id );
	}

	public function maybe_assign_trusted_domain_user( int $user_id ): void {
		$user = get_user_by( 'id', $user_id );
		if ( ! $user || ! self::is_trusted_domain_email( $user->user_email ) ) {
			return;
		}
		if ( ! in_array( self::ROLE, (array) $user->roles, true ) ) {
			self::assign( $user_id );
		}
	}

	/**
	 * WP_User::set_role() replaces a user's entire role list, so a trusted
	 * domain user can silently lose this role when someone changes their
	 * role via the Users admin screen (or wp user update --role). Re-assert
	 * it whenever a role gets set.
	 */
	public function maybe_reassign_after_role_change( int $user_id ): void {
		$this->maybe_assign_trusted_domain_user( $user_id );
	}

	/**
	 * Sweep existing users and grant the role to anyone on a trusted domain
	 * who doesn't already have it. Additive only — never removes roles.
	 */
	public static function assign_trusted_domain_users(): int {
		$assigned = 0;
		foreach ( self::TRUSTED_DOMAINS as $domain ) {
			$user_ids = get_users(
				[
					'search'         => '*@' . $domain,
					'search_columns' => [ 'user_email' ],
					'fields'         => 'ids',
				]
			);
			foreach ( $user_ids as $user_id ) {
				$user = get_user_by( 'id', $user_id );
				if ( ! $user || ! self::is_trusted_domain_email( $user->user_email ) ) {
					continue;
				}
				if ( in_array( self::ROLE, (array) $user->roles, true ) ) {
					continue;
				}
				self::assign( (int) $user_id );
				$assigned++;
			}
		}
		return $assigned;
	}

	private static function is_trusted_domain_email( string $email ): bool {
		$domain = strtolower( (string) substr( strrchr( $email, '@' ), 1 ) );
		return '' !== $domain && in_array( $domain, self::TRUSTED_DOMAINS, true );
	}
}
