<?php
/**
 * Class Story_Archive.
 *
 * @link      https://github.com/googleforcreators/web-stories-wp
 *
 * @copyright 2020 Google LLC
 * @license   https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
 */

/**
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

declare(strict_types = 1);

namespace Google\Web_Stories;

use WP_Post;
use WP_Query;
use WP_Rewrite;

/**
 * Class Story_Archive.
 */
class Story_Archive extends Service_Base {

	/**
	 * Settings instance.
	 *
	 * @var Settings Settings instance.
	 */
	private Settings $settings;

	/**
	 * Story_Post_Type instance.
	 *
	 * @var Story_Post_Type Story_Post_Type instance.
	 */
	private Story_Post_Type $story_post_type;

	/**
	 * Analytics constructor.
	 *
	 * @since 1.13.0
	 *
	 * @param Settings        $settings        Settings instance.
	 * @param Story_Post_Type $story_post_type Experiments instance.
	 * @return void
	 */
	public function __construct( Settings $settings, Story_Post_Type $story_post_type ) {
		$this->settings        = $settings;
		$this->story_post_type = $story_post_type;
	}

	/**
	 * Registers Filters and actions
	 *
	 * @since 1.13.0
	 */
	public function register(): void {
		add_filter( 'pre_handle_404', [ $this, 'redirect_post_type_archive_urls' ], 10, 2 );

		add_action( 'add_option_' . $this->settings::SETTING_NAME_ARCHIVE, [ $this, 'update_archive_setting' ] );
		add_action( 'update_option_' . $this->settings::SETTING_NAME_ARCHIVE, [ $this, 'update_archive_setting' ] );
		add_action( 'add_option_' . $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID, [ $this, 'update_archive_setting' ] );
		add_action( 'update_option_' . $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID, [ $this, 'update_archive_setting' ] );

		add_filter( 'display_post_states', [ $this, 'filter_display_post_states' ], 10, 2 );
		add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ] );
		add_action( 'wp_trash_post', [ $this, 'on_remove_archive_page' ] );
		add_action( 'delete_post', [ $this, 'on_remove_archive_page' ] );
	}

	/**
	 * Handles redirects to the post type archive.
	 *
	 * @since 1.13.0
	 *
	 * @param bool|mixed $bypass Pass-through of the pre_handle_404 filter value.
	 * @param \WP_Query  $query  The WP_Query object.
	 * @return bool|mixed Whether to pass-through or not.
	 */
	public function redirect_post_type_archive_urls( $bypass, WP_Query $query ) {
		global $wp_rewrite;

		if ( $bypass || ! \is_string( $this->story_post_type->get_has_archive() ) || ( ! $wp_rewrite instanceof WP_Rewrite || ! $wp_rewrite->using_permalinks() ) ) {
			return $bypass;
		}

		// 'pagename' is for most permalink types, name is for when the %postname% is used as a top-level field.
		if ( $this->story_post_type::REWRITE_SLUG === $query->get( 'pagename' ) || $this->story_post_type::REWRITE_SLUG === $query->get( 'name' ) ) {
			$redirect_url = get_post_type_archive_link( $this->story_post_type->get_slug() );

			if ( ! $redirect_url ) {
				return $bypass;
			}

			// Only exit if there was actually a location to redirect to.
			// Allows filtering location in tests to verify behavior.
			if ( wp_safe_redirect( $redirect_url, 301 ) ) {
				exit;
			}
		}

		return $bypass;
	}

	/**
	 * Clear rewrite rules on update on setting.
	 *
	 * @since 1.13.0
	 */
	public function update_archive_setting(): void {
		$this->story_post_type->unregister_post_type();
		$this->story_post_type->register_post_type();

		if ( ! \defined( '\WPCOM_IS_VIP_ENV' ) || false === \WPCOM_IS_VIP_ENV ) {
			flush_rewrite_rules( false );
		}
	}

	/**
	 * Modifies the current query to set up the custom archive page.
	 *
	 * @since 1.13.0
	 *
	 * @param WP_Query $query Current query instance, passed by reference.
	 */
	public function pre_get_posts( WP_Query $query ): void {
		if ( ! \is_string( $this->story_post_type->get_has_archive() ) ) {
			return;
		}

		if ( $query->is_admin || ! $query->is_main_query() ) {
			return;
		}

		if ( ! $query->is_post_type_archive( $this->story_post_type->get_slug() ) ) {
			return;
		}

		$custom_archive_page_id = (int) $this->settings->get_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID );

		$query->set( 'page_id', $custom_archive_page_id );
		$query->set( 'post_type', 'page' );
		$query->is_post_type_archive = false;
		$query->is_archive           = false;
		$query->is_singular          = true;
		$query->is_page              = true;
	}

	/**
	 * Resets archive settings when the custom archive page is trashed.
	 *
	 * @since 1.14.0
	 *
	 * @param int $postid Post ID.
	 */
	public function on_remove_archive_page( int $postid ): void {
		if ( 'page' !== get_post_type( $postid ) ) {
			return;
		}

		$custom_archive_page_id = (int) $this->settings->get_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID );

		if ( $custom_archive_page_id !== $postid ) {
			return;
		}

		$this->settings->update_setting( $this->settings::SETTING_NAME_ARCHIVE, 'default' );
		$this->settings->update_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID, 0 );
	}

	/**
	 * Filters the default post display states used in the posts list table.
	 *
	 * @since 1.13.0
	 *
	 * @param string[]|mixed $post_states An array of post display states.
	 * @param WP_Post|null   $post        The current post object.
	 * @return string[]|mixed Filtered post display states.
	 *
	 * @template T
	 *
	 * @phpstan-return ($post_states is array<T> ? array<T> : mixed)
	 */
	public function filter_display_post_states( $post_states, ?WP_Post $post ) {
		if ( ! \is_array( $post_states ) || ! $post ) {
			return $post_states;
		}

		if ( ! \is_string( $this->story_post_type->get_has_archive() ) ) {
			return $post_states;
		}

		$custom_archive_page_id = (int) $this->settings->get_setting( $this->settings::SETTING_NAME_ARCHIVE_PAGE_ID );

		if ( $post->ID === $custom_archive_page_id ) {
			$post_states['web_stories_archive_page'] = __( 'Web Stories Archive Page', 'web-stories' );
		}

		return $post_states;
	}
}
