<?php
/**
 * Please see weepie-framework.php for more details.
 */

namespace WpieFw\Notices;

use WpieFw\Helpers\WpieMiscHelper;

if( ! defined( 'ABSPATH' ) ) exit;

/**
 * WpieNotices Class
 *
 * Class for handling Plugin Settings page
 *
 * @author $Author: Vincent Weber <weepie-plugins@outlook.com> $
 * @since 1.1.8
 */
final class WpieNotices
{
	/**
	 * The singleton instance
	 *
	 * @access private
	 *
	 * @since 1.1.8
	 *
	 * @var WpieNotices
	 */
	private static $instance = null;

	/**
	 * Notices array
	 *
	 * @access private
	 *
	 * @since 1.1.8
	 *
	 * @var array
	 */
	private static $notices = [];

	/**
	 * HTML class attribute format for notices of type notice
	 *
	 * @since 1.4.10
	 *
	 * @var string
	 */
	const SUCCESS_CSS_CLASS = 'notice notice-success is-dismissible';

	/**
	 * HTML class attribute format for notices of type error
	 *
	 * @since 1.4.10
	 *
	 * @var string
	 */
	const ERROR_CSS_CLASS = 'notice notice-error error';

	/**
	 * HTML class attribute format for notices of type info
	 *
	 * @since 2.0.3
	 *
	 * @var string
	 */
	const INFO_CSS_CLASS = 'notice notice-info info is-dismissible';

	/**
	 * HTML class attribute format for notices of type info
	 *
	 * @since 2.0.3
	 *
	 * @var string
	 */
	const WARNING_CSS_CLASS = 'notice notice-warning warning is-dismissible';

	/**
	 * Namespace when an empty namespace is passed
	 *
	 * @since 2.0
	 *
	 * @var string
	 */
	const NAMESPACE_NONE = 'none';

	/**
	 * Constructor
	 *
	 * @access private
	 */
	private function __construct() {}

	/**
	 * Singleton implementation
	 *
	 * @acces public
	 *
	 * @since 1.1.8
	 *
	 * @return WpieNotices
	 */
	public static function instance()
	{
		if ( null === self::$instance ) {
			self::$instance = new self();
			self::$instance->init();
		}

		return self::$instance;
	}

	/**
	 * Add a notice
	 *
	 * With given namespace and type, the notice message is added to the static self::$notices array
	 * If $setting is not false, a WordPress settings error will be added
	 *
	 * @access public
	 *
	 * @param string $nameSpace
	 * @param string $message
	 * @param string $type
	 * @param string $setting
	 * @param bool $once
	 *
	 * @uses add_settings_error() if $setting is not false
	 *
	 * @since 1.1.8
	 */
	public static function add( string $nameSpace, string $message, string $type = 'error', string $setting = '', bool $once = false )
	{
		if( '' === $nameSpace ) {
			$nameSpace = self::NAMESPACE_NONE;
		}

		if( !is_admin() || '' === trim( $message ) ) {
			return;
		}

		if ( null === self::$instance ) {
			self::instance();
		}

		if( '' !== $setting && function_exists( 'add_settings_error' ) ) {
			add_settings_error( $setting, $nameSpace.'-'.$type, $message, $type );
		} else {
			$id = wp_unique_id_from_values( [ $message ] );
		    $data = array(
		        'msg' => $message,
		        'once' => $once,
		        'setting' => $setting,
				'id' => $id
		    );

		    self::$notices[$nameSpace][$type][] = $data;
		}
	}

	/**
	 * Callback for the admin_notices hook
	 *
	 * Add settings errors if any
	 *
	 * @access public
	 *
	 * @uses settings_errors()
	 *
	 * @since 1.1.8
	 */
	public function show()
	{
		if( !empty( self::$notices ) ) {
			foreach ( self::$notices as $nameSpace => $notices ) {
				if( empty( $notices ) ) {
					continue;
				}

				foreach ( $notices as $type => $messages ) {
				    switch ( $type ) {
				        case 'warning':
				            $class = self::WARNING_CSS_CLASS;
				            break;
				        case 'info':
				            $class = self::INFO_CSS_CLASS;
				            break;
				        case 'success':
				            $class = self::SUCCESS_CSS_CLASS;
				            break;
				        case 'error':
				        default:
				            $class = self::ERROR_CSS_CLASS;
				        break;
				    }

				    // print notices with at least CSS class "notice-error".
				    // WP then takes the notice element and moves it to the WP header
					foreach ( $messages as $message ) {						
					    $page = '';
					    $updated = false;
						$id = $message['id'];
					    $msg = $message['msg'];
					    $once = $message['once'];
					    $setting = $message['setting'];
					    $html = sprintf( "<div class='".$class."' data-ns='".$nameSpace."'><p><strong>%s</strong></p></div>\n", $msg );

						if( $once ) {
							if( '' !== $setting ) {
								// if notice should be printed once and a setting is passed
					    		// check if the settings have been updated and if the "page" query var is for the passed setting	
								$page = 'wpie-'.WpieMiscHelper::convertToHyphens( $setting );
								$updated = ( isset( $_GET['settings-updated'] ) && isset( $_GET['page'] ) && $page === $_GET['page'] );
							}

							$key = $id . 'shown';
							$userId = get_current_user_id();
							$shown = get_user_meta($userId, $key, true);

							if( $shown && !$updated ) {
								continue;
							}						
						}

					    if( !$once || $once && $updated ) {
							if( $once ) {
								// Store that message has been showed
								update_user_meta($userId, $key, true);								
							}							
							echo $html;						
						}
					}
				}
			}
		}

		// echo settings errors only if not on WP settings pages.
		// options-head.php does also call settings_errors()
		global $parent_file;
		if ( 'options-general.php' !== $parent_file ) {
			settings_errors();
		}
	}

	/**
	 * Init the notice instance
	 *
	 * Adding the WordPress admin_notices hook
	 *
	 * @acces private
	 *
	 * @since 1.1.8
	 */
	private function init()
	{
		if( !is_admin() ) {
			return;
		}

		self::$notices = [];

		add_action( 'all_admin_notices', [ $this, 'show' ], 1 );
	}
}