<?php
/**
 * Shared first-run wp-admin getting-started notices for RSM plugins.
 *
 * @package Rsmspark_Suite
 */

defined( 'ABSPATH' ) || exit;

/**
 * Class Rsmspark_Getting_Started_Notice
 */
class Rsmspark_Getting_Started_Notice {

	const DEFAULT_PRO_URL = 'https://rsmspark.com';

	const REMIND_SNOOZE_SECONDS = 604800;

	const DISMISSED_PREFIX = 'dismissed:';

	/**
	 * Registered notices keyed by slug.
	 *
	 * @var array<string, array<string, mixed>>
	 */
	private static $notices = array();

	/**
	 * Hook admin handlers.
	 *
	 * @return void
	 */
	public static function init() {
		if ( defined( 'RSMSPARK_GETTING_STARTED_NOTICE_INIT_DONE' ) ) {
			return;
		}

		define( 'RSMSPARK_GETTING_STARTED_NOTICE_INIT_DONE', true );

		add_action( 'admin_notices', array( __CLASS__, 'render_all' ), 5 );
		add_action( 'admin_init', array( __CLASS__, 'handle_notice_action' ) );
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_styles' ) );
	}

	/**
	 * Register a plugin-specific getting-started notice.
	 *
	 * @param array<string, mixed> $config {
	 *     @type string          $slug              Unique notice slug (e.g. rsm-directory).
	 *     @type string          $message           Plugin-specific welcome copy.
	 *     @type string          $plugin_version    Active plugin version (for version-based dismiss).
	 *     @type string          $text_domain       Text domain for shared strings.
	 *     @type string          $capability        Required capability. Default manage_options.
	 *     @type bool            $show_on_dashboard Show on wp-admin dashboard. Default true.
	 *                                           When RSM Spark is active, only the rsm-spark notice may use the dashboard (Phase A policy).
	 *     @type string[]        $screen_slugs      Screen ID substrings / exact IDs.
	 *     @type string[]        $post_types        Additional CPT screen matches.
	 *     @type string          $pro_url           Go Pro link URL.
	 *     @type callable|null   $show_if           Optional callback returning bool.
	 * }
	 * @return void
	 */
	public static function register( array $config ) {
		$slug = sanitize_key( (string) ( $config['slug'] ?? '' ) );
		if ( '' === $slug || empty( $config['message'] ) ) {
			return;
		}

		self::$notices[ $slug ] = wp_parse_args(
			$config,
			array(
				'slug'              => $slug,
				'plugin_version'    => '0.0.0',
				'plugin_path'       => '',
				'plugin_url'        => '',
				'capability'        => 'manage_options',
				'show_on_dashboard' => true,
				'screen_slugs'      => array( $slug ),
				'post_types'        => array(),
				'pro_url'           => self::DEFAULT_PRO_URL,
				'show_if'           => null,
				'text_domain' => 'rsm-suite',
			)
		);
	}

	/**
	 * Flag a notice to show after plugin activation.
	 *
	 * @param string $slug Notice slug.
	 * @return void
	 */
	public static function flag_on_activation( $slug ) {
		$slug = sanitize_key( (string) $slug );
		if ( '' === $slug ) {
			return;
		}

		update_option( self::option_key( $slug ), 'active', false );
	}

	/**
	 * Flag notice once when a plugin version includes the getting-started feature.
	 *
	 * WordPress zip updates often skip register_activation_hook, so this runs on admin_init.
	 *
	 * @param string $slug                    Notice slug (e.g. rsm-directory).
	 * @param string $minimum_plugin_version  Version that introduced the notice.
	 * @param string $current_plugin_version  Active plugin version.
	 * @return void
	 */
	public static function flag_if_pending_introduction( $slug, $minimum_plugin_version, $current_plugin_version ) {
		$slug = sanitize_key( (string) $slug );
		if ( '' === $slug ) {
			return;
		}

		$intro_key = 'rsmspark_getting_started_intro_' . $slug;
		$state     = get_option( self::option_key( $slug ) );

		if ( self::is_version_dismissed_state( $state ) ) {
			$dismissed_version = self::parse_dismissed_version( $state );
			if ( '' !== $dismissed_version && version_compare( (string) $current_plugin_version, $dismissed_version, '>' ) ) {
				update_option( self::option_key( $slug ), 'active', false );
				update_option( $intro_key, 1, false );
				return;
			}
		}

		if ( get_option( $intro_key ) ) {
			if ( self::should_reactivate_intro( $state, $minimum_plugin_version, $current_plugin_version ) ) {
				update_option( self::option_key( $slug ), 'active', false );
			}
			return;
		}

		if ( 'dismissed' === $state ) {
			update_option( $intro_key, 1, false );
			return;
		}

		if ( version_compare( (string) $current_plugin_version, (string) $minimum_plugin_version, '<' ) ) {
			return;
		}

		update_option( self::option_key( $slug ), 'active', false );
		update_option( $intro_key, 1, false );
	}

	/**
	 * Render all pending notices for the current screen.
	 *
	 * @return void
	 */
	public static function render_all() {
		if ( ! is_admin() || empty( self::$notices ) ) {
			return;
		}

		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		if ( ! $screen ) {
			return;
		}

		$will_render = false;
		foreach ( self::$notices as $slug => $config ) {
			if ( ! current_user_can( (string) $config['capability'] ) ) {
				continue;
			}
			if ( ! self::should_show_notice( $slug, $config ) ) {
				continue;
			}
			if ( ! self::matches_screen( $config, $screen ) ) {
				continue;
			}
			$will_render = true;
			break;
		}

		if ( $will_render ) {
			self::enqueue_notice_styles();
		}

		foreach ( self::$notices as $slug => $config ) {
			self::maybe_render_notice( $slug, $config, $screen );
		}
	}

	/**
	 * Print getting-started notice CSS (deprecated — styles enqueue via wp_enqueue_style).
	 *
	 * @return void
	 */
	public static function print_notice_styles() {
		// Retained as no-op for older call sites; CSS is enqueued only.
	}

	/**
	 * Handle Remind me Later and Don't Show Again links.
	 *
	 * @return void
	 */
	public static function handle_notice_action() {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( empty( $_GET['rsmspark_notice'] ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$action = ! empty( $_GET['rsmspark_getting_started_action'] )
			? sanitize_key( wp_unslash( (string) $_GET['rsmspark_getting_started_action'] ) )
			: '';

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( '' === $action && ! empty( $_GET['rsmspark_dismiss_getting_started'] ) ) {
			$action = 'hide';
		}

		if ( '' === $action ) {
			return;
		}
		$slug   = sanitize_key( wp_unslash( (string) $_GET['rsmspark_notice'] ) );

		if ( '' === $slug || empty( self::$notices[ $slug ] ) ) {
			return;
		}

		if ( ! in_array( $action, array( 'remind', 'hide' ), true ) ) {
			return;
		}

		$config = self::$notices[ $slug ];
		if ( ! current_user_can( (string) $config['capability'] ) ) {
			return;
		}

		$nonce_action = 'rsmspark_getting_started_action_' . $slug;
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! empty( $_GET['rsmspark_dismiss_getting_started'] ) ) {
			$nonce_action = 'rsmspark_dismiss_getting_started_' . $slug;
		}

		check_admin_referer( $nonce_action );

		if ( 'remind' === $action ) {
			self::remind_later( $slug );
		} else {
			self::hide_until_version_change( $slug, $config );
		}

		wp_safe_redirect(
			remove_query_arg(
				array( 'rsmspark_getting_started_action', 'rsmspark_dismiss_getting_started', 'rsmspark_notice', '_wpnonce' )
			)
		);
		exit;
	}

	/**
	 * Enqueue shared admin styles when a notice may appear.
	 *
	 * @param string $hook_suffix Current admin hook.
	 * @return void
	 */
	public static function enqueue_styles( $hook_suffix ) {
		unset( $hook_suffix );

		if ( ! self::has_pending_notices() ) {
			return;
		}

		self::enqueue_notice_styles();
	}

	/**
	 * Enqueue getting-started notice styles.
	 *
	 * @return void
	 */
	private static function enqueue_notice_styles() {
		$inline = self::notice_css_rules();
		$handle = 'rsmspark-getting-started-notice';

		if ( ! wp_style_is( $handle, 'registered' ) ) {
			wp_register_style(
				$handle,
				false,
				array( 'wp-admin', 'common' ),
				defined( 'RSMSPARK_SUITE_VERSION' ) ? RSMSPARK_SUITE_VERSION : '1.0.0'
			);
		}

		wp_enqueue_style( $handle );
		wp_add_inline_style( $handle, $inline );

		$css_url = function_exists( 'rsmspark_suite_shared_asset_url' )
			? rsmspark_suite_shared_asset_url( 'assets/css/admin-suite.css' )
			: ( defined( 'RSMSPARK_SUITE_URL' ) && '' !== RSMSPARK_SUITE_URL ? RSMSPARK_SUITE_URL . 'assets/css/admin-suite.css' : '' );

		if ( '' !== $css_url && ! wp_style_is( 'rsmspark-suite-admin', 'enqueued' ) && ! wp_style_is( 'rsmspark-suite-admin', 'done' ) ) {
			wp_enqueue_style(
				'rsmspark-suite-admin',
				$css_url,
				array( 'wp-admin', 'common' ),
				defined( 'RSMSPARK_SUITE_VERSION' ) ? RSMSPARK_SUITE_VERSION : '1.0.0'
			);
		}
	}

	/**
	 * Inline CSS rules for getting-started notices.
	 *
	 * @return string
	 */
	private static function notice_css_rules() {
		return '#wpbody-content .notice.rsm-getting-started-notice,.wrap .notice.rsm-getting-started-notice{margin:12px 0 16px;padding:0;background:#fff6f1!important;border:1px solid #f0d4c4;border-left:4px solid #ff6b35!important;box-shadow:0 1px 2px rgba(0,0,0,.04)}.rsm-getting-started-notice__inner{display:flex;align-items:stretch;gap:20px;padding:14px 18px}.rsm-getting-started-notice__content{flex:1 1 auto;min-width:0}.rsm-getting-started-notice__brand{flex:0 0 auto;align-self:stretch;display:flex;align-items:center;justify-content:flex-end;margin-left:auto;max-width:40%;min-width:80px;padding-left:12px}.rsm-getting-started-notice__logo{display:block;height:100%;max-height:100%;width:auto;max-width:160px;object-fit:contain;object-position:center right}.rsm-getting-started-notice__message{margin:0 0 12px;font-size:14px;line-height:1.5}.rsm-getting-started-notice__actions{margin:0;display:flex;flex-wrap:wrap;gap:8px 18px}.rsm-getting-started-notice__action{font-weight:600;text-decoration:none}.rsm-getting-started-notice__action--pro{color:#c44f00}.rsm-getting-started-notice__action--pro:hover,.rsm-getting-started-notice__action--pro:focus{color:#ff6b35;text-decoration:underline}.rsm-getting-started-notice__action--remind,.rsm-getting-started-notice__action--hide{color:#1d2327}.rsm-getting-started-notice__action--remind:hover,.rsm-getting-started-notice__action--remind:focus,.rsm-getting-started-notice__action--hide:hover,.rsm-getting-started-notice__action--hide:focus{color:#ff6b35;text-decoration:underline}';
	}

	/**
	 * Inline style attribute for a single notice element.
	 *
	 * @return string
	 */
	private static function notice_inline_style_attr() {
		return 'margin:12px 0 16px;padding:0;background:#fff6f1;border:1px solid #f0d4c4;border-left:4px solid #ff6b35;box-shadow:0 1px 2px rgba(0,0,0,.04)';
	}

	/**
	 * @param string    $slug   Notice slug.
	 * @param array     $config Notice config.
	 * @param WP_Screen $screen Current screen.
	 * @return void
	 */
	private static function maybe_render_notice( $slug, array $config, $screen ) {
		if ( ! current_user_can( (string) $config['capability'] ) ) {
			return;
		}

		if ( ! self::should_show_notice( $slug, $config ) ) {
			return;
		}

		if ( ! self::matches_screen( $config, $screen ) ) {
			return;
		}

		$text_domain = (string) $config['text_domain'];
		$message     = (string) $config['message'];
		$pro_url     = esc_url( (string) $config['pro_url'] );
		$logo_url    = self::resolve_logo_url( $config );

		$remind_url = wp_nonce_url(
			add_query_arg(
				array(
					'rsmspark_getting_started_action' => 'remind',
					'rsmspark_notice'                 => $slug,
				)
			),
			'rsmspark_getting_started_action_' . $slug
		);

		$hide_url = wp_nonce_url(
			add_query_arg(
				array(
					'rsmspark_getting_started_action' => 'hide',
					'rsmspark_notice'                 => $slug,
				)
			),
			'rsmspark_getting_started_action_' . $slug
		);

		echo '<div class="notice rsm-getting-started-notice" style="' . esc_attr( self::notice_inline_style_attr() ) . '">';
		echo '<div class="rsm-getting-started-notice__inner">';

		echo '<div class="rsm-getting-started-notice__content">';
		echo '<p class="rsm-getting-started-notice__message">' . esc_html( $message ) . '</p>';
		echo '<p class="rsm-getting-started-notice__actions">';
		echo '<a class="rsm-getting-started-notice__action rsm-getting-started-notice__action--pro" href="' . esc_url( $pro_url ) . '" target="_blank" rel="noopener noreferrer">';
		echo esc_html__( 'Go Pro', 'rsm-content-widgets' );
		echo '</a>';
		echo '<a class="rsm-getting-started-notice__action rsm-getting-started-notice__action--remind" href="' . esc_url( $remind_url ) . '">';
		echo esc_html__( 'Remind me Later', 'rsm-content-widgets' );
		echo '</a>';
		echo '<a class="rsm-getting-started-notice__action rsm-getting-started-notice__action--hide" href="' . esc_url( $hide_url ) . '">';
		echo esc_html__( "Don't Show Again", 'rsm-content-widgets' );
		echo '</a>';
		echo '</p>';
		echo '</div>';

		if ( '' !== $logo_url ) {
			echo '<div class="rsm-getting-started-notice__brand">';
			echo '<img class="rsm-getting-started-notice__logo" src="' . esc_url( $logo_url ) . '" alt="' . esc_attr__( 'RSM Spark', 'rsm-content-widgets' ) . '" loading="lazy" decoding="async" />';
			echo '</div>';
		}

		echo '</div>';
		echo '</div>';
	}

	/**
	 * Resolve logo URL for a notice (prefer the registering plugin's assets).
	 *
	 * @param array<string, mixed> $config Notice config.
	 * @return string
	 */
	private static function resolve_logo_url( array $config ) {
		$plugin_path = trailingslashit( (string) ( $config['plugin_path'] ?? '' ) );
		$plugin_url  = trailingslashit( (string) ( $config['plugin_url'] ?? '' ) );

		if ( '' !== $plugin_path && '' !== $plugin_url ) {
			$url = self::first_readable_asset_url(
				$plugin_path,
				$plugin_url,
				array(
					'assets/images/rsm-spark-news-logo.webp',
					'assets/images/rsm-spark-news-logo.png',
					'assets/images/rsm-spark-news-logo.jpg',
					'assets/images/rsm-spark-news-logo.svg',
				)
			);
			if ( '' !== $url ) {
				return $url;
			}
		}

		if ( defined( 'RSMSPARK_SUITE_DIR' ) && defined( 'RSMSPARK_SUITE_URL' ) ) {
			$url = self::first_readable_asset_url(
				trailingslashit( RSMSPARK_SUITE_DIR ),
				trailingslashit( RSMSPARK_SUITE_URL ),
				array(
					'assets/images/rsm-spark-news-logo.webp',
					'assets/images/rsm-spark-news-logo.png',
					'assets/images/rsm-spark-news-logo.jpg',
					'assets/images/rsm-spark-news-logo.svg',
				)
			);
			if ( '' !== $url ) {
				return $url;
			}
		}

		return self::resolve_logo_url_from_active_plugins();
	}

	/**
	 * @param string        $base_path Filesystem base.
	 * @param string        $base_url  URL base.
	 * @param array<string> $relative  Candidate relative paths.
	 * @return string
	 */
	private static function first_readable_asset_url( $base_path, $base_url, array $relative ) {
		foreach ( $relative as $file ) {
			if ( is_readable( $base_path . $file ) ) {
				return $base_url . $file;
			}
		}

		return '';
	}

	/**
	 * Last-resort logo lookup across active RSM plugins.
	 *
	 * @return string
	 */
	private static function resolve_logo_url_from_active_plugins() {
		if ( ! function_exists( 'get_plugins' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		$plugins = get_plugins();
		foreach ( $plugins as $plugin_file => $data ) {
			if ( ! is_plugin_active( $plugin_file ) ) {
				continue;
			}

			$slug = dirname( $plugin_file );
			if ( 0 !== strpos( $slug, 'rsm-' ) && 'openrsm-start' !== $slug ) {
				continue;
			}

			$main_file   = WP_PLUGIN_DIR . '/' . $plugin_file;
			$plugin_path = plugin_dir_path( $main_file );
			$plugin_url  = plugin_dir_url( $main_file );

			$url = self::first_readable_asset_url(
				$plugin_path,
				$plugin_url,
				array(
					'assets/images/rsm-spark-news-logo.webp',
					'assets/images/rsm-spark-news-logo.png',
					'assets/images/rsm-spark-news-logo.jpg',
					'assets/images/rsm-spark-news-logo.svg',
				)
			);
			if ( '' !== $url ) {
				return $url;
			}
		}

		return '';
	}

	/**
	 * Phase A: when Spark is active, only Spark may show getting-started notices on the Dashboard.
	 *
	 * @return bool
	 */
	private static function spark_owns_dashboard() {
		return class_exists( 'RSM_Spark_Admin', false );
	}

	/**
	 * @param array     $config Notice config.
	 * @param WP_Screen $screen Current screen.
	 * @return bool
	 */
	private static function matches_screen( array $config, $screen ) {
		if ( ! empty( $config['show_on_dashboard'] ) && 'dashboard' === $screen->id ) {
			if ( self::spark_owns_dashboard() && 'rsm-spark' !== (string) ( $config['slug'] ?? '' ) ) {
				return false;
			}

			return true;
		}

		foreach ( (array) $config['screen_slugs'] as $slug ) {
			$slug = (string) $slug;
			if ( '' === $slug ) {
				continue;
			}
			if ( $slug === $screen->id || false !== strpos( (string) $screen->id, $slug ) ) {
				return true;
			}
		}

		if ( ! empty( $config['post_types'] ) && ! empty( $screen->post_type ) ) {
			if ( in_array( (string) $screen->post_type, (array) $config['post_types'], true ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * @param string $slug   Notice slug.
	 * @param array  $config Notice config.
	 * @return bool
	 */
	private static function should_show_notice( $slug, array $config ) {
		if ( ! self::is_pending( $slug, $config ) ) {
			return false;
		}

		$show_if = $config['show_if'] ?? null;
		if ( is_callable( $show_if ) && ! (bool) call_user_func( $show_if ) ) {
			return false;
		}

		return true;
	}

	/**
	 * @param string               $slug   Notice slug.
	 * @param array<string, mixed> $config Notice config.
	 * @return bool
	 */
	private static function is_pending( $slug, array $config = array() ) {
		if (
			'rsm-directory' === $slug
			&& (
				'1' === get_option( 'rsmspark_directory_show_getting_started' )
				|| '1' === get_option( 'rsm_directory_show_getting_started' )
			)
		) {
			return true;
		}

		$value = get_option( self::option_key( $slug ) );

		if ( 'active' === $value || '1' === $value ) {
			return true;
		}

		if ( 'dismissed' === $value ) {
			return false;
		}

		if ( self::is_version_dismissed_state( $value ) ) {
			$dismissed_version = self::parse_dismissed_version( $value );
			$current_version   = (string) ( $config['plugin_version'] ?? '0.0.0' );

			if ( '' === $dismissed_version ) {
				return false;
			}

			return version_compare( $current_version, $dismissed_version, '>' );
		}

		if ( is_numeric( $value ) ) {
			return (int) $value <= time();
		}

		return false;
	}

	/**
	 * @return bool
	 */
	private static function has_pending_notices() {
		foreach ( self::$notices as $slug => $config ) {
			if ( self::should_show_notice( $slug, $config ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Whether to re-flag a notice after intro ran but no dismiss/snooze was stored.
	 *
	 * @param mixed  $state           Stored notice state.
	 * @param string $minimum_version Version that introduced the notice.
	 * @param string $current_version Active plugin version.
	 * @return bool
	 */
	private static function should_reactivate_intro( $state, $minimum_version, $current_version ) {
		if ( 'active' === $state || '1' === $state ) {
			return false;
		}

		if ( self::is_version_dismissed_state( $state ) || 'dismissed' === $state || is_numeric( $state ) ) {
			return false;
		}

		return version_compare( (string) $current_version, (string) $minimum_version, '>=' );
	}

	/**
	 * @param string $slug Notice slug.
	 * @return void
	 */
	private static function remind_later( $slug ) {
		update_option( self::option_key( $slug ), time() + self::REMIND_SNOOZE_SECONDS, false );

		if ( 'rsm-directory' === $slug ) {
			delete_option( 'rsmspark_directory_show_getting_started' );
			delete_option( 'rsm_directory_show_getting_started' );
		}
	}

	/**
	 * @param string               $slug   Notice slug.
	 * @param array<string, mixed> $config Notice config.
	 * @return void
	 */
	private static function hide_until_version_change( $slug, array $config ) {
		$version = (string) ( $config['plugin_version'] ?? '0.0.0' );
		update_option( self::option_key( $slug ), self::DISMISSED_PREFIX . $version, false );

		if ( 'rsm-directory' === $slug ) {
			delete_option( 'rsmspark_directory_show_getting_started' );
			delete_option( 'rsm_directory_show_getting_started' );
		}
	}

	/**
	 * @param string $value Stored notice state.
	 * @return bool
	 */
	private static function is_version_dismissed_state( $value ) {
		return 0 === strpos( (string) $value, self::DISMISSED_PREFIX );
	}

	/**
	 * @param string $value Stored notice state.
	 * @return string
	 */
	private static function parse_dismissed_version( $value ) {
		if ( ! self::is_version_dismissed_state( $value ) ) {
			return '';
		}

		return substr( (string) $value, strlen( self::DISMISSED_PREFIX ) );
	}

	/**
	 * @param string $slug Notice slug.
	 * @return string
	 */
	private static function option_key( $slug ) {
		return 'rsmspark_getting_started_' . sanitize_key( $slug );
	}
}
