<?php
/**
 * Group Memberships Rest Endpoint
 *
 * @package Nisje
 */

namespace Dekode\Nisje\Components\Rest;

defined( 'ABSPATH' ) || die( 'Shame on you' );

/**
 * Groups Memberships Class
 */
class Groups_Memberships_Controller extends \WP_REST_Controller {
	/**
	 * Constructor
	 */
	public function __construct() {
		$this->namespace = nisje_get_rest_namespace();
		$this->rest_base = buddypress()->groups->id;
		$this->hook_base = 'groups_memberships';
	}

	/**
	 * Register routes
	 */
	public function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/join', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'join' ],
				'permission_callback' => [ $this, 'join_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/add-user', [
			'args'   => [
				'current_user_id' => [
					'description' => esc_html__( 'Unique identifier for the current user.', 'nisje' ),
					'type'        => 'integer',
				],
				'id'              => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'add_user' ],
				'permission_callback' => [ $this, 'add_user_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/leave', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'leave' ],
				'permission_callback' => [ $this, 'leave_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/remove-user', [
			'args'   => [
				'current_user_id' => [
					'description' => esc_html__( 'Unique identifier for the current user.', 'nisje' ),
					'type'        => 'integer',
				],
				'id'              => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'remove_user' ],
				'permission_callback' => [ $this, 'remove_user_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/ban', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'ban' ],
				'permission_callback' => [ $this, 'ban_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/unban', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'unban' ],
				'permission_callback' => [ $this, 'unban_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/request/membership', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'request_membership' ],
				'permission_callback' => [ $this, 'request_membership_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/accept/membership', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'accept_membership' ],
				'permission_callback' => [ $this, 'accept_membership_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/reject/membership', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'reject_membership' ],
				'permission_callback' => [ $this, 'reject_membership_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/accept/invite', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'accept_invite' ],
				'permission_callback' => [ $this, 'accept_invite_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/reject/invite', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'reject_invite' ],
				'permission_callback' => [ $this, 'reject_invite_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/promote/admin', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'promote_admin' ],
				'permission_callback' => [ $this, 'promote_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/promote/mod', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'promote_mod' ],
				'permission_callback' => [ $this, 'promote_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/demote', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'demote' ],
				'permission_callback' => [ $this, 'demote_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		$invite_args             = $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE );
		$invite_args['user_ids'] = [
			'context'     => [ 'view' ],
			'description' => esc_html__( 'Array of users to invite.', 'nisje' ),
			'type'        => 'array',
			'items'       => [
				'type' => 'integer',
			],
			'required'    => true,
		];

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/invite', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'invite' ],
				'permission_callback' => [ $this, 'invite_permissions_check' ],
				'args'                => $invite_args,
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/uninvite', [
			'args'   => [
				'id' => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'uninvite' ],
				'permission_callback' => [ $this, 'uninvite_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/update-user-role', [
			'args'   => [
				'id'              => [
					'description' => esc_html__( 'Unique identifier for the object.', 'nisje' ),
					'type'        => 'integer',
				],
				'current_user_id' => [
					'description' => esc_html__( 'Unique identifier for the current user.', 'nisje' ),
					'type'        => 'integer',
				],
				'username'        => [
					'description' => esc_html__( 'Username for the user.', 'nisje' ),
					'type'        => 'string',
				],
				'role'            => [
					'description' => esc_html__( 'Role for the user.', 'nisje' ),
					'type'        => 'string',
				],
			],
			[
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'update_user_role' ],
				'permission_callback' => [ $this, 'update_user_role_permissions_check' ],
				'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );

	}

	/**
	 * Base permission check
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @param string          $route   Endpoint route.
	 *
	 * @return WP_Error|boolean
	 */
	private function base_permissions_check( $request, $route = '' ) {
		$auth = nisje_validate_rest_authentication( $this->hook_base, $route );
		if ( is_wp_error( $auth ) ) {
			return $auth;
		}

		$user_id = get_current_user_id();
		if ( ! empty( $request['user_id'] ) ) {
			$user_id = (int) $request['user_id'];
		}

		$excluded = nisje_validate_rest_excluded( $user_id );
		if ( is_wp_error( $excluded ) ) {
			return $excluded;
		}

		$group_id = (int) $request['id'];

		$group = nisje_get_group( $group_id, true );
		if ( is_wp_error( $group ) ) {
			return $group;
		}

		$data = [
			'user_id' => $user_id,
			'group'   => $group,
		];

		return apply_filters( "nisje_rest_{$this->hook_base}_{$route}_permission", $data, $request );
	}

	/**
	 * Build args.
	 *
	 * @param  string          $type    Type.
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return array $args Array of arguments.
	 */
	private function build_args( $type, $request ) {
		$user_id = get_current_user_id();
		if ( ! empty( $request['user_id'] ) ) {
			$user_id = $request['user_id'];
		}

		$args = [
			'group_id' => (int) $request['id'],
			'user_id'  => (int) $user_id,
			'type'     => $type,
		];

		$args = apply_filters( "nisje_rest_{$this->hook_base}_args", $args, $request );

		return $args;
	}

	/**
	 * Build return object.
	 *
	 * @param  array $args Array of arguments.
	 *
	 * @return object $obj Object with populated values.
	 */
	private function build_return_value( $args ) {
		$obj = new \stdClass();

		$obj->user_id  = $args['user_id'];
		$obj->group_id = $args['group_id'];
		$obj->type     = $args['type'];

		if ( isset( $args['invited_ids'] ) ) {
			$obj->invited_ids = $args['invited_ids'];
		}

		if ( isset( $args['not_invited_ids'] ) ) {
			$obj->not_invited_ids = $args['not_invited_ids'];
		}

		return $obj;
	}

	/**
	 * Build response.
	 *
	 * @param  array           $args    Array with arguments.
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	private function build_response( $args, $request ) {
		$obj = $this->build_return_value( $args );

		do_action( "nisje_rest_{$this->hook_base}_after", $obj, $request );

		$request->set_param( 'context', 'view' );
		$response = $this->prepare_item_for_response( $obj, $request );
		$response = rest_ensure_response( $response );
		$response->set_status( 201 );
		$response->header( 'Location', rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );

		return $response;
	}

	/**
	 * Check if a given request has access to join a group.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function join_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'join' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( get_current_user_id() !== $user_id ) {
			return new \WP_Error( 'nisje_rest_cannot_invite', esc_html__( 'You are not allowed to perform actions as this user.', 'nisje' ), [
				'status' => rest_authorization_required_code(),
			] );
		}

		// Check if the user is already a member.
		if ( groups_is_user_member( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'User is already a member of this group.', 'nisje' ), [
				'status' => 400,
			] );
		}

		// Check if the user is banned.
		if ( groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is banned from this group.', 'nisje' ), [
				'status' => 400,
			] );
		}

		// User wants to join a group that is not public.
		if ( 'public' !== $group->status ) {
			if ( ! groups_check_user_has_invite( $user_id, $group->id ) ) {
				return new \WP_Error( 'nisje_rest_user_missing_invite', esc_html__( 'You are not invited to join this group.', 'nisje' ), [
					'status' => 400,
				] );
			}
		}

		return true;
	}

	/**
	 * Join group
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function join( $request ) {
		$args = $this->build_args( 'join', $request );

		if ( ! groups_join_group( $args['group_id'], $args['user_id'] ) ) {
			return new \WP_Error( 'nisje_rest_join_error', esc_html__( 'The user could not join the group. Please contact the administrator with error code 2701.', 'nisje' ), [
				'status' => 500,
			] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to add a user to a group.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function add_user_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'add-user' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$current_user_id = $request['current_user_id'];
		$group           = $args['group'];
		$group_id        = $group->id;
		$user_id         = $request['user_id'];

		if ( ! $current_user_id ) {
			return new \WP_Error( 'nisje_no_current_user_id', esc_html__( 'No current user id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $group_id ) {
			return new \WP_Error( 'nisje_no_group_id', esc_html__( 'No group id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $user_id ) {
			return new \WP_Error( 'nisje_no_user_id', esc_html__( 'No user id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( get_current_user_id() !== $current_user_id ) {
			return new \WP_Error( 'nisje_rest_cannot_invite', esc_html__( 'You are not allowed to perform actions as this user.', 'nisje' ), [
				'status' => rest_authorization_required_code(),
			] );
		}

		// Check if the user is already a member.
		if ( groups_is_user_member( $user_id, $group_id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'User is already a member of this group.', 'nisje' ), [
				'status' => 400,
			] );
		}

		// Check if the user is banned.
		if ( groups_is_user_banned( $user_id, $group_id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is banned from this group.', 'nisje' ), [
				'status' => 400,
			] );
		}

		// User wants to join a group that is not public.
		if ( 'public' !== $group->status ) {
			if ( ! groups_is_user_admin( $current_user_id, $group_id ) ) {
				if ( ! groups_check_user_has_invite( $user_id, $group_id ) ) {
					return new \WP_Error( 'nisje_rest_user_missing_invite', esc_html__( 'The user is not invited to join this group.', 'nisje' ), [
						'status' => 400,
					] );
				}
			}
		}

		return true;
	}

	/**
	 * Add user to group
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function add_user( $request ) {
		$args = $this->build_args( 'add-user', $request );

		if ( ! groups_join_group( $args['group_id'], $args['user_id'] ) ) {
			return new \WP_Error( 'nisje_rest_join_error', esc_html__( 'The user could not join the group. Please contact the administrator with error code 2701.', 'nisje' ), [
				'status' => 500,
			] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to leave a group.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function leave_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'leave' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( get_current_user_id() !== $user_id ) {
			return new \WP_Error( 'nisje_rest_cannot_leave', esc_html__( 'You are not allowed to perform actions as this user.', 'nisje' ), [
				'status' => rest_authorization_required_code(),
			] );
		}

		if ( ! groups_is_user_member( $user_id, $group->id ) ) {
			if ( 'hidden' === $group->status ) {
				return new \WP_Error( 'nisje_rest_user_member', esc_html__( 'Group not found.', 'nisje' ), [
					'status' => 404,
				] );
			} else {
				return new \WP_Error( 'nisje_rest_user_member', esc_html__( 'You are not member of this group.', 'nisje' ), [
					'status' => 400,
				] );
			}
		}

		if ( function_exists( 'nisje_get_main_group' ) ) {
			$main_group = nisje_get_main_group( $user_id );
			if ( $group->id === $main_group ) {
				return new \WP_Error( 'nisje_rest_user_leave_main_group', esc_html__( 'You are not allowed to leave your main group.', 'nisje' ), [
					'status' => 400,
				] );
			}
		}

		// Stop sole admins from abandoning their group.
		$group_admins = groups_get_group_admins( $group->id );
		if ( 1 === count( $group_admins ) && $group_admins[0]->user_id === $user_id ) {
			return new \WP_Error( 'nisje_rest_user_last_admin', esc_html__( 'This group must have at least one admin.', 'nisje' ), [
				'status' => 400,
			] );
		}

		return true;
	}

	/**
	 * Leave group
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function leave( $request ) {
		$args = $this->build_args( 'leave', $request );

		if ( ! groups_leave_group( $args['group_id'], $args['user_id'] ) ) {
			return new \WP_Error( 'nisje_rest_leave_error', esc_html__( 'There was an error leaving the group. Please contact the administrator with error code 2702.', 'nisje' ), [
				'status' => 500,
			] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to remove a member from a group.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function remove_user_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'remove-user' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$current_user_id = $request['current_user_id'];
		$group           = $args['group'];
		$group_id        = $group->id;
		$user_id         = $request['user_id'];

		if ( ! groups_is_user_admin( get_current_user_id(), $group_id ) ) {
			return new \WP_Error( 'nisje_only_admins_can_remove', esc_html__( 'Only group admins can remove users.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $current_user_id ) {
			return new \WP_Error( 'nisje_no_current_user_id', esc_html__( 'No current user id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $group_id ) {
			return new \WP_Error( 'nisje_no_group_id', esc_html__( 'No group id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $user_id ) {
			return new \WP_Error( 'nisje_no_user_id', esc_html__( 'No user id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( get_current_user_id() !== $current_user_id ) {
			return new \WP_Error( 'nisje_rest_cannot_leave', esc_html__( 'You are not allowed to perform actions as this user.', 'nisje' ), [
				'status' => rest_authorization_required_code(),
			] );
		}

		if ( ! groups_is_user_member( $user_id, $group->id ) ) {
			if ( 'hidden' === $group->status ) {
				return new \WP_Error( 'nisje_rest_user_member', esc_html__( 'Group not found.', 'nisje' ), [
					'status' => 404,
				] );
			} else {
				return new \WP_Error( 'nisje_rest_user_member', esc_html__( 'User is not member of this group.', 'nisje' ), [
					'status' => 400,
				] );
			}
		}

		if ( function_exists( 'nisje_get_main_group' ) ) {
			$main_group = nisje_get_main_group( $user_id );
			if ( $group->id === $main_group ) {
				return new \WP_Error( 'nisje_rest_user_leave_main_group', esc_html__( 'User is not allowed to leave your main group.', 'nisje' ), [
					'status' => 400,
				] );
			}
		}

		// Stop sole admins from abandoning their group.
		$group_admins = groups_get_group_admins( $group->id );
		if ( 1 === count( $group_admins ) && $group_admins[0]->user_id === $user_id ) {
			return new \WP_Error( 'nisje_rest_user_last_admin', esc_html__( 'This group must have at least one admin.', 'nisje' ), [
				'status' => 400,
			] );
		}

		return true;
	}

	/**
	 * Leave group
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function remove_user( $request ) {
		$args = $this->build_args( 'remove-user', $request );

		if ( ! groups_leave_group( $args['group_id'], $args['user_id'] ) ) {
			return new \WP_Error( 'nisje_rest_leave_error', esc_html__( 'There was an error removing user from the group. Please contact the administrator with error code 2702.', 'nisje' ), [
				'status' => 500,
			] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to reject membership requests.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function ban_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'ban' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( ! groups_is_user_admin( get_current_user_id(), $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'Only administrators can ban users.', 'nisje' ), [ 'status' => 401 ] );
		}

		if ( get_current_user_id() === $user_id ) {
			return new \WP_Error( 'nisje_rest_ban_yourself', esc_html__( 'You cannot ban yourself.', 'nisje' ), [ 'status' => 400 ] );
		}

		if ( groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is already banned from this group.', 'nisje' ), [ 'status' => 401 ] );
		}

		if ( ! groups_is_user_member( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_member', esc_html__( 'The user is not a member of this group.', 'nisje' ), [ 'status' => 400 ] );
		}

		return true;
	}

	/**
	 * Ban User
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function ban( $request ) {
		$args = $this->build_args( 'ban', $request );

		if ( ! groups_ban_member( $args['user_id'], $args['group_id'] ) ) {
			return new \WP_Error( 'nisje_rest_ban_error', esc_html__( 'There was an error banning the member from this group. Please contact the administrator with error code 2703.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to reject membership requests.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function unban_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'unban' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( ! groups_is_user_admin( get_current_user_id(), $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'Only administrators can unban users for this group.', 'nisje' ), [ 'status' => 401 ] );
		}

		if ( get_current_user_id() === $user_id ) {
			return new \WP_Error( 'nisje_rest_unban_yourself', esc_html__( 'You cannot unban yourself.', 'nisje' ), [ 'status' => 400 ] );
		}

		if ( ! groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is not banned from this group.', 'nisje' ), [ 'status' => 401 ] );
		}

		return true;
	}

	/**
	 * Ban User
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function unban( $request ) {
		$args = $this->build_args( 'unban', $request );

		if ( ! groups_unban_member( $args['group_id'], $args['user_id'] ) ) {
			return new \WP_Error( 'nisje_rest_unban_error', esc_html__( 'There was an error unbanning the member from the group. Please contact the administrator with error code 2704.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to ban user from group.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function request_membership_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'request_membership' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( get_current_user_id() !== $user_id ) {
			return new \WP_Error( 'nisje_rest_cannot_invite', esc_html__( 'You are not allowed to perform actions as this user.', 'nisje' ), [ 'status' => rest_authorization_required_code() ] );
		}

		// Prevent duplicate requests.
		if ( groups_check_for_membership_request( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'You have a pending request.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is already a member.
		if ( groups_is_user_member( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'User is already a member of this group.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is banned.
		if ( groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is banned from this group.', 'nisje' ), [ 'status' => 400 ] );
		}

		return true;
	}

	/**
	 * Request membership
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function request_membership( $request ) {
		$args = $this->build_args( 'request-membership', $request );

		// Check if the user is already invited - if so, simply accept invite.
		if ( groups_check_user_has_invite( $args['user_id'], $args['group_id'] ) ) {
			if ( ! groups_accept_invite( $args['user_id'], $args['group_id'] ) ) {
				return new \WP_Error( 'nisje_rest_accept_invite_error', esc_html__( 'There was an error accepting invitation for this user. Please contact the administrator with error code 2705.', 'nisje' ), [ 'status' => 500 ] );
			}
		} elseif ( ! groups_send_membership_request( $args['user_id'], $args['group_id'] ) ) {
			return new \WP_Error( 'nisje_rest_request_membership_error', esc_html__( 'There was an error sending a membership request. Please contact the administrator with error code 2706.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to accept membership request.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function accept_membership_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'accept_membership' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( ! groups_is_user_admin( get_current_user_id(), $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'Only administrators can accept memberships.', 'nisje' ), [ 'status' => 401 ] );
		}

		if ( get_current_user_id() === $user_id ) {
			return new \WP_Error( 'nisje_rest_accept_yourself', esc_html__( 'You cannot accept yourself.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is already a member.
		if ( groups_is_user_member( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'User is already a member of group.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is banned.
		if ( groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is banned from this group.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if user has request.
		if ( ! groups_check_for_membership_request( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_missing_request', esc_html__( 'There is no pending requests for this user.', 'nisje' ), [ 'status' => 400 ] );
		}

		return true;
	}

	/**
	 * Accept membership.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function accept_membership( $request ) {
		$args = $this->build_args( 'accept-membership', $request );

		$membership = groups_check_for_membership_request( $args['user_id'], $args['group_id'] );
		if ( ! groups_accept_membership_request( $membership, $args['user_id'], $args['group_id'] ) ) {
			return new \WP_Error( 'nisje_rest_accept_membership_error', esc_html__( 'There was an error accepting the membership request. Please contact the administrator with error code 2707.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to reject membership requests.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function reject_membership_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'reject_membership' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		// Prevent duplicate requests.
		if ( ! groups_is_user_admin( get_current_user_id(), $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'Only administrators can reject memberships.', 'nisje' ), [ 'status' => 401 ] );
		}

		if ( get_current_user_id() === $user_id ) {
			return new \WP_Error( 'nisje_rest_accept_yourself', esc_html__( 'You cannot reject yourself.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if user has request.
		if ( ! groups_check_for_membership_request( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_missing_request', esc_html__( 'There is no pending requests for this user.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is already a member.
		if ( groups_is_user_member( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'User is already a member or banned from group.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is banned.
		if ( groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is banned from this group.', 'nisje' ), [ 'status' => 400 ] );
		}

		return true;
	}

	/**
	 * Reject membership.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function reject_membership( $request ) {
		$args = $this->build_args( 'reject-membership', $request );

		$membership = groups_check_for_membership_request( $args['user_id'], $args['group_id'] );
		if ( ! groups_reject_membership_request( $membership, $args['user_id'], $args['group_id'] ) ) {
			return new \WP_Error( 'nisje_rest_reject_membership_error', esc_html__( 'There was an error rejecting membership. Please contact the administrator with error code 2708.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to accept invite.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function accept_invite_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'accept_invite' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( get_current_user_id() !== $user_id ) {
			return new \WP_Error( 'nisje_rest_cannot_invite', esc_html__( 'You are not allowed to perform actions as this user.', 'nisje' ), [ 'status' => rest_authorization_required_code() ] );
		}

		// Check if the user is already a member.
		if ( groups_is_user_member( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'User is already a member of group.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is banned.
		if ( groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is banned from this group.', 'nisje' ), [ 'status' => 400 ] );
		}

		if ( ! groups_check_user_has_invite( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_missing_invite', esc_html__( 'This user do not have an invite to this group.', 'nisje' ), 404 );
		}

		return true;
	}

	/**
	 * Accept invite.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function accept_invite( $request ) {
		$args = $this->build_args( 'accept-invite', $request );

		if ( ! groups_accept_invite( $args['user_id'], $args['group_id'] ) ) {
			return new \WP_Error( 'nisje_rest_accept_invite_error', esc_html__( 'The invitation was not accepted. Please contact the administrator with error code 2101.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to reject invite
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function reject_invite_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'reject_invite' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( get_current_user_id() !== $user_id ) {
			return new \WP_Error( 'nisje_rest_cannot_invite', esc_html__( 'You are not allowed to perform actions as this user.', 'nisje' ), [ 'status' => rest_authorization_required_code() ] );
		}

		if ( ! groups_check_user_has_invite( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_missing_invite', esc_html__( 'This user do not have an invite to this group.', 'nisje' ), 404 );
		}

		return true;
	}

	/**
	 * Reject invite
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function reject_invite( $request ) {
		$args = $this->build_args( 'reject-invite', $request );

		if ( ! groups_reject_invite( $args['user_id'], $args['group_id'] ) ) {
			return new \WP_Error( 'nisje_rest_reject_invite_error', esc_html__( 'The invitation was not rejected. Please contact the administrator with error code 2102.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to promote user
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function promote_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'promote' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( ! groups_is_user_admin( get_current_user_id(), $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'Only administrators can promote.', 'nisje' ), [ 'status' => 401 ] );
		}

		if ( get_current_user_id() === $user_id ) {
			return new \WP_Error( 'nisje_rest_promote_yourself', esc_html__( 'You cannot promote yourself.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is already a member.
		if ( ! groups_is_user_member( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'User is not member of group.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is banned.
		if ( groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'The user is banned from this group.', 'nisje' ), [ 'status' => 400 ] );
		}

		return true;
	}

	/**
	 * Promote admin
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function promote_admin( $request ) {
		$args = $this->build_args( 'promote-admin', $request );

		if ( ! groups_promote_member( $args['user_id'], $args['group_id'], 'admin' ) ) {
			return new \WP_Error( 'nisje_rest_promote_admin_error', esc_html__( 'Could not promote user to admnistrator. Please contact the administrator with error code 2801.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Promote admin
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function promote_mod( $request ) {
		$args = $this->build_args( 'promote-mod', $request );

		if ( ! groups_promote_member( $args['user_id'], $args['group_id'], 'mod' ) ) {
			return new \WP_Error( 'nisje_rest_promote_mod_error', esc_html__( 'Could not promote user to moderator. Please contact the administrator with error code 2801.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to demote user
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function demote_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'demote' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( ! groups_is_user_admin( get_current_user_id(), $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_not_allowed', esc_html__( 'Only administrators can promote.', 'nisje' ), [ 'status' => 401 ] );
		}

		if ( get_current_user_id() === $user_id ) {
			return new \WP_Error( 'nisje_rest_demote_yourself', esc_html__( 'You cannot demote yourself.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is already a member.
		if ( ! groups_is_user_member( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_not_in_group', esc_html__( 'User is not member of group.', 'nisje' ), [ 'status' => 400 ] );
		}

		// Check if the user is banned.
		if ( groups_is_user_banned( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_banned', esc_html__( 'The user is banned from this group.', 'nisje' ), [ 'status' => 400 ] );
		}

		return true;
	}

	/**
	 * Demote user
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function demote( $request ) {
		$args = $this->build_args( 'demote', $request );

		if ( ! groups_demote_member( $args['user_id'], $args['group_id'] ) ) {
			return new \WP_Error( 'nisje_rest_demote_error', esc_html__( 'Could not demote user. Please contact the administrator with error code 2802.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to invite user.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function invite_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'invite' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$inviter_id = $args['user_id'];
		$group      = $args['group'];

		$user_ids = $request['user_ids'];

		if ( empty( $user_ids ) ) {
			return new \WP_Error( 'nisje_rest_missing_users', esc_html__( 'Missing user ids.', 'nisje' ), [ 'status' => 404 ] );
		}

		if ( get_current_user_id() !== $inviter_id ) {
			return new \WP_Error( 'nisje_rest_cannot_invite', esc_html__( 'You are not allowed to invite as this user.', 'nisje' ), [ 'status' => rest_authorization_required_code() ] );
		}

		if ( ! bp_groups_user_can_send_invites( $group->id, $inviter_id ) ) {
			return new \WP_Error( 'nisje_rest_user_cannot_invite', esc_html__( 'Sorry, you are not allowed to invite people to this group.', 'nisje' ), [ 'status' => rest_authorization_required_code() ] );
		}

		return true;
	}

	/**
	 * Invite users
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function invite( $request ) {
		$args     = $this->build_args( 'invite', $request );
		$user_ids = $request['user_ids'];

		if ( ! empty( $user_ids ) ) {
			foreach ( $user_ids as $user_id ) {
				$invited = groups_invite_user( [
					'user_id'    => $user_id,
					'group_id'   => $args['group_id'],
					'inviter_id' => $args['user_id'],
				] );

				if ( $invited ) {
					$args['invited_ids'][] = $user_id;
				} else {
					$args['not_invited_ids'][] = $user_id;
				}
			}
		}

		// Send the invites.
		groups_send_invites( $args['user_id'], $args['group_id'] );
		do_action( 'nisje_send_group_invites', $args['group_id'], $args['invited_ids'], $args['user_id'] );

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to uninvite user.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function uninvite_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'uninvite' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$user_id = $args['user_id'];
		$group   = $args['group'];

		if ( ! bp_groups_user_can_send_invites( $group->id, $user_id ) ) {
			return new \WP_Error( 'nisje_rest_user_cannot_uninvite', esc_html__( 'Sorry, you are not allowed to uninvite people to this group.', 'nisje' ), [ 'status' => rest_authorization_required_code() ] );
		}

		if ( ! groups_check_user_has_invite( $user_id, $group->id ) ) {
			return new \WP_Error( 'nisje_rest_user_missing_invite', esc_html__( 'This user do not have an invite to this group.', 'nisje' ), 404 );
		}

		return true;
	}

	/**
	 * Uninvite user.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function uninvite( $request ) {
		$args = $this->build_args( 'uninvite', $request );

		if ( ! groups_uninvite_user( $args['user_id'], $args['group_id'] ) ) {
			return new \WP_Error( 'nisje_rest_uninvite_user', esc_html__( 'We could not uninvite the user. Please contact the administrator with error code 2103.', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Check if a given request has access to update a user's role within a group.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|boolean
	 */
	public function update_user_role_permissions_check( $request ) {
		$args = $this->base_permissions_check( $request, 'update-user-role' );
		if ( is_wp_error( $args ) ) {
			return $args;
		}

		$current_user_id = $request['current_user_id'];
		$group           = $args['group'];
		$group_id        = $group->id;
		$user_id         = $args['user_id'];
		$role            = $request['role'];

		if ( groups_is_user_admin( $user_id, $group_id ) ) {
			$current_role = 'admin';
		} elseif ( groups_is_user_mod( $user_id, $group_id ) ) {
			$current_role = 'mod';
		} elseif ( groups_is_user_banned( $user_id, $group_id ) ) {
			$current_role = 'banned';
		} else {
			$current_role = 'member';
		}

		if ( ! groups_is_user_admin( get_current_user_id(), $group_id ) ) {
			return new \WP_Error( 'nisje_only_admins_update_role', esc_html__( 'Only group admins can update roles.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $current_user_id ) {
			return new \WP_Error( 'nisje_no_current_user_id', esc_html__( 'No current user id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $group_id ) {
			return new \WP_Error( 'nisje_no_group_id', esc_html__( 'No group id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $user_id ) {
			return new \WP_Error( 'nisje_no_user_id', esc_html__( 'No user id passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( ! $role ) {
			return new \WP_Error( 'nisje_no_role', esc_html__( 'No role passed.', 'nisje' ), [
				'status' => 400,
			] );
		}

		if ( get_current_user_id() !== $current_user_id ) {
			return new \WP_Error( 'nisje_rest_cannot_invite', esc_html__( 'You are not allowed to perform actions as this user.', 'nisje' ), [
				'status' => rest_authorization_required_code(),
			] );
		}

		// Check if the user is a member or banned.
		if ( ! groups_is_user_member( $user_id, $group_id ) && ! groups_is_user_banned( $user_id, $group_id ) ) {
			return new \WP_Error( 'nisje_rest_duplicate_user_request', esc_html__( 'User is not a member of this group.', 'nisje' ), [
				'status' => 400,
			] );
		}

		// Check if new role is the same as the current.
		if ( $current_role === $role ) {
			return new \WP_Error( 'nisje_already_has_role', esc_html__( 'User already has this role.', 'nisje' ), [
				'status' => 400,
			] );
		}

		return true;
	}

	/**
	 * Update a user's role within a group.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function update_user_role( $request ) {
		$args     = $this->build_args( 'update-user-role', $request );
		$group_id = $request['group_id'];
		$user_id  = $args['user_id'];
		$role     = $request['role'];

		if ( groups_is_user_admin( $user_id, $group_id ) ) {
			$current_role = 'admin';
		} elseif ( groups_is_user_mod( $user_id, $group_id ) ) {
			$current_role = 'mod';
		} elseif ( groups_is_user_banned( $user_id, $group_id ) ) {
			$current_role = 'banned';
		} else {
			$current_role = 'member';
		}

		// Then set specified role.
		switch ( $current_role ) {
			case 'banned':
				// Unban first.
				if ( ! groups_unban_member( $user_id, $group_id ) ) {
					return new \WP_Error( 'nisje_rest_unban_error', esc_html__( 'There was an error unbanning the member from the group. Please contact the administrator with error code 2704.', 'nisje' ), [ 'status' => 500 ] );
				}
				// Adjust role.
				if ( 'admin' === $role ) {
					if ( ! groups_promote_member( $user_id, $group_id, $role ) ) {
						return new \WP_Error( 'nisje_rest_promote_admin_error', esc_html__( 'Could not promote user to admin. Please contact the administrator with error code 2801.', 'nisje' ), [ 'status' => 500 ] );
					}
				} elseif ( 'mod' === $role ) {
					if ( ! groups_promote_member( $user_id, $group_id, $role ) ) {
						return new \WP_Error( 'nisje_rest_promote_mod_error', esc_html__( 'Could not promote user to mod. Please contact the administrator with error code 2801.', 'nisje' ), [ 'status' => 500 ] );
					}
				}
				break;

			case 'member':
				if ( ! groups_promote_member( $user_id, $group_id, $role ) ) {
					return new \WP_Error( 'nisje_rest_promote_mod_error', esc_html__( 'Could not promote user. Please contact the administrator with error code 2801.', 'nisje' ), [ 'status' => 500 ] );
				}
				break;

			case 'admin':
				// Stop sole admins from abandoning their group.
				$group_admins = groups_get_group_admins( $group_id );
				if ( 1 === count( $group_admins ) && $group_admins[0]->user_id === $user_id ) {
					return new \WP_Error( 'nisje_rest_user_last_admin', esc_html__( 'This group must have at least one admin.', 'nisje' ), [
						'status' => 400,
					] );
				}

				if ( 'member' === $role ) {
					if ( ! groups_demote_member( $user_id, $group_id ) ) {
						return new \WP_Error( 'nisje_rest_demote_error', esc_html__( 'Could not demote user. Please contact the administrator with error code 2802.', 'nisje' ), [ 'status' => 500 ] );
					}
				} elseif ( 'mod' === $role ) {
					if ( ! groups_promote_member( $user_id, $group_id, $role ) ) {
						return new \WP_Error( 'nisje_rest_promote_mod_error', esc_html__( 'Could not promote user to mod. Please contact the administrator with error code 2801.', 'nisje' ), [ 'status' => 500 ] );
					}
				} else {
					return new \WP_Error( 'nisje_update_user_role_error', esc_html__( 'Could not update user role', 'nisje' ), [ 'status' => 500 ] );
				}
				break;

			case 'mod':
				if ( 'member' === $role ) {
					if ( ! groups_demote_member( $user_id, $group_id ) ) {
						return new \WP_Error( 'nisje_rest_demote_error', esc_html__( 'Could not demote user. Please contact the administrator with error code 2802.', 'nisje' ), [ 'status' => 500 ] );
					}
				} elseif ( 'admin' === $role ) {
					if ( ! groups_promote_member( $user_id, $group_id, $role ) ) {
						return new \WP_Error( 'nisje_rest_promote_admin_error', esc_html__( 'Could not promote user to admin. Please contact the administrator with error code 2801.', 'nisje' ), [ 'status' => 500 ] );
					}
				} else {
					return new \WP_Error( 'nisje_update_user_role_error', esc_html__( 'Could not update user role', 'nisje' ), [ 'status' => 500 ] );
				}
				break;

			default:
				return new \WP_Error( 'nisje_update_user_role_error', esc_html__( 'Could not update user role', 'nisje' ), [ 'status' => 500 ] );
		}

		return $this->build_response( $args, $request );
	}

	/**
	 * Prepares the return value
	 *
	 * @param stdClass        $item    Object.
	 * @param WP_REST_Request $request The request.
	 * @param boolean         $is_raw  Optional, not used. Defaults to false.
	 *
	 * @return WP_REST_Response
	 */
	public function prepare_item_for_response( $item, $request, $is_raw = false ) {
		$schema = $this->get_item_schema();

		$data = [];

		if ( ! empty( $schema['properties']['user_id'] ) ) {
			$data['user_id'] = (int) $item->user_id;
		}

		if ( ! empty( $schema['properties']['group_id'] ) ) {
			$data['group_id'] = (int) $item->group_id;
		}

		if ( ! empty( $schema['properties']['type'] ) ) {
			$data['type'] = $item->type;
		}

		if ( isset( $item->invited_ids ) ) {
			$data['invited_ids'] = $item->invited_ids;
		}

		if ( isset( $item->not_invited_ids ) ) {
			$data['not_invited_ids'] = $item->not_invited_ids;
		}

		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';

		$data = $this->add_additional_fields_to_object( $data, $request );
		$data = $this->filter_response_by_context( $data, $context );

		$response = rest_ensure_response( $data );
		$response->add_links( $this->prepare_links( $item ) );

		/**
		 * Filter value returned from the API.
		 */
		return apply_filters( "nisje_rest_prepare_{$this->hook_base}_value", $response, $item, $request );
	}

	/**
	 * Prepare links for the request.
	 *
	 * @param array $item Item.
	 *
	 * @return array Links for the given plugin.
	 */
	protected function prepare_links( $item ) {
		$base = sprintf( '/%s/%s/', $this->namespace, $this->rest_base );

		// Entity meta.
		$links = [
			'group'  => [
				'href' => rest_url( '/' . $this->namespace . '/groups/' . $item->group_id ),
			],
			'member' => [
				'href' => rest_url( '/' . $this->namespace . '/members/' . $item->user_id ),
			],
		];

		return $links;
	}

	/**
	 * Get the plugin schema, conforming to JSON Schema.
	 *
	 * @return array
	 */
	public function get_item_schema() {
		$schema = [
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => $this->hook_base,
			'type'       => 'object',
			'properties' => [
				'user_id'  => [
					'context'     => [ 'view' ],
					'description' => esc_html__( 'A unique alphanumeric ID for user.', 'nisje' ),
					'type'        => 'integer',
				],
				'group_id' => [
					'context'     => [ 'view' ],
					'description' => esc_html__( 'A unique alphanumeric ID for the group.', 'nisje' ),
					'type'        => 'integer',
					'readonly'    => true,
				],
				'type'     => [
					'context'     => [ 'view' ],
					'description' => esc_html__( 'Membership type.', 'nisje' ),
					'type'        => 'string',
					'enum'        => [
						'join',
						'leave',
						'ban',
						'unban',
						'request-membership',
						'accept-membership',
						'reject-membership',
						'accept-invite',
						'reject-invite',
						'promote-admin',
						'promote-mod',
						'demote',
						'invite',
						'uninvite',
					],
					'readonly'    => true,
				],
			],
		];

		return $this->add_additional_fields_schema( $schema );
	}
}
