<?php
/**
 * The first five minutes after somebody installs the plugin.
 *
 * @package TextTango
 */

declare( strict_types=1 );

namespace TextTango\Admin;

use TextTango\Install\Activator;

defined( 'ABSPATH' ) || exit;

/**
 * Getting a new install to the one screen that helps (spec §20).
 *
 * ### What used to happen
 *
 * Nothing. Activating the plugin produced no notice anywhere, the menu landed on
 * Settings — an empty token field, three em-dashes and a badge reading "Not
 * tested yet" — and the Setup screen, built for exactly that moment, sat third
 * in the menu with nothing pointing at it.
 *
 * ### Why the menu moves rather than a redirect
 *
 * This plugin had a takeover on the Setup screen once, and it was removed for
 * reasons that still hold. A redirect has to answer when it stops applying, how
 * somebody gets past it, and why a URL they typed took them somewhere else.
 * Changing where a menu link points asks none of those questions, so it has to
 * answer none of them — and somebody who wants Settings on day one can still
 * click Settings.
 */
final class FirstRun {

	/**
	 * Option recording that the setup notice was dismissed.
	 *
	 * Already listed in `uninstall.php`, left over from the takeover that was
	 * removed — nothing read or wrote it until now.
	 */
	public const OPTION_DISMISSED = 'texttango_setup_dismissed';

	/**
	 * Query argument that dismisses the notice.
	 */
	public const DISMISS_ARG = 'texttango_dismiss_setup';

	/**
	 * Register hooks.
	 */
	public function register(): void {
		add_action( 'admin_notices', array( $this, 'render_notice' ) );
		add_action( 'admin_init', array( $this, 'maybe_dismiss' ) );
	}

	/**
	 * Which screen the top-level menu item opens.
	 *
	 * Pure, because "where does the plugin's own menu link go" is a rule worth
	 * being able to read in one place rather than inferring from a branch inside
	 * the menu builder.
	 *
	 * @param bool $setup_complete Whether every applicable setup stage is done.
	 * @return string The menu slug to open.
	 */
	public static function landing_slug( bool $setup_complete ): string {
		return $setup_complete ? Menu::SETTINGS_SLUG : Wizard::SLUG;
	}

	/**
	 * Whether the setup notice should be shown.
	 *
	 * Pure, and all three conditions matter separately. Without the capability
	 * check every editor on the site is nagged about a plugin they cannot
	 * configure; without the dismissal check the notice is unclosable; without
	 * the completeness check it keeps appearing after there is nothing left to
	 * do, which is how people learn to ignore notices.
	 *
	 * @param bool $setup_complete Whether setup is finished.
	 * @param bool $dismissed      Whether somebody closed the notice.
	 * @param bool $may_configure  Whether this user can configure the plugin.
	 */
	public static function should_notice( bool $setup_complete, bool $dismissed, bool $may_configure ): bool {
		return $may_configure && ! $dismissed && ! $setup_complete;
	}

	/**
	 * Whether the notice has been dismissed.
	 */
	public static function is_dismissed(): bool {
		return (bool) get_option( self::OPTION_DISMISSED, false );
	}

	/**
	 * Show the setup notice, if it is wanted.
	 */
	public function render_notice(): void {
		$show = self::should_notice(
			Wizard::is_complete(),
			self::is_dismissed(),
			current_user_can( Activator::CAPABILITY )
		);

		if ( ! $show ) {
			return;
		}

		load_template(
			TEXTTANGO_PATH . 'templates/admin/partials/setup-notice.php',
			false,
			array(
				'setup_url'   => admin_url( 'admin.php?page=' . Wizard::SLUG ),
				'dismiss_url' => self::dismiss_url(),
			)
		);
	}

	/**
	 * Close the notice.
	 *
	 * A nonced link rather than an AJAX endpoint: it is one option write, it
	 * works with JavaScript switched off, and it needs nothing on the other end.
	 */
	public function maybe_dismiss(): void {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The nonce is checked on the next statement; this only decides whether to look.
		if ( ! isset( $_GET[ self::DISMISS_ARG ] ) ) {
			return;
		}

		if ( ! current_user_can( Activator::CAPABILITY ) || ! check_admin_referer( self::DISMISS_ARG ) ) {
			return;
		}

		update_option( self::OPTION_DISMISSED, true, false );

		wp_safe_redirect( remove_query_arg( array( self::DISMISS_ARG, '_wpnonce' ) ) );
		exit;
	}

	/**
	 * The link that closes the notice, back to wherever it was shown.
	 */
	public static function dismiss_url(): string {
		return wp_nonce_url( add_query_arg( self::DISMISS_ARG, '1' ), self::DISMISS_ARG );
	}
}
