<?php

// Exit if accessed directly
defined( 'ABSPATH' ) || exit;

class Wiaas_User_Organization extends WP_User_Taxonomy {

	const TAXONOMY_NAME = 'wiaas-user-organization';
	const TAXONOMY_SLUG = 'users/wiaas-organization';

	public function __construct()
	{
		$args = array(
			'singular' => __('Organization', 'wiaas'),
			'plural' => __('Organizations', 'wiaas'),
			'exclusive' => true,
			'public' => true,
			'show_in_rest' => false,
		);
		$labels = array();
		$caps = array();
		parent::__construct(self::TAXONOMY_NAME, self::TAXONOMY_SLUG, $args, $labels, $caps);

		$this->hooks();
	}

	/**
	 * Add organization specific hooks
	 */
	function hooks() {

		parent::hooks();

		add_action('user_register', array( $this, 'save_terms_for_user' ));

		add_action( 'created_' . self::TAXONOMY_NAME, array( __CLASS__, 'on_organization_added' ));
		add_action( 'pre_delete_term', array( __CLASS__, 'on_taxonomy_term_will_be_deleted' ), 10, 2);
		add_action( 'delete_' . self::TAXONOMY_NAME, array( __CLASS__, 'on_organization_deleted' ));

		add_action('acf/save_post', array(__CLASS__, 'on_organization_roles_maybe_updated'), 20);

		add_action('set_object_terms', array( __CLASS__, 'on_taxonomy_term_assigned' ), 10, 4);
		add_action('deleted_term_relationships', array( __CLASS__, 'on_taxonomy_term_unassigned' ), 10, 3);


		// Remove bulk editor for organizations on users list
        remove_filter( 'admin_notices', array( $this, 'bulk_notice'));
        remove_filter( 'bulk_actions-users', array( $this, 'bulk_actions'));
        remove_filter( 'bulk_actions-users', array( $this, 'bulk_actions_sort'));
        remove_action( 'handle_bulk_actions-users', array( $this, 'handle_bulk_actions'));

        // remove default organization info from profiles
        // it will be handled by custom fields
        remove_action( 'show_user_profile', array( $this, 'edit_user_relationships' ), 99);
        remove_action( 'edit_user_profile', array( $this, 'edit_user_relationships' ), 99 );
	}

	// hooks functions

	/**
	 * Creates corresponding access group for newly created organizational term
	 *
	 * @param $organization_id id of the organization term
	 */
	public static function on_organization_added($organization_id) {
		self::_create_organization_access_group($organization_id);

		do_action('wiaas_organization_created', $organization_id);
	}

	/**
	 * Removes corresponding acces group when organization term is deleted
	 *
	 * @param $term_id - term id that will be deleted
	 * @param $taxonomy - taxonomy to which term belongs (in our case `user-organizations`)
	 */
	public static function on_taxonomy_term_will_be_deleted($term_id, $taxonomy) {
		if ($taxonomy === self::TAXONOMY_NAME) {
			$organization_id = $term_id;
			self::_remove_organization_access_group($organization_id);

			do_action('wiaas_organization_will_be_deleted', $organization_id);
		}
	}

	/**
	 * Removes corresponding acces group when organization term is deleted
	 *
	 * @param $organization_id id of the organization term
	 */
	public static function on_organization_deleted($organization_id) {
		do_action('wiaas_organization_deleted', $organization_id);
	}

	/**
	 * @param string $id acf object id for which data has been updated,
	 *                   for organization it will be in format `term_{$organization_id}`
	 */
	public static function on_organization_roles_maybe_updated($id) {
		if ($_POST['taxonomy'] === self::TAXONOMY_NAME) {
			$roles = get_field('_wiaas_organization_roles', $id);

			//get organization id
			$id = absint(str_replace('term_', '', $id));

			do_action('wiaas_organization_roles_updated', $id, $roles);
		}
	}



	/**
	 * Adds user to corresponding access groups when he is assigned to organization.
	 * User will also be added to child organizations access groups.
	 *
	 * @param $object_id - id of object to which term is assigned (in our case $user_id)
	 * @param $terms - assigned terms (in our case $organizations)
	 * @param $tt_ids - assigned terms ids (in our case $organizations_ids)
	 * @param $taxonomy - taxonomy to which term belongs (in our case `user-organizations`)
	 */
	public static function on_taxonomy_term_assigned($object_id, $terms, $tt_ids, $taxonomy) {
		if ($taxonomy === self::TAXONOMY_NAME) {
			$user_id = $object_id;
			$organization_id = $tt_ids[0];

			self::_add_user_to_access_group($user_id, $organization_id);
		}
	}

	/**
	 * Removes user from corresponding access groups when he is removed from organization.
	 * User will also be removed from child organizations access groups.
	 *
	 * @param $object_id - id of object to which term is assigned (in our case $user_id)
	 * @param $tt_ids - assigned terms ids (in our case $organizations_ids)
	 * @param $taxonomy - taxonomy to which term belongs (in our case `user-organizations`)
	 */
	public static function on_taxonomy_term_unassigned($object_id, $tt_ids, $taxonomy) {
		if ($taxonomy === self::TAXONOMY_NAME) {
			$user_id = $object_id;
			$organization_id = $tt_ids[0];

			self::_remove_user_from_organization_access_groups($user_id, $organization_id);
		}
	}

	/**
	 * Retrieves user organization based on user id
	 *
	 * @param null $user_id
	 * @return mixed
	 */
	public static function get_user_organization_id($user_id = null) {
		if (!isset($user_id)) {
            $user_id = get_current_user_id();
		}
		$organization_id = get_user_meta($user_id, '_wiaas_organization_id', true);

		return empty($organization_id) ? null : (int) $organization_id;
	}

	/**
	 * Assignees post to user organization. Post will be assigned to corresponding access groups.
	 * If user organization has parent organizations, staff from parent organizations will also be able
	 * to access order.
	 *
	 * @param $post_id - custom post id (product, order, ...)
	 * @param $organization_id
	 */
	public static function assign_post_to_organization($post_id, $organization_id) {
		self::_assign_post_to_organization( $post_id, $organization_id );
	}


	// private helper functions

	/**
	 * Retrieves organization object based organization id
	 *
	 * @param $organization_id
	 * @return mixed
	 */
	private static function _get_organization_access_group_id($organization_id) {
		return get_term_meta($organization_id, 'group_id', true);
	}

	/**
	 * Retrieves all access groups ids for organization. This includes corresponding access group
	 * for provided organization and also access groups for all of its child organizations.
	 *
	 * @param $organization_id
	 * @return array
	 */
	private static function _get_organization_all_access_groups_ids($organization_id) {
		$access_groups_ids = array();
		$access_groups_ids[] = self::_get_organization_access_group_id($organization_id);
		$organization_departments_ids = self::_get_organization_departments_ids($organization_id);
		foreach ($organization_departments_ids as $organization_department_id) {
			$access_groups_ids[] = self::_get_organization_access_group_id($organization_department_id);
		}
		return $access_groups_ids;
	}

	/**
	 * Retrieves all departments of organization
	 *
	 * @param $organization_id
	 * @return array|WP_Error
	 */
	private static function _get_organization_departments_ids($organization_id) {
		return get_term_children($organization_id, self::TAXONOMY_NAME);
	}

	/**
	 * Assign custom post to corresponding organizational acccess group.
	 *
	 * @param $post_id
	 * @param $organization_id
	 */
	private static function _assign_post_to_organization($post_id, $organization_id) {
		if (class_exists('Groups_Post_Access')) {
			$access_group_id = self::_get_organization_access_group_id($organization_id);
			Groups_Post_Access::create( array( 'post_id' => $post_id, 'group_id' => $access_group_id ) );
		}
	}


	/**
	 * Create corresponding access group for organization
	 *
	 * @param $organization_id
	 */
	private static function _create_organization_access_group($organization_id) {
		if (class_exists('Groups_Group')) {
			$organization = get_term_by('id', $organization_id, self::TAXONOMY_NAME);
			$access_group_id = Groups_Group::create(array(
				'name' => $organization->name,
			));

			add_term_meta($organization_id, 'group_id', $access_group_id);
		}

	}

	/**
	 * Remove corresponding access group for organization
	 *
	 * @param $organization_id
	 */
	private static function _remove_organization_access_group($organization_id) {
		if (class_exists('Groups_Group')) {
			$access_group_id = self::_get_organization_access_group_id($organization_id);
			Groups_Group::delete($access_group_id);
		}
	}

	/**
	 * Add user to all access groups found in provided organization.
	 *
	 * @param $user_id
	 * @param $organization_id
	 */
	private static function _add_user_to_access_group($user_id, $organization_id) {
		if (class_exists('Groups_User_Group')) {
			$access_groups_ids = self::_get_organization_all_access_groups_ids($organization_id);
			foreach ($access_groups_ids as $access_group_id) {
				Groups_User_Group::create( array( 'user_id' => $user_id, 'group_id' => $access_group_id ) );
			}
		}
	}

	/**
	 * Remove user from all access groups found in provided organization,
	 *
	 * @param $user_id
	 * @param $organization_id
	 */
	private static function _remove_user_from_organization_access_groups($user_id, $organization_id) {
		if (class_exists('Groups_User_Group')) {
			$access_groups_ids = self::_get_organization_all_access_groups_ids($organization_id);
			foreach ($access_groups_ids as $access_group_id) {
				Groups_User_Group::delete($user_id, $access_group_id);
			}
		}
	}
}