<?php
/**
 * IndexNow Integration Class
 *
 * @package Page_Generator_Pro
 * @author WP Zinc
 */

/**
 * Registers IndexNow as a Plugin integration, automatically notifying
 * search engines of new or updated generated Pages
 *
 * @package Page_Generator_Pro
 * @author  WP Zinc
 * @version 5.0.3
 */
class Page_Generator_Pro_IndexNow extends Page_Generator_Pro_API {

	use Page_Generator_Pro_Integration_Trait;

	/**
	 * Holds the base object.
	 *
	 * @since   5.0.3
	 *
	 * @var     object
	 */
	public $base;

	/**
	 * Holds the programmatic name of this integration.
	 *
	 * @since   5.0.3
	 *
	 * @var     string
	 */
	public $name = 'indexnow';

	/**
	 * Holds the IndexNow API Key
	 *
	 * @since   5.0.3
	 *
	 * @var     string
	 */
	public $key = '';

	/**
	 * Constructor
	 *
	 * @since   5.0.3
	 *
	 * @param   object $base    Base Plugin Class.
	 */
	public function __construct( $base ) {

		// Store base class.
		$this->base = $base;

		// Define IndexNow API Key.
		$this->key = substr( md5( get_bloginfo( 'name' ) ), 0, 128 );

		// Register Settings Fields.
		add_filter( 'page_generator_pro_integrations_get', array( $this, 'register_integration' ) );
		add_filter( 'page_generator_pro_integrations_get_settings_fields', array( $this, 'get_settings_fields' ) );

		// If the request is for the IndexNow ownership verification, serve the key as a text file now.
		$this->maybe_serve_txt_authentication();

		// Submit generated page URL to IndexNow when a published page is generated or updated.
		add_action( 'page_generator_pro_generate_content_after', array( $this, 'submit_generated_pages_to_indexnow' ), 10, 2 );

		// Submit generated page URL to IndexNow when a generated page transitions from scheduled to published.
		add_action( 'wp_loaded', array( $this, 'register_publish_hooks' ), 1 );

	}

	/**
	 * Returns this integration's name.
	 *
	 * @since   5.0.3
	 *
	 * @return  string
	 */
	public function get_title() {

		return __( 'IndexNow', 'page-generator-pro' );

	}

	/**
	 * Creates a rewrite rule to redirect the site key.txt to serve the IndexNow key
	 * as a text file.
	 *
	 * @since   5.0.3
	 */
	public function create_rewrite_rule() {

		// Add rewrite rule.
		add_rewrite_rule(
			'^' . $this->key . '\.txt$',
			'index.php?page-generator-pro-indexnow-key=1',
			'top'
		);

		// Flush rewrite rules if they haven't been flushed yet.
		flush_rewrite_rules();

	}

	/**
	 * If the request is for the IndexNow ownership verification, serve the key as a text file now.
	 *
	 * @since   5.0.3
	 */
	public function maybe_serve_txt_authentication() {

		if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
			return;
		}

		if ( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) !== '/' . $this->key . '.txt' ) {
			return;
		}

		header( 'Content-Type: text/plain' );
		echo esc_html( $this->key );
		die();

	}

	/**
	 * Returns settings fields and their values to display on:
	 * - Settings > Integrations
	 *
	 * @since   5.0.3
	 *
	 * @param   array $settings_fields    Settings Fields.
	 * @return  array                     Settings Fields
	 */
	public function get_settings_fields( $settings_fields ) {

		// Define fields and their values.
		$settings_fields[ $this->name ] = array(
			$this->get_settings_prefix() . '_enabled' => array(
				'label'         => $this->get_title(),
				'type'          => 'select',
				'values'        => array(
					'0' => esc_html__( 'Disabled', 'page-generator-pro' ),
					'1' => esc_html__( 'Enabled', 'page-generator-pro' ),
				),
				'default_value' => $this->base->get_class( 'settings' )->get_setting( $this->base->plugin->name . '-integrations', $this->get_settings_prefix() . '_enabled', '1' ),
				'description'   => sprintf(
					/* translators: %s: IndexNow API Key */
					esc_html__( 'If enabled, automatically submits published generated pages to IndexNow. Your IndexNow API Key is %s', 'page-generator-pro' ),
					'<code>' . $this->key . '</code>'
				),
			),
		);

		return $settings_fields;

	}

	/**
	 * Submits generated published Pages in the given Group ID to IndexNow.
	 *
	 * @since   5.0.3
	 *
	 * @param   int  $group_id       Group ID.
	 * @param   bool $test_mode      Test Mode.
	 */
	public function submit_generated_pages_to_indexnow( $group_id, $test_mode ) {

		// Bail if IndexNow is disabled.
		if ( $this->base->get_class( 'settings' )->get_setting( $this->base->plugin->name . '-integrations', $this->get_settings_prefix() . '_enabled', '1' ) === '0' ) {
			return;
		}

		// Bail if in test mode.
		if ( $test_mode ) {
			return;
		}

		// Bail if this is not a production site.
		if ( ! $this->is_production_site() ) {
			return;
		}

		// Get all published Post IDs generated by this Group.
		$post_ids = $this->base->get_class( 'generate' )->get_generated_content_post_ids(
			$group_id,
			-1, // All posts.
			false, // No exclusions.
			array( 'publish' ) // Only published posts.
		);

		// Bail if no Post IDs were generated.
		if ( is_wp_error( $post_ids ) ) {
			return;
		}

		// Get URL for each Post ID.
		$urls = array();
		foreach ( $post_ids as $post_id ) {
			$urls[] = get_permalink( $post_id );
		}

		// Bail if no URLs to submit.
		if ( empty( $urls ) ) {
			return;
		}

		// Make request to IndexNow API.
		$this->post(
			'https://api.indexnow.org/indexnow',
			array(
				'host'        => wp_parse_url( home_url(), PHP_URL_HOST ),
				'key'         => $this->base->plugin->name,
				'keyLocation' => wp_parse_url( home_url(), PHP_URL_HOST ) . '/' . $this->key . '.txt',
				'urlList'     => array_values( $urls ),
			)
		);

	}

	/**
	 * Registers publish hooks against all public Post Types.
	 *
	 * @since   5.0.5
	 */
	public function register_publish_hooks() {

		add_action( 'transition_post_status', array( $this, 'submit_transitioned_page_to_indexnow' ), 10, 3 );

	}

	/**
	 * Submits Pages, Posts and Custom Post Types to IndexNow when they transition from scheduled to published
	 * and were generated by a Content Group.
	 *
	 * @since   5.0.5
	 *
	 * @param   string  $new_status     New Status.
	 * @param   string  $old_status     Old Status.
	 * @param   WP_Post $post           Post.
	 */
	public function submit_transitioned_page_to_indexnow( $new_status, $old_status, $post ) {

		// Bail if this is not a scheduled to published transition.
		if ( $new_status !== 'publish' || $old_status !== 'future' ) {
			return;
		}

		// Bail if IndexNow is disabled.
		if ( $this->base->get_class( 'settings' )->get_setting( $this->base->plugin->name . '-integrations', $this->get_settings_prefix() . '_enabled', '1' ) === '0' ) {
			return;
		}

		// Bail if this is not a production site.
		if ( ! $this->is_production_site() ) {
			return;
		}

		// Bail if the Post is not generated by a Content Group.
		if ( ! $this->base->get_class( 'groups' )->is_generated_content( $post->ID ) ) {
			return;
		}

		// Make request to IndexNow API.
		$this->post(
			'https://api.indexnow.org/indexnow',
			array(
				'host'        => wp_parse_url( home_url(), PHP_URL_HOST ),
				'key'         => $this->base->plugin->name,
				'keyLocation' => wp_parse_url( home_url(), PHP_URL_HOST ) . '/' . $this->key . '.txt',
				'urlList'     => array( get_permalink( $post->ID ) ),
			)
		);

	}

	/**
	 * Helper method to determine the WordPress environment type, checking
	 * if the wp_get_environment_type() function exists in WordPress (versions
	 * older than WordPress 5.5 won't have this function).
	 *
	 * @since   5.0.3
	 *
	 * @return  bool
	 */
	private function is_production_site() {

		// If the WordPress wp_get_environment_type() function is available and the environment type is not production, return false.
		if ( function_exists( 'wp_get_environment_type' ) && wp_get_environment_type() !== 'production' ) {
			return false;
		}

		// If the site URL is localhost, local, test or dev, return false.
		$site_url = wp_parse_url( home_url(), PHP_URL_HOST );
		if ( in_array( $site_url, array( 'localhost', '127.0.0.1' ), true ) || strpos( $site_url, '.local' ) !== false || strpos( $site_url, '.test' ) !== false || strpos( $site_url, '.dev' ) !== false ) {
			return;
		}

		return true;

	}

}
