<?php
/**
 * Show the dismissible admin notice
 *
 * @package Teydea_Studio\Careers\Dependencies\Universal_Modules
 */

namespace Teydea_Studio\Careers\Dependencies\Universal_Modules;

use Teydea_Studio\Careers\Dependencies\Utils;
use WP_Error;
use WP_REST_Response;
use WP_REST_Server;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // @codeCoverageIgnore
}

/**
 * The "Module_Admin_Notice" class
 */
class Module_Admin_Notice extends Utils\Module {
	/**
	 * Nonce action
	 *
	 * @var string
	 */
	const NONCE_ACTION = 'careers__dismiss_notice';

	/**
	 * User meta key
	 *
	 * @var string
	 */
	const USER_META_KEY = 'careers__admin_notice_dismissed';

	/**
	 * Register hooks
	 *
	 * @return void
	 */
	public function register(): void {
		// Maybe register the admin notice.
		add_action( 'admin_notices', [ $this, 'maybe_register_admin_notice' ] );

		// Maybe enqueue admin script used to dismiss the admin notice.
		add_action( 'admin_enqueue_scripts', [ $this, 'maybe_enqueue_script' ] );

		// Register endpoint.
		add_action( 'rest_api_init', [ $this, 'register_endpoint' ] );
	}

	/**
	 * Get the message to show
	 *
	 * @return string Message to show.
	 */
	protected function get_message(): string {
		return '';
	}

	/**
	 * Recognize whether the admin notice should be shown
	 *
	 * @return bool Whether the admin notice should be shown.
	 */
	protected function should_show_admin_notice(): bool {
		$user = new Utils\User( $this->container );

		// Check if user has managing permissions.
		if ( ! $user->has_managing_permissions() ) {
			return false;
		}

		/**
		 * Check if user has not dismissed the admin notice yet.
		 *
		 * If not, notice should show.
		 */
		return ! $user->get_meta_as_boolean( self::USER_META_KEY, false );
	}

	/**
	 * Maybe register the admin notice
	 *
	 * @return void
	 */
	public function maybe_register_admin_notice(): void {
		// Check if notice should be shown.
		if ( ! $this->should_show_admin_notice() ) {
			return;
		}

		$nonce = new Utils\Nonce( $this->container, self::NONCE_ACTION );

		// Register the admin notice.
		wp_admin_notice(
			wp_kses(
				$this->get_message(),
				[
					'a'      => [ 'href' => true ],
					'br'     => [],
					'strong' => [],
				],
			),
			[
				'id'          => 'careers__admin_notice',
				'attributes'  => [
					'data-nonce' => $nonce->create(),
				],
				'type'        => 'warning',
				'dismissible' => true,
			],
		);
	}

	/**
	 * Enqueue admin script used to dismiss the admin notice
	 *
	 * @return void
	 */
	public function maybe_enqueue_script(): void {
		// Check if notice should be shown.
		if ( ! $this->should_show_admin_notice() ) {
			return;
		}

		$asset = new Utils\Asset( $this->container, 'admin-notice' );
		$asset->enqueue_script( false );
	}

	/**
	 * Register endpoint
	 *
	 * @return void
	 */
	public function register_endpoint(): void {
		register_rest_route(
			sprintf( '%s/v1', $this->container->get_slug() ),
			'/admin-notice',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'dismiss' ],
				'args'                => [
					'nonce' => [
						'required'          => true,
						'type'              => 'string',
						'sanitize_callback' => 'sanitize_text_field',

						/**
						 * Nonce value validation
						 *
						 * @param string $value Nonce value.
						 *
						 * @return bool Whether the nonce value is valid or not.
						 */
						'validate_callback' => function ( string $value ): bool {
							$nonce = new Utils\Nonce( $this->container, self::NONCE_ACTION );
							return false !== wp_verify_nonce( $value, $nonce->get_action() );
						},
					],
				],

				/**
				 * Ensure that user is logged in and has the required
				 * capability
				 *
				 * @return bool|WP_Error Boolean "true" if user has the permission to process this request, WP_Error otherwise.
				 */
				'permission_callback' => function () {
					$user = new Utils\User( $this->container );

					if ( ! $user->has_managing_permissions() ) {
						return new WP_Error(
							'rest_forbidden',
							__( 'You do not have permission to dismiss this notice.', 'careers' ),
							[ 'status' => rest_authorization_required_code() ],
						);
					}

					return true;
				},
			],
		);
	}

	/**
	 * Dismiss the admin notice
	 *
	 * @return WP_Error|WP_REST_Response Instance of WP_REST_Response on success, instance of WP_Error on failure.
	 */
	public function dismiss() {
		$user  = new Utils\User( $this->container );
		$added = $user->add_meta( self::USER_META_KEY, true, false );

		if ( false === $added ) {
			return new WP_Error(
				'unable_to_add_user_meta',
				__( 'Unable to add user meta.', 'careers' ),
			);
		}

		return new WP_REST_Response( [], 200 );
	}
}
