<?php
/**
 * Block Class.
 *
 * Handles front-end output of blocks.
 *
 * @package   MembersBlockPermissions
 * @author    Justin Tadlock <justintadlock@gmail.com>
 * @copyright 2019, Justin Tadlock
 * @link      https://themehybrid.com/plugins/members-block-permissions
 * @license   https://www.gnu.org/licenses/gpl-2.0.html GPL-2.0-or-later
 */

namespace Members\BlockPermissions;

use WP_User;

/**
 * Block component class.
 *
 * @since  1.0.0
 * @access public
 */
class Block {

	/**
	 * Bootstraps the component.
	 *
	 * @since  1.0.0
	 * @access public
	 * @return void
	 */
	public function boot() {
		add_filter( 'pre_render_block', [ $this, 'preRenderBlock' ], PHP_INT_MAX, 2 );
	}

	/**
	 * Short-circuits block rendering on the front end if the user doesn't
	 * have permission view the block.
	 *
	 * @since  1.0.0
	 * @access public
	 * @param  string|null  $pre_render  Returning anything other than null will short-circuit the block.
	 * @param  array        $block       The block data.
	 * @return mixed
	 */
	public function preRenderBlock( $pre_render, $block ) {

		// Bail if we're in the admin or there are no block attributes.
		if ( is_admin() || ! isset( $block['attrs'] ) ) {
			return $pre_render;
		}

		// Bail if there isn't a condition set.
		if ( ! isset( $block['attrs']['blockPermissionsCondition'] ) || ! $block['attrs']['blockPermissionsCondition'] ) {
			return $pre_render;
		}

		// Bail if there isn't a type set.
		if ( ! isset( $block['attrs']['blockPermissionsType'] ) || ! $block['attrs']['blockPermissionsType'] ) {
			return $pre_render;
		}

		// Gets the permissions type.
		$type = $block['attrs']['blockPermissionsType'];

		// Assume that we will render this block by default.
		$maybe_render = true;

		// Check the permission type to determine whether we will render
		// the block.
		if ( 'user-status' === $type ) {
			$maybe_render = $this->checkUserStatus( $block );
		} elseif ( 'role' === $type ) {
			$maybe_render = $this->checkRole( $block );
		} elseif ( 'cap' === $type ) {
			$maybe_render = $this->checkCap( $block );
		}

		// If the block should not be rendered.
		if ( ! $maybe_render ) {

			// Set to an empty string by default, which will short-
			// circuit the block output.
			$pre_render = '';

			// Get the error message.
			$message = isset( $block['attrs']['blockPermissionsMessage'] )
			           ? $block['attrs']['blockPermissionsMessage']
				   : '';

			// Allow devs to overwrite the message.
	   		$message = apply_filters(
	   			'members/block/permissions/error/message',
				$message,
				$block
			);

			// Check if there's an error message and use it if so.
			if ( $message ) {

				$class = apply_filters(
					'members/block/permissions/error/class',
					[ 'block-permissions-error' ],
					$block
				);

				$pre_render = sprintf(
					'<div class="%s">%s</div>',
					esc_attr( join( ' ', $class ) ),
					wpautop( $message )
				);

				$pre_render = apply_filters(
					'members/block/permissions/error',
					$pre_render,
					$block
				);
			}
		}

		return $pre_render;
	}

	/**
	 * Determines whether to render the block based on user status.
	 *
	 * @since  1.0.0
	 * @access protected
	 * @param  array   $block  The block data.
	 * @return bool
	 */
	protected function checkUserStatus( $block ) {

		$maybe_render = true;
		$user_status  = false;

		if ( isset( $block['attrs']['blockPermissionsUserStatus'] ) && $block['attrs']['blockPermissionsUserStatus'] ) {
			$user_status = $block['attrs']['blockPermissionsUserStatus'];
		}

		// Bail if we don't have a user status.
		if ( ! $user_status ) {
			return $maybe_render;
		}

		$condition = $block['attrs']['blockPermissionsCondition'];

		if ( '=' === $condition ) {

			$maybe_render = false;

			if ( 'logged-in' === $user_status && is_user_logged_in() ) {
				$maybe_render = true;
			} elseif ( 'logged-out' === $user_status && ! is_user_logged_in() ) {
				$maybe_render = true;
			}

		} elseif ( '!=' === $condition ) {

			$maybe_render = true;

			if ( 'logged-in' === $user_status && is_user_logged_in() ) {
				$maybe_render = false;
			} elseif ( 'logged-out' === $user_status && ! is_user_logged_in() ) {
				$maybe_render = false;
			}
		}

		return $maybe_render;
	}

	/**
	 * Determines whether to render the block based on user role.
	 *
	 * @since  1.0.0
	 * @access protected
	 * @param  array   $block  The block data.
	 * @return bool
	 */
	protected function checkRole( $block ) {

		$maybe_render = true;
		$roles        = false;

		if ( isset( $block['attrs']['blockPermissionsRoles'] ) && is_array( $block['attrs']['blockPermissionsRoles'] ) && $block['attrs']['blockPermissionsRoles'] ) {
			$roles = $block['attrs']['blockPermissionsRoles'];
		}

		// Bail if we don't have any roles.
		if ( ! $roles ) {
			return $maybe_render;
		}

		$condition = $block['attrs']['blockPermissionsCondition'];

		if ( '=' === $condition ) {

			$maybe_render = false;

			if ( is_user_logged_in() ) {
				$user = new WP_User( get_current_user_id() );

				foreach ( (array) $user->roles as $role ) {

					if ( in_array( $role, $roles ) ) {
						$maybe_render = true;
						break;
					}
				}
			}

		} elseif ( '!=' === $condition ) {

			$maybe_render = true;

			if ( is_user_logged_in() ) {
				$user = new WP_User( get_current_user_id() );

				foreach ( (array) $user->roles as $role ) {

					if ( in_array( $role, $roles ) ) {
						$maybe_render = false;
						break;
					}
				}
			}
		}

		return $maybe_render;
	}

	/**
	 * Determines whether to render the block based on capability.
	 *
	 * @since  1.0.0
	 * @access protected
	 * @param  array   $block  The block data.
	 * @return bool
	 */
	protected function checkCap( $block ) {

		$maybe_render = true;
		$cap          = false;

		if ( isset( $block['attrs']['blockPermissionsCap'] ) && $block['attrs']['blockPermissionsCap'] ) {
			$cap = $block['attrs']['blockPermissionsCap'];
		}

		// Bail if we have no capability.
		if ( ! $cap ) {
			return $maybe_render;
		}

		$condition = $block['attrs']['blockPermissionsCondition'];

		if ( '=' === $condition ) {
			$maybe_render = current_user_can( $cap );
		} elseif ( '!=' === $condition ) {
			$maybe_render = ! current_user_can( $cap );
		}

		return $maybe_render;
	}
}
