<?php
/**
 * RSM Buttons module bootstrap.
 *
 * @package Rsmspark_Content_Widgets
 */

defined( 'ABSPATH' ) || exit;

/**
 * Rsmspark_Buttons
 */
class Rsmspark_Buttons {

	/**
	 * @var bool
	 */
	private static $content_filters_registered = false;

	/**
	 * @var bool
	 */
	private static $pre_filter_registered = false;

	/**
	 * @return void
	 */
	public static function init() {
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-config.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-icons.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-cpt.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-render.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-seed.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-ajax.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-list-table.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-settings.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-theme-defaults.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-presets.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-admin.php';

		add_action( 'init', array( 'Rsmspark_Buttons_CPT', 'register' ) );
		add_action( 'init', array( 'Rsmspark_Buttons_CPT', 'register_capabilities' ), 5 );
		add_action( 'init', array( __CLASS__, 'register_shortcode' ), 5 );
		add_action( 'init', array( __CLASS__, 'register_content_filters' ), 20 );
		if ( ! self::$pre_filter_registered ) {
			add_filter( 'pre_do_shortcode_tag', array( __CLASS__, 'pre_render_shortcode' ), 10, 4 );
			self::$pre_filter_registered = true;
		}
		add_filter( 'rsmspark_content_widgets_should_render', array( __CLASS__, 'allow_builder_preview' ), 10, 2 );

		Rsmspark_Buttons_Icons::init();
		Rsmspark_Buttons_Ajax::init();
		Rsmspark_Buttons_Settings::init();
		Rsmspark_Buttons_Admin::init();
	}

	/**
	 * Register [rsm_button] directly so it always exists when the plugin is enabled.
	 *
	 * @return void
	 */
	public static function register_shortcode() {
		if ( ! class_exists( 'Rsmspark_Content_Widgets_Settings', false ) || ! Rsmspark_Content_Widgets_Settings::is_enabled() ) {
			return;
		}

		if ( shortcode_exists( 'rsmspark_button' ) ) {
			remove_shortcode( 'rsmspark_button' );
		}

		add_shortcode( 'rsmspark_button', array( __CLASS__, 'shortcode' ) );
	}

	/**
	 * Process literal shortcodes in SiteOrigin / widget HTML areas.
	 *
	 * @return void
	 */
	public static function register_content_filters() {
		if ( self::$content_filters_registered ) {
			return;
		}

		self::$content_filters_registered = true;

		$shortcode_filters = array(
			'the_content',
			'the_excerpt',
			'widget_text',
			'widget_text_content',
			'widget_block_content',
			'siteorigin_panels_widget_content',
			'siteorigin_widgets_text',
		);

		foreach ( $shortcode_filters as $filter ) {
			add_filter( $filter, array( __CLASS__, 'maybe_do_shortcode' ), 11 );
			add_filter( $filter, array( __CLASS__, 'render_literal_shortcodes' ), 99 );
		}
	}

	/**
	 * Only expand shortcodes on the front end and in SiteOrigin live preview — not during post save.
	 *
	 * @param string $content Content.
	 * @return string
	 */
	public static function maybe_do_shortcode( $content ) {
		if ( ! self::should_expand_shortcodes_in_content() ) {
			return $content;
		}

		return do_shortcode( $content );
	}

	/**
	 * @return bool
	 */
	public static function should_expand_shortcodes_in_content() {
		if ( ! is_admin() ) {
			return true;
		}

		if ( ! empty( $GLOBALS['SITEORIGIN_PANELS_PREVIEW_RENDER'] ) || ! empty( $GLOBALS['SITEORIGIN_PANELS_CACHE_RENDER'] ) ) {
			return true;
		}

		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request context check; no state change.
			$action = sanitize_key( wp_unslash( (string) ( $_REQUEST['action'] ?? '' ) ) );
			if ( 0 === strpos( $action, 'so_' ) || 0 === strpos( $action, 'sow_' ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Render [rsm_button] even when shortcode registration timing is late (RSM Calendar pattern).
	 *
	 * @param false|string $return Short-circuit return.
	 * @param string       $tag    Shortcode tag.
	 * @param array|string $attr   Attributes.
	 * @param array<int, string> $match Regex match.
	 * @return false|string
	 */
	public static function pre_render_shortcode( $return, $tag, $attr, $match ) {
		unset( $match );

		if ( 'rsmspark_button' !== $tag ) {
			return $return;
		}

		$atts = is_array( $attr ) ? $attr : array();
		return self::shortcode( $atts );
	}

	/**
	 * @return bool
	 */
	public static function shortcodes_active() {
		return class_exists( 'Rsmspark_Content_Widgets_Settings', false )
			&& Rsmspark_Content_Widgets_Settings::is_enabled()
			&& Rsmspark_Content_Widgets_Settings::shortcodes_enabled();
	}

	/**
	 * @param string $content Content.
	 * @return string
	 */
	public static function render_literal_shortcodes( $content, $instance = null, $widget = null ) {
		unset( $instance, $widget );

		if ( ! is_string( $content ) || ! self::content_has_button_shortcode( $content ) ) {
			return $content;
		}

		if ( ! self::should_expand_shortcodes_in_content() ) {
			return $content;
		}

		return self::expand_button_shortcodes( $content );
	}

	/**
	 * @param string $content Raw content.
	 * @return bool
	 */
	public static function content_has_button_shortcode( $content ) {
		if ( ! is_string( $content ) || '' === $content ) {
			return false;
		}

		$normalized = self::normalize_shortcode_literals( $content );

		return (bool) preg_match( '/\[rsm_button\b/i', $normalized );
	}

	/**
	 * @param string $content Raw content.
	 * @return string
	 */
	public static function normalize_shortcode_literals( $content ) {
		if ( ! is_string( $content ) || '' === $content ) {
			return '';
		}

		if ( strpos( $content, '&' ) !== false ) {
			$content = html_entity_decode( $content, ENT_QUOTES | ENT_HTML5, 'UTF-8' );
		}

		$content = str_replace(
			array( "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x98", "\xE2\x80\x99", "\xC2\xAB", "\xC2\xBB" ),
			array( '"', '"', "'", "'", '"', '"' ),
			$content
		);
		$content = (string) preg_replace( '/\[\s*rsm_button/i', '[rsm_button', $content );
		$content = (string) preg_replace( '/\[rsm_button(?:\xC2\xA0|&nbsp;|\s)+/i', '[rsm_button ', $content );

		return trim( $content );
	}

	/**
	 * Parse attributes from a literal [rsm_button ...] string.
	 *
	 * @param string $literal Shortcode literal.
	 * @return array<string, mixed>
	 */
	public static function parse_shortcode_literal( $literal ) {
		$literal = self::normalize_shortcode_literals( (string) $literal );
		if ( ! preg_match( '/\[rsm_button\b([^\]]*)\]/i', $literal, $matches ) ) {
			return array();
		}

		$raw_atts = trim( (string) ( $matches[1] ?? '' ) );
		if ( '' === $raw_atts ) {
			return array();
		}

		$atts = array();
		if ( function_exists( 'shortcode_parse_atts' ) ) {
			$parsed = shortcode_parse_atts( $raw_atts );
			if ( is_array( $parsed ) ) {
				foreach ( $parsed as $key => $value ) {
					if ( is_int( $key ) && is_string( $value ) && preg_match( '/^id=(.+)$/i', $value, $id_match ) ) {
						$atts['id'] = trim( $id_match[1], "\"' " );
						continue;
					}
					$atts[ (string) $key ] = $value;
				}
			}
		}

		if ( empty( $atts['id'] ) && preg_match( '/\bid\s*=\s*(["\']?)([^"\'\s\]]+)\1/i', $raw_atts, $id_match ) ) {
			$atts['id'] = $id_match[2];
		}

		return $atts;
	}

	/**
	 * Render a literal [rsm_button] without relying on do_shortcode().
	 *
	 * @param string $literal Shortcode literal.
	 * @return string
	 */
	public static function render_shortcode_literal( $literal ) {
		$literal = self::normalize_shortcode_literals( (string) $literal );
		if ( '' === $literal || ! self::content_has_button_shortcode( $literal ) ) {
			return '';
		}

		$atts = self::parse_shortcode_literal( $literal );
		if ( empty( $atts ) ) {
			self::register_shortcode();
			$expanded = do_shortcode( shortcode_unautop( $literal ) );
			if ( '' !== trim( $expanded ) && $expanded !== $literal ) {
				return $expanded;
			}
			return '';
		}

		return self::shortcode( $atts );
	}

	/**
	 * @param string $content Content possibly containing [rsm_button].
	 * @return string
	 */
	public static function expand_button_shortcodes( $content ) {
		if ( ! is_string( $content ) || ! self::content_has_button_shortcode( $content ) ) {
			return $content;
		}

		$content = self::normalize_shortcode_literals( $content );

		return (string) preg_replace_callback(
			'/\[rsm_button\b[^\]]*\]/i',
			static function ( $matches ) {
				$rendered = self::render_shortcode_literal( (string) ( $matches[0] ?? '' ) );
				return '' !== trim( $rendered ) ? $rendered : (string) ( $matches[0] ?? '' );
			},
			shortcode_unautop( $content )
		);
	}

	/**
	 * Allow SiteOrigin / builder previews even when widget output is gated.
	 *
	 * @param bool   $should      Whether rendering is allowed.
	 * @param string $registry_id Widget registry id.
	 * @return bool
	 */
	public static function allow_builder_preview( $should, $registry_id ) {
		$id = sanitize_key( (string) $registry_id );
		if ( $should || 'buttons' !== $id ) {
			return $should;
		}

		if ( class_exists( 'Rsmspark_Content_Widgets_Blocks', false ) && Rsmspark_Content_Widgets_Blocks::is_editor_preview_request() ) {
			return true;
		}

		if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
			return $should;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only request context check; no state change.
		$action = sanitize_key( wp_unslash( (string) ( $_REQUEST['action'] ?? '' ) ) );

		return 0 === strpos( $action, 'so_' ) || 0 === strpos( $action, 'sow_' );
	}

	/**
	 * @param array<string, mixed> $atts Shortcode attributes.
	 * @param string               $content Content.
	 * @return string
	 */
	public static function shortcode( $atts, $content = '' ) {
		unset( $content );

		if ( ! class_exists( 'Rsmspark_Content_Widgets_Settings', false ) || ! Rsmspark_Content_Widgets_Settings::is_enabled() ) {
			return '';
		}

		$atts = is_array( $atts ) ? $atts : array();

		$button_id = absint( $atts['id'] ?? 0 );
		$config    = array();

		$post = null;
		if ( $button_id > 0 ) {
			$post = get_post( $button_id );
			if ( $post && ! in_array( $post->post_status, array( 'trash', 'auto-draft' ), true ) ) {
				$config = Rsmspark_Buttons_Config::get( $button_id );
				if ( '' === trim( (string) $config['text_line_1'] ) && '' !== trim( (string) $post->post_title ) ) {
					$config['text_line_1'] = $post->post_title;
				}
			}
		}

		$override_atts = $atts;
		unset( $override_atts['id'] );

		$overrides = Rsmspark_Buttons_Config::sanitize_atts_partial( $override_atts );
		if ( ! empty( $overrides ) ) {
			$config = wp_parse_args( $overrides, $config );
		}

		$html = Rsmspark_Buttons_Render::render( $config, $button_id );
		if ( class_exists( 'Rsmspark_Content_Widgets_Kses', false )
			&& method_exists( 'Rsmspark_Content_Widgets_Kses', 'allowed_html' )
		) {
			return wp_kses( $html, Rsmspark_Content_Widgets_Kses::allowed_html() );
		}

		return wp_kses_post( $html );
	}

	/**
	 * @return array<int, string> id => title
	 */
	public static function get_saved_buttons_options() {
		$posts = get_posts(
			array(
				'post_type'      => Rsmspark_Buttons_CPT::get_post_types(),
				'post_status'    => 'publish',
				'posts_per_page' => 200,
				'orderby'        => 'title',
				'order'          => 'ASC',
			)
		);

		$options = array( '' => __( '— Select a saved button —', 'rsm-content-widgets' ) );
		foreach ( $posts as $post ) {
			$options[ (string) $post->ID ] = $post->post_title;
		}

		return $options;
	}

	/**
	 * Pages and posts for the button link dropdown.
	 *
	 * @return array<string, string> id => label
	 */
	public static function get_link_post_options() {
		$posts = get_posts(
			array(
				'post_type'      => array( 'page', 'post' ),
				'post_status'    => 'publish',
				'posts_per_page' => 300,
				'orderby'        => 'title',
				'order'          => 'ASC',
			)
		);

		$options = array( '' => __( '— Select a page or post —', 'rsm-content-widgets' ) );
		foreach ( $posts as $post ) {
			$type_label              = 'page' === $post->post_type
				? __( 'Page', 'rsm-content-widgets' )
				: __( 'Post', 'rsm-content-widgets' );
			$options[ (string) $post->ID ] = sprintf(
				'%s (%s)',
				$post->post_title,
				$type_label
			);
		}

		return $options;
	}

	/**
	 * Activation tasks.
	 *
	 * @return void
	 */
	public static function activate() {
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-config.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-cpt.php';
		require_once RSMSPARK_CONTENT_WIDGETS_PATH . 'includes/buttons/class-rsmspark-buttons-seed.php';

		Rsmspark_Buttons_CPT::register();
		Rsmspark_Buttons_CPT::register_capabilities();
		Rsmspark_Buttons_Seed::maybe_create_defaults();
		flush_rewrite_rules();
	}
}
