<?php

namespace Simple_History\Dropins;

use Simple_History\Helpers;
use Simple_History\Simple_History;
use Simple_History\Log_Query;
use Simple_History\Log_Levels;
use Simple_History\Compat;

/**
 * Dropin Name: Global RSS Feed
 * Dropin URI: http://simple-history.com/
 * Author: Pär Thernström
 */
class RSS_Dropin extends Dropin {
	/**
	 * @inheritdoc
	 */
	public function loaded() {
		if ( ! function_exists( 'get_editable_roles' ) ) {
			/** @phpstan-ignore requireOnce.fileNotFound */
			require_once ABSPATH . '/wp-admin/includes/user.php';
		}

		// Check the status of the RSS feed.
		$this->is_rss_enabled();

		// Generate a rss secret, if it does not exist.
		if ( ! $this->get_rss_secret() ) {
			$this->update_rss_secret();
		}

		add_action( 'init', array( $this, 'check_for_rss_feed_request' ) );

		// Add settings with priority 15 so it' added after the main Simple History settings.
		add_action( 'admin_menu', array( $this, 'add_settings' ), 15 );
	}

	/**
	 * Add settings for the RSS feed.
	 *
	 * Also regenerates the secret if requested.
	 */
	public function add_settings() {
		// Register a setting to keep track of the RSS feed status (enabled/disabled).
		register_setting(
			Simple_History::SETTINGS_GENERAL_OPTION_GROUP,
			'simple_history_enable_rss_feed',
			array(
				'sanitize_callback' => array(
					Helpers::class,
					'sanitize_checkbox_input',
				),
			)
		);

		/**
		 * Start new section for RSS feed.
		 *
		 * @var string $settings_section_rss_id ID of the section
		 */
		$settings_section_rss_id = 'simple_history_settings_section_rss';

		/**
		 * Filters the title for the RSS feed section headline.
		 *
		 * @var string $rss_section_title
		 */
		$rss_section_title = apply_filters(
			'simple_history/feeds/settings_section_title',
			_x( 'RSS feed', 'rss settings headline', 'simple-history' )
		);

		Helpers::add_settings_section(
			$settings_section_rss_id,
			[ $rss_section_title, 'rss_feed' ],
			array( $this, 'settings_section_output' ),
			Simple_History::SETTINGS_MENU_SLUG // same slug as for options menu page.
		);

		// Enable/Disable RSS feed.
		add_settings_field(
			'simple_history_enable_rss_feed',
			Helpers::get_settings_field_title_output( __( 'Enable', 'simple-history' ), 'toggle-on' ),
			array( $this, 'settings_field_rss_enable' ),
			Simple_History::SETTINGS_MENU_SLUG,
			$settings_section_rss_id
		);

		// If RSS is activated we display other fields.
		if ( $this->is_rss_enabled() ) {
			// RSS address.
			add_settings_field(
				'simple_history_rss_feed',
				Helpers::get_settings_field_title_output( __( 'Address', 'simple-history' ), 'link' ),
				array( $this, 'settings_field_rss' ),
				Simple_History::SETTINGS_MENU_SLUG,
				$settings_section_rss_id
			);

			// Link button to regenerate RSS secret.
			add_settings_field(
				'simple_history_rss_feed_regenerate_secret',
				Helpers::get_settings_field_title_output( __( 'Regenerate', 'simple-history' ), 'autorenew' ),
				array( $this, 'settings_field_rss_regenerate' ),
				Simple_History::SETTINGS_MENU_SLUG,
				$settings_section_rss_id
			);
		}

		// Create new RSS secret.
		$create_secret_nonce_name = 'simple_history_rss_secret_regenerate_nonce';
		$create_nonce_ok = isset( $_GET[ $create_secret_nonce_name ] ) && wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET[ $create_secret_nonce_name ] ) ), 'simple_history_rss_update_secret' );

		if ( $create_nonce_ok ) {
			$this->update_rss_secret();

			// Add updated-message and store in transient and then redirect
			// This is the way options.php does it.
			$msg = __( 'Created new secret RSS address', 'simple-history' );
			add_settings_error( 'simple_history_rss_feed_regenerate_secret', 'simple_history_rss_feed_regenerate_secret', $msg, 'updated' );
			set_transient( 'settings_errors', get_settings_errors(), 30 );

			/**
			 * Fires after RSS secret has been updated.
			 */
			do_action( 'simple_history/rss_feed/secret_updated' );

			$goback = esc_url_raw( add_query_arg( 'settings-updated', 'true', wp_get_referer() ) );
			wp_redirect( $goback );
			exit;
		}
	}

	/**
	 * Check if RSS feed is enabled or disabled.
	 *
	 * @return bool true if enabled, false if disabled
	 */
	public function is_rss_enabled() {
		$is_enabled = false;

		// User has never used the plugin we disable RSS feed.
		if ( $this->get_rss_secret() === false && get_option( 'simple_history_enable_rss_feed' ) === false ) {
			// We disable RSS by default, we use 0/1 to prevent fake disabled with bools from functions returning false for unset.
			update_option( 'simple_history_enable_rss_feed', '0' );
		} elseif ( get_option( 'simple_history_enable_rss_feed' ) === false ) {
			// User was using the plugin before RSS feed became disabled by default.
			// We activate RSS to prevent a "breaking change".
			update_option( 'simple_history_enable_rss_feed', '1' );
			$is_enabled = true;
		} elseif ( get_option( 'simple_history_enable_rss_feed' ) === '1' ) {
			$is_enabled = true;
		}

		return $is_enabled;
	}

	/**
	 * Output for settings field that show current RSS address.
	 */
	public function settings_field_rss_enable() {
		/**
		 * Filters the text for the RSS enable checkbox.
		 *
		 * @var string $enable_rss_text
		 */
		$enable_rss_text = apply_filters(
			'simple_history/feeds/enable_feeds_checkbox_text',
			__( 'Enable RSS feed', 'simple-history' )
		);

		?>
		<input value="1" type="checkbox" id="simple_history_enable_rss_feed" name="simple_history_enable_rss_feed" <?php checked( $this->is_rss_enabled(), 1 ); ?> />
		<label for="simple_history_enable_rss_feed"><?php echo esc_html( $enable_rss_text ); ?></label>
		<?php
	}

	/**
	 * Check if current request is a request for the RSS feed.
	 */
	public function check_for_rss_feed_request() {
		if ( isset( $_GET['simple_history_get_rss'] ) ) {
			$this->output_rss();
			exit;
		}
	}

	/**
	 * Get the RSS secret.
	 *
	 * @return bool|string RSS secret or false if not set.
	 */
	public function get_rss_secret() {
		return get_option( 'simple_history_rss_secret' );
	}

	/**
	 * Output RSS.
	 */
	public function output_rss() {
		$rss_secret_option = get_option( 'simple_history_rss_secret' );
		$rss_secret_get = sanitize_text_field( wp_unslash( $_GET['rss_secret'] ?? '' ) );

		if ( empty( $rss_secret_option ) || empty( $rss_secret_get ) ) {
			die();
		}

		/** @var bool $rss_show */
		$rss_show = true;

		/**
		 * Filter if RSS feed should be shown or not.
		 * Default is true.
		 * @since 1.3.8
		 * @param bool $rss_show
		 */
		$rss_show = apply_filters( 'simple_history/rss_feed_show', $rss_show );

		if ( ! $rss_show || ! $this->is_rss_enabled() ) {
			wp_die( 'Nothing here.' );
		}

		header( 'Content-Type: text/xml; charset=utf-8' );

		echo '<?xml version="1.0" encoding="UTF-8"?>';

		$self_link = $this->get_rss_address();

		$title = sprintf(
			/* translators: %s blog name */
			__( 'History for %s', 'simple-history' ),
			get_bloginfo( 'name' ),
		);

		$description = sprintf(
			/* translators: %s blog name */
			esc_html__( 'WordPress History for %s', 'simple-history' ),
			get_bloginfo( 'name' )
		);

		if ( $rss_secret_option === $rss_secret_get ) {
			echo PHP_EOL;

			?>
			<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
				<channel>
					<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					<title><?php echo Compat::esc_xml( $title ); ?></title>
					<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					<description><?php echo Compat::esc_xml( $description ); ?></description> 
					<link><?php echo esc_url( get_bloginfo( 'url' ) ); ?></link>
					<atom:link href="<?php echo esc_url( $self_link ); ?>" rel="self" type="application/atom+xml" />
					<?php

					// Override capability check: if you have a valid rss_secret_key you can read it all.
					$action_tag = 'simple_history/loggers_user_can_read/can_read_single_logger';
					add_filter( $action_tag, '__return_true', 10, 0 );

					// Modify header time output so it does not show relative date or time ago-format
					// Because we don't know when a user reads the RSS feed, time ago format may be very inaccurate.
					add_filter( 'simple_history/header_just_now_max_time', '__return_zero' );
					add_filter( 'simple_history/header_time_ago_max_time', '__return_zero' );

					// Set args from query string.
					$args = $this->set_log_query_args_from_query_string( $_GET );

					/**
					 * Filters the arguments passed to `SimpleHistoryLogQuery()` when fetching the RSS feed
					 *
					 * @example Change number of posts to retrieve in RSS feed.
					 *
					 * // This example changes the number of posts in the RSS feed to 50 from the default 10.
					 *
					 * ```php
					 *  add_filter(
					 *    'simple_history/rss_feed_args',
					 *      function( $args ) {
					 *        $args['posts_per_page'] = 50;
					 *        return $args;
					 *     }
					 * );
					 *
					 * @example Change number of posts to retrieve in RSS feed.
					 *
					 * // This example changes the number of posts in the RSS feed to 20 from the default 10.
					 *
					 * ```php
					 *  add_filter(
					 *    'simple_history/rss_feed_args',
					 *      function( $args ) {
					 *        $args['posts_per_page'] = 20;
					 *        return $args;
					 *     }
					 * );
					 *
					 * @param array $args SimpleHistoryLogQuery arguments.
					 * @return array
					 */
					$args = apply_filters( 'simple_history/rss_feed_args', $args );

					$logQuery = new Log_Query();
					$queryResults = $logQuery->query( $args );

					// Remove capability override after query is done
					// remove_action( $action_tag, '__return_true', 10 );.
					foreach ( $queryResults['log_rows'] as $row ) {
						$header_output = $this->simple_history->get_log_row_header_output( $row );
						$text_output = $this->simple_history->get_log_row_plain_text_output( $row );
						$details_output = $this->simple_history->get_log_row_details_output( $row );

						// http://cyber.law.harvard.edu/rss/rss.html#ltguidgtSubelementOfLtitemgt
						// $item_guid = home_url() . "?SimpleHistoryGuid=" . $row->id;.
						$item_guid = esc_url( add_query_arg( 'SimpleHistoryGuid', $row->id, home_url() ) );
						$item_link = esc_url( add_query_arg( 'SimpleHistoryGuid', $row->id, home_url() ) );

						/**
						 * Filter the guid/link URL used in RSS feed.
						 * Link will be esc_url'ed by simple history, so no need to do that in your filter
						 *
						 * @since 2.0.23
						 *
						 * @param string $item_guid link.
						 * @param object $row
						 */
						$item_link = apply_filters( 'simple_history/rss_item_link', $item_link, $row );
						$item_link = esc_url( $item_link );

						$item_title = sprintf(
							'%2$s',
							Log_Levels::get_log_level_translated( $row->level ),
							wp_kses( $text_output, array() )
						);

						$level_output = sprintf(
							// translators: %s is the severity level of the log.
							esc_html__( 'Severity level: %1$s', 'simple-history' ),
							Log_Levels::get_log_level_translated( $row->level )
						);

						$wp_kses_attrs = array(
							'a' => array(
								'href' => array(),
								'class' => array(),
								'data-ip-address' => array(),
								'target' => array(),
								'title' => array(),
							),
							'em' => array(),
							'span' => array(
								'class' => array(),
								'title' => array(),
								'aria-hidden' => array(),
							),
							'time' => array(
								'datetime' => array(),
								'class' => array(),
							),
							'strong' => array(
								'class' => array(),
							),
							'div' => array(
								'class' => array(),
								'tabindex' => array(),
							),
							'p' => array(),
							'del' => array(),
							'ins' => array(),
							'table' => array(
								'class' => array(),
							),
							'tbody' => array(),
							'tr' => array(),
							'td' => array(
								'class' => array(),
							),
							'col' => array(
								'class' => array(),
							),
						);
						?>
						<item>
						<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
							<title><?php echo Compat::esc_xml( $item_title ); ?></title>
							<description><![CDATA[
								<p><?php echo wp_kses( $header_output, $wp_kses_attrs ); ?></p>
								<p><?php echo wp_kses( $text_output, $wp_kses_attrs ); ?></p>
								<div><?php echo wp_kses( $details_output, $wp_kses_attrs ); ?></div>
								<p><?php echo wp_kses( $level_output, $wp_kses_attrs ); ?></p>
								<?php
								// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
								$occasions = $row->subsequentOccasions - 1;

								if ( $occasions ) {
									echo '<p>';
									esc_html(
										sprintf(
											// translators: %1$s is the number of times this log has been repeated.
											_n( '+%1$s occasion', '+%1$s occasions', $occasions, 'simple-history' ),
											(int) $occasions
										)
									);
									echo '</p>';
								}
								?>
							]]></description>
							<?php
							// author must be email to validate, but the field is optional, so we skip it.
							/* <author><?php echo $row->initiator ?></author> */
							?>
							<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
							<pubDate><?php echo Compat::esc_xml( gmdate( 'D, d M Y H:i:s', strtotime( $row->date ) ) ); ?> GMT</pubDate>
							<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
							<guid isPermaLink="false"><![CDATA[<?php echo Compat::esc_xml( $item_guid ); ?>]]></guid>
							<link><![CDATA[<?php echo esc_url( $item_link ); ?>]]></link>
						</item>
						<?php
					} // End foreach().

					?>
				</channel>
			</rss>
			<?php
		} else {
			// RSS secret was not ok.
			echo PHP_EOL;
			?>
			<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
				<channel>
					<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					<title><?php echo Compat::esc_xml( $title ); ?></title>
					<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					<description><?php echo Compat::esc_xml( $description ); ?></description>
					<link><?php echo esc_url( home_url() ); ?></link>
					<atom:link href="<?php echo esc_url( $self_link ); ?>" rel="self" type="application/atom+xml" />
					<item>
						<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
						<title><?php echo Compat::esc_xml( __( 'Wrong RSS secret', 'simple-history' ) ); ?></title>
						<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
						<description><?php echo Compat::esc_xml( __( 'Your RSS secret for Simple History RSS feed is wrong. Please see WordPress settings for current link to the RSS feed.', 'simple-history' ) ); ?></description>
						<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
						<pubDate><?php echo Compat::esc_xml( gmdate( 'D, d M Y H:i:s', time() ) ); ?> GMT</pubDate>
						<?php // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
						<guid><?php echo esc_url( add_query_arg( 'SimpleHistoryGuid', 'wrong-secret', home_url() ) ); ?></guid>
					</item>
				</channel>
			</rss>
			<?php
		}// End if().
	}

	/**
	 * Create a new RSS secret.
	 *
	 * @return string new secret
	 */
	public function update_rss_secret() {
		$rss_secret = '';

		for ( $i = 0; $i < 20; $i++ ) {
			$rss_secret .= chr( random_int( 97, 122 ) );
		}

		update_option( 'simple_history_rss_secret', $rss_secret );

		return $rss_secret;
	}

	/**
	 * Output for settings field that show current RSS address.
	 */
	public function settings_field_rss() {
		printf(
			'<p>
				<code>
					<a id="simple_history_rss_feed_address" href="%1$s">%1$s</a>
				</code>
			</p>',
			esc_url( $this->get_rss_address() )
		);

		echo '<p class="simple_history_rss_feed_query_parameters">';
		echo wp_kses(
			sprintf(
				/* translators: %s is a link to the documentation */
				__( 'Query parameters can be used to control what to include in the feed. <a href="%1$s" class="sh-ExternalLink" target="_blank">View documentation</a>.', 'simple-history' ),
				'https://simple-history.com/docs/feeds/?utm_source=wordpress_admin&utm_medium=Simple_History&utm_campaign=documentation&utm_content=rss-feed-params'
			),
			[
				'a' => [
					'href' => [],
					'target' => [],
					'class' => [],
				],
			]
		);
		echo '</p>';

		/**
		 * Fires after the RSS address has been output.
		 *
		 * @param RSS_Dropin $instance
		 */
		do_action( 'simple_history/feeds/after_address', $this );
	}

	/**
	 * Output for settings field that regenerates the RSS address/secret
	 */
	public function settings_field_rss_regenerate() {
		$update_link = esc_url( add_query_arg( '', '' ) );
		$update_link = wp_nonce_url( $update_link, 'simple_history_rss_update_secret', 'simple_history_rss_secret_regenerate_nonce' );

		echo '<p>';
		esc_html_e( 'You can generate a new address for the RSS feed. This is useful if you think that the address has fallen into the wrong hands.', 'simple-history' );
		echo '</p>';

		echo '<p>';
		printf(
			'<a class="button" href="%1$s">%2$s</a>',
			esc_url( $update_link ), // 1
			esc_html__( 'Generate new address', 'simple-history' ) // 2
		);

		echo '</p>';
	}

	/**
	 * Get the URL to the RSS feed.
	 *
	 * @return string URL
	 */
	public function get_rss_address() {
		$rss_secret = get_option( 'simple_history_rss_secret' );

		$rss_address = add_query_arg(
			array(
				'simple_history_get_rss' => '1',
				'rss_secret' => $rss_secret,
			),
			get_bloginfo( 'url' ) . '/'
		);

		return $rss_address;
	}

	/**
	 * Content for section intro. Leave it be, even if empty.
	 * Called from add_sections_setting.
	 */
	public function settings_section_output() {
		echo '<p>';
		esc_html_e( 'Simple History has a RSS feed which you can subscribe to and receive log updates. Make sure you only share the feed with people you trust, since it can contain sensitive or confidential information.', 'simple-history' );
		echo '</p>';
	}

	/**
	 * Update log query args from query string.
	 *
	 * @param array $args Query string from $_GET.
	 * @return array Updated log query args.
	 */
	public function set_log_query_args_from_query_string( $args ) {
		$posts_per_page = isset( $args['posts_per_page'] ) ? (int) $args['posts_per_page'] : 10;
		$paged = isset( $args['paged'] ) ? (int) $args['paged'] : 1;
		$date_from = isset( $args['date_from'] ) ? sanitize_text_field( $args['date_from'] ) : '';
		$date_to = isset( $args['date_to'] ) ? sanitize_text_field( $args['date_to'] ) : '';
		$loggers = isset( $args['loggers'] ) ? sanitize_text_field( $args['loggers'] ) : [];
		$messages = isset( $args['messages'] ) ? sanitize_text_field( $args['messages'] ) : [];
		$loglevels = isset( $args['loglevels'] ) ? sanitize_text_field( $args['loglevels'] ) : '';

		return [
			'posts_per_page' => $posts_per_page,
			'paged' => $paged,
			'date_from' => $date_from,
			'date_to' => $date_to,
			'loggers' => $loggers,
			'messages' => $messages,
			'loglevels' => $loglevels,
		];
	}
}
