<?php
/**
 * Notice model.
 *
 * @package northmill-online-shared
 */

declare(strict_types=1);

namespace Northmill_Online_Shared\Modules\Admin\Notices\Models;

use Closure;
use Northmill_Online_Shared\Modules\Admin\Notices\Notice_Type_Enum;

defined( 'ABSPATH' ) || exit;

/**
 * Class Notice_Model
 */
class Notice_Model {

	/**
	 * Option key for storing if notice is closed.
	 *
	 * @var string
	 */
	private string $closed_option_key;

	/**
	 * Notice_Model constructor.
	 *
	 * @param string             $key           Key unique for each notice.
	 * @param Notice_Type_Enum   $type          Type of notice.
	 * @param array|Closure      $message       Message to show in the notice. Can be a callable to delay translation.
	 * @param bool               $dismissible   Whether the notice should be dismissible.
	 * @param array|Closure|bool $should_show   Whether the notice should be shown.
	 *                                          Can be an array (callable), a Closure or bool.
	 *                                          This will be checked if the notice hasn't already been closed.
	 * @param array              $args          Additional arguments to pass to wp_admin_notice().
	 */
	public function __construct(
		private readonly string $key,
		private readonly Notice_Type_Enum $type,
		private array|Closure $message,
		private readonly bool $dismissible = true,
		private readonly array|Closure|bool $should_show = true,
		private readonly array $args = array(),
	) {
		$this->closed_option_key = 'northmill_notice_closed_' . $key;

		add_action( 'admin_notices', array( $this, 'maybe_output' ) );
	}

	/**
	 * Maybe output the notice.
	 */
	public function maybe_output(): void {
		if ( ! $this->should_show() ) {
			return;
		}

		$notice_args = array(
			'type'               => $this->type->to_string(),
			'dismissible'        => $this->dismissible,
			'additional_classes' => array(
				'northmill-admin-notice',
			),
			'attributes'         => array(
				'data-notice-key' => $this->key,
			),
		);

		$args = wp_parse_args( $this->args, $notice_args );

		wp_admin_notice(
			$this->get_message(),
			$args
		);
	}

	/**
	 * Check if the notice should be shown.
	 *
	 * @return bool
	 */
	public function should_show(): bool {
		$is_closed = get_option( $this->closed_option_key, null );

		if ( ! is_null( $is_closed ) ) {
			return 'no' === $is_closed;
		}

		if ( is_callable( $this->should_show ) ) {
			return call_user_func( $this->should_show, $this );
		} elseif ( is_bool( $this->should_show ) ) {
			return $this->should_show;
		}

		return true;
	}

	/**
	 * Set whether the notice is closed.
	 *
	 * @param bool $closed Whether the notice is closed.
	 */
	public function set_closed( bool $closed = true ): void {
		update_option( $this->closed_option_key, $closed ? 'yes' : 'no' );
	}

	/**
	 * Get the message to show in the notice.
	 *
	 * @return array|string|Closure
	 */
	public function get_message(): string {
		if ( is_callable( $this->message ) ) {
			return call_user_func( $this->message );
		}

		return '';
	}

	/**
	 * Set the message to show in the notice.
	 *
	 * @param array|string|Closure $message Message to show in the notice.
	 *
	 * @used Northmill Sync
	 */
	public function set_message( array|string|Closure $message ): void {
		$this->message = $message;
	}
}
