[Back]
<?php
/*
 * Copyright (c) 2023 LatePoint LLC. All rights reserved.
 */

namespace LatePoint\Misc;

class User{
	public ?string $backend_user_type = null;
	public ?\WP_User $wp_user = null;
	public array $roles = [];

	public ?\OsAgentModel $agent = null;
	public ?\OsCustomerModel $customer = null;

	public ?string $wp_capability = null;
	protected array $allowed_records = ['agent' => [], 'service' => [], 'location' => []];
	protected array $capabilities = [];


	function __construct(){
	}


	public function get_wp_user_meta($meta_key, $default = ''){
		$meta_value = $default;
		if($this->wp_user){
			$meta_value = get_user_meta($this->wp_user->ID, $meta_key, true);
			if(empty($meta_value)) $meta_value = $default;
		}
		return $meta_value;
	}


	public function update_wp_user_meta($meta_key, $meta_value){
		if($this->wp_user){
			update_user_meta($this->wp_user->ID, $meta_key, $meta_value);
		}
	}

	/**
	 *
	 * Checks if user has a certain capability
	 *
	 * @param array|string $capability single capability or an array of capabilities to check if user has or not
	 * @return bool
	 */
	public function has_capability($capability): bool{
		if($this->has_backend_access()){
			// only backend user types have capabilities, check if it's a backend user type first
			if(is_array($capability)){
				$can = empty(array_diff($capability, $this->get_capabilities()));
			}else{
				$can = in_array($capability, $this->get_capabilities());
			}
		}else{
			$can = false;
		}
		/**
		 * Checks if a user has certain capability
		 *
		 * @since 4.7.0
		 * @hook latepoint_user_has_capability
		 *
		 * @param {bool} $can answer to a question if user has a capability
		 * @param {array|string} $capability array or a single capability that needs to be checked
		 * @returns {bool} answer to a question if user has a capability
		 */
		return apply_filters('latepoint_user_has_capability', $can, $capability);
	}

	public static function load_from_wp_user(\WP_User $wp_user): User{
		$user = new self();
		$user->wp_user = $wp_user;

		if(in_array('administrator', $wp_user->roles)){
			// ADMIN
			$user->backend_user_type = LATEPOINT_USER_TYPE_ADMIN;
			$user->wp_capability = 'manage_options';
		}elseif(in_array(LATEPOINT_WP_AGENT_ROLE, $wp_user->roles)){
			// AGENT
			$user->backend_user_type = LATEPOINT_USER_TYPE_AGENT;
			$user->wp_capability = 'edit_bookings';
			// get connected agent model
      $agent = new \OsAgentModel();
      $agent = $agent->where(['wp_user_id' => $wp_user->ID])->set_limit(1)->get_results_as_models();
			if($agent) $user->agent = $agent;
		}else{
			// see if it's one of custom roles
			$custom_roles = \OsRolesHelper::get_custom_roles();
			foreach($custom_roles as $custom_role){
				if(!empty($wp_user->roles) && in_array($custom_role['wp_role'], $wp_user->roles)){
					$user->backend_user_type = LATEPOINT_USER_TYPE_CUSTOM;
					$user->wp_capability = 'manage_latepoint';
					break;
				}
			}
		}
		$user->set_roles();
		$user->set_capabilities();
		$user->set_allowed_records();
		return $user;
	}

	public function set_roles(){
		if(empty($this->wp_user)) return;
		if($this->wp_user->roles){
			foreach($this->wp_user->roles as $role){
				$this->roles[] = Role::get_from_wp_role($role);
			}
		}
	}

	/*
	 * Check if user has custom permissions set to access actions, instead of using default ones attached to the role
	 */
	public function is_custom_capabilities(): bool{
		return(!empty($this->get_custom_capabilities()));
	}

	/*
	 * Check if user has custom access to model records, instead of using default ones attached to the role
	 */
	public function is_custom_allowed_records(): bool{
		return(!empty($this->get_custom_allowed_records()));
	}


	// PERMITTED ACTIONS
	// -----------------

	public function clear_custom_capabilities(){
		return delete_user_meta($this->wp_user->ID, 'latepoint_custom_capabilities');
	}

	public function set_custom_capabilities($capabilities){
		return update_user_meta($this->wp_user->ID, 'latepoint_custom_capabilities', $capabilities);
	}

	public function get_custom_capabilities(){
		return get_user_meta($this->wp_user->ID, 'latepoint_custom_capabilities', true);
	}

	public function get_capabilities(){
		return $this->capabilities;
	}

	protected function set_capabilities(){
		switch($this->backend_user_type){
			case LATEPOINT_USER_TYPE_ADMIN:
				// admins always get all permissions
				$this->capabilities = \OsRolesHelper::get_all_available_actions_list();
				break;
			case LATEPOINT_USER_TYPE_AGENT:
			case LATEPOINT_USER_TYPE_CUSTOM:
					$custom = $this->get_custom_capabilities();
					if(empty($custom)){
						foreach($this->roles as $role){
							$this->capabilities = array_merge($this->capabilities, $role->get_capabilities());
						}
					}else{
						$this->capabilities = maybe_unserialize($custom);
					}
				break;
		}
	}


	// ALLOWED RECORDS
	// -----------------

	public function clear_custom_allowed_records(){
		return delete_user_meta($this->wp_user->ID, 'latepoint_custom_allowed_records');
	}

	public function set_custom_allowed_records($allowed_records){
		if(empty($allowed_records)){
			return $this->clear_custom_allowed_records();
		}else{
			return update_user_meta($this->wp_user->ID, 'latepoint_custom_allowed_records', $allowed_records);
		}
	}

	public function get_custom_allowed_records(){
		return get_user_meta($this->wp_user->ID, 'latepoint_custom_allowed_records', true);
	}

	public function get_allowed_records(string $model_type, $load_from_db = false){
		if($load_from_db){
			// do not calculate allowed records based on what connections are available, just load from database whatever is set
			$custom = $this->get_custom_allowed_records();
			if($custom){
				$custom_records = maybe_unserialize($custom);
				return !empty($custom_records[$model_type]) ? $custom_records[$model_type] : [];
			}else{
				return LATEPOINT_ALL;
			}
		}else{
			return !empty($this->allowed_records[$model_type]) ? $this->allowed_records[$model_type] : [];
		}
	}

	public function is_single_record_allowed(string $model_type): bool{
		return $this->are_all_records_allowed($model_type) ? false : (count($this->get_allowed_records($model_type)) == 1);
	}

	public function are_all_records_allowed(string $model_type = '', $load_from_db = false): bool{
		if($load_from_db && $model_type){
			$allowed_records = $this->get_allowed_records($model_type, true);
			return ($allowed_records == LATEPOINT_ALL);
		}else{
			if($model_type){
				return ($this->allowed_records[$model_type] == LATEPOINT_ALL);
			}else{
				return (($this->allowed_records['agent'] == LATEPOINT_ALL) && ($this->allowed_records['location'] == LATEPOINT_ALL) && ($this->allowed_records['service'] == LATEPOINT_ALL));
			}
		}
	}

	public function check_if_allowed_record_id($id, string $model_type){
		if(empty($id)) return false;
		if($this->are_all_records_allowed($model_type)) return $id;
		if(array_intersect([$id], $this->get_allowed_records($model_type))) return $id;
		return false;
	}

	public function clean_query_args($args){
		$model_types = ['agent', 'service', 'location'];
		foreach($model_types as $model_type){
			if(empty($args[$model_type.'_id']) && !$this->are_all_records_allowed($model_type)){
				$args[$model_type.'_id'] = $this->get_allowed_records($model_type);
			}
		}
		return $args;
	}

	protected function set_allowed_records(){
		switch($this->backend_user_type){
			case LATEPOINT_USER_TYPE_ADMIN:
				$this->allowed_records['agent'] = LATEPOINT_ALL;
				$this->allowed_records['service'] = LATEPOINT_ALL;
				$this->allowed_records['location'] = LATEPOINT_ALL;
				break;
			case LATEPOINT_USER_TYPE_AGENT:
				if($this->agent){
					$this->allowed_records['agent'] = [$this->agent->id] ?? [];
					$connection = new \OsConnectorModel();
					$connections = $connection->where(['agent_id' => $this->agent->id])->get_results_as_models();

					if($connections){
						foreach($connections as $connection){
							if(!in_array($connection->service_id, $this->allowed_records['service'])) $this->allowed_records['service'][] = $connection->service_id;
							if(!in_array($connection->location_id, $this->allowed_records['location'])) $this->allowed_records['location'][] = $connection->location_id;
						}
					}
				}
				break;
			case LATEPOINT_USER_TYPE_CUSTOM:
				// each user has their own settings, by default they allowed to access ALL, query DB to find custom settings
				$custom = $this->get_custom_allowed_records();
				if(empty($custom)){
					$this->allowed_records['agent'] = LATEPOINT_ALL;
					$this->allowed_records['service'] = LATEPOINT_ALL;
					$this->allowed_records['location'] = LATEPOINT_ALL;
				}else{
					$custom_records = maybe_unserialize($custom);
					$model_types = ['agent', 'service', 'location'];
					$connection = new \OsConnectorModel();
					// (e.g. if specific locations are selected - make sure agents and services are filtered to only have those that are connected to that location)
					foreach($model_types as $model_type){
						if($custom_records[$model_type] != LATEPOINT_ALL){
							$connection->where([$model_type.'_id' => $custom_records[$model_type]]);
						}
					}
					$allowed_connections = $connection->get_results_as_models();
					foreach($allowed_connections as $allowed_connection){
						if(!in_array($allowed_connection->agent_id, $this->allowed_records['agent'])) $this->allowed_records['agent'][] = $allowed_connection->agent_id;
						if(!in_array($allowed_connection->service_id, $this->allowed_records['service'])) $this->allowed_records['service'][] = $allowed_connection->service_id;
						if(!in_array($allowed_connection->location_id, $this->allowed_records['location'])) $this->allowed_records['location'][] = $allowed_connection->location_id;
					}
				}
				break;
		}
	}


	public function get_link_to_settings(): string{
		switch($this->backend_user_type){
			case LATEPOINT_USER_TYPE_ADMIN:
				return \OsRouterHelper::build_link(['settings', 'general']);
			case LATEPOINT_USER_TYPE_AGENT:
				return \OsRouterHelper::build_link(['agents', 'edit_form'], ['id' => $this->agent->id]);
		}
		return '';
	}

	public function get_avatar_url(): string{
		// if backend user logged in - try to get their avatar first, before trying to get customer url
		if($this->backend_user_type){
			if($this->agent) return $this->agent->get_avatar_url();
			if($this->wp_user) return get_avatar_url($this->wp_user->user_email);
		}
		if($this->customer) return $this->customer->get_avatar_url();
		if($this->wp_user) return get_avatar_url($this->wp_user->user_email);
		return '';
	}

	public function get_display_name(){
		if($this->backend_user_type){
			if($this->agent) return $this->agent->full_name;
			if($this->wp_user) return $this->wp_user->display_name;
		}
		if($this->customer) return $this->customer->full_name;
		if($this->wp_user) return $this->wp_user->display_name;
	}

	public function get_user_type_label(){
		$labels = [
			LATEPOINT_USER_TYPE_ADMIN => __('Administrator', 'latepoint'),
			LATEPOINT_USER_TYPE_AGENT => __('Agent', 'latepoint'),
			LATEPOINT_USER_TYPE_CUSTOM => __('Custom', 'latepoint')
		];
		if($this->backend_user_type){
			if($this->backend_user_type == LATEPOINT_USER_TYPE_CUSTOM){
				if($this->roles && ($this->roles[0] instanceof Role)){
					return $this->roles[0]->name;
				}else{
					return $labels[$this->backend_user_type];
				}
			}else{
				return $labels[$this->backend_user_type];
			}
		}elseif($this->customer){
			return __('Customer', 'latepoint');
		}
	}

	public static function get_backend_user_types(){
		$backend_user_types = [LATEPOINT_USER_TYPE_ADMIN, LATEPOINT_USER_TYPE_AGENT, LATEPOINT_USER_TYPE_CUSTOM];
		/**
		 * Get array of user levels that can access backend
		 *
		 * @since 4.7.0
		 * @hook latepoint_get_backend_user_types
		 *
		 * @param {array} $backend_user_types array of user levels (strings) that can access backend
		 * @returns {array} The filtered array of user types
		 */
		return apply_filters('latepoint_get_backend_user_types', $backend_user_types);
	}

	public function has_backend_access(): bool{
		return in_array($this->backend_user_type, self::get_backend_user_types());
	}

	public static function allowed_props(): array{
		return [];
	}
}