<?php

namespace Barn2\Plugin\Password_Protected_Categories;

use Barn2\Plugin\Password_Protected_Categories\Dependencies\Lib\Registerable;
use Barn2\Plugin\Password_Protected_Categories\Dependencies\Lib\Service\Premium_Service;

/**
 * This class handles the template display for a protected category or taxonomy archive, and for single posts which belong to a protected category.
 *
 * Depending on the level of protection, a password login form or 404 page will be displayed.
 *
 * @package   Barn2\password-protected-categories
 * @author    Barn2 Plugins <support@barn2.co.uk>
 * @license   GPL-3.0
 * @copyright Barn2 Media Ltd
 */
class Template_Handler implements Premium_Service, Registerable {

	public function register() {
		\add_action( 'template_redirect', [ $this, '__protect_templates' ], 1 ); // after Password_Form->handle_login()
	}

	/**
	 * Protects single and taxonomy templates if they are password protected or private.
	 *
	 * Should run after Password_Form::handle_login() which may have set the passowrd cookie.
	 *
	 * @global WP $wp Main WP instance
	 * @global WP_Query $wp_query The main query
	 */
	public function __protect_templates() {

		global $wp_did_header;

		// Return if we're not loading a template on the front-end.
		if ( ! $wp_did_header ) {
			return;
		}

		$protection      = false;
		$term_visibility = false;

		if ( PPC_Util::is_protectable_category() ) {
			$term            = \get_queried_object();
			$term_visibility = PPC_Util::get_term_visibility( $term );

			if ( $term instanceof \WP_Term ) {
				$protection = PPC_Util::is_protected( PPC_Util::get_term_visibility( $term ) );
			}
		} elseif ( PPC_Util::is_protectable_single_post() ) {
			$protection = PPC_Util::is_protected( PPC_Util::get_the_term_visibility() );
		}

		if ( $protection ) {
			\do_action( 'ppc_protected_category_template', $term_visibility );
			$this->prevent_indexing();
			$this->prevent_caching();
		}

		if ( 'password' === $protection ) {
			$this->handle_password_protected_category();
			\do_action( 'ppc_password_protected_category_template', $term_visibility );
			\add_filter( 'body_class', [ $this, 'body_class_protected_category' ] );
		} elseif ( \in_array( $protection, [ 'user', 'role', 'private' ], true ) ) {
			$this->handle_user_protection();
			\do_action( 'ppc_private_category_template', $term_visibility );
			\add_filter( 'body_class', [ $this, 'body_class_private_category' ] );
		}

	}

	/**
	 * Add class to the body tag.
	 *
	 * @param array $classes
	 * @return array
	 */
	public function body_class_private_category( $classes ) {
		$classes[] = 'ppc-private-category';
		return $classes;
	}

	/**
	 * Add class to the body tag.
	 *
	 * @param array $classes
	 * @return array
	 */
	public function body_class_protected_category( $classes ) {
		$classes[] = 'ppc-protected-category';
		return $classes;
	}

	/**
	 * Set page status to 404 on private cats.
	 *
	 * @return void
	 */
	private function handle_private_category() {
		global $wp, $wp_query;

		$wp->handle_404();

		$wp_query->is_404      = true;
		$wp_query->is_single   = false;
		$wp_query->is_singular = false;
		$wp_query->is_tax      = false;
		$wp_query->is_archive  = false;

		\do_action( 'ppc_handle_private_category' );
	}

	/**
	 * Determine how to handle the protection when a user login is required.
	 *
	 * @return void
	 */
	private function handle_user_protection() {
		\do_action( 'ppc_handle_user_protection' );

		$user_protected_option = PPC_Util::get_option( 'ppc_user_protected', '404' );

		if ( '404' === $user_protected_option ) {
			$this->handle_404();
		} elseif ( 'wplogin' === $user_protected_option ) {
			\wp_safe_redirect( $this->get_login_url_with_current_page_redirect() );
		} elseif ( 'page' === $user_protected_option ) {
			if ( $page_id = PPC_Util::get_option( 'ppc_user_protected_redirect' ) ) {
				$redirect = \add_query_arg( 'redirect_to', \urlencode( \add_query_arg( null, null ) ), \get_permalink( $page_id ) );
				\wp_safe_redirect( $redirect );
			} else {
				\wp_safe_redirect( $this->get_login_url_with_current_page_redirect() );
			}
		}
	}

	/**
	 * Generic method to set a page status to 404.
	 *
	 * @return void
	 */
	private function handle_404() {
		global $wp, $wp_query;

		$wp->handle_404();
		$wp_query->is_404      = true;
		$wp_query->is_single   = false;
		$wp_query->is_singular = false;
		$wp_query->is_tax      = false;
		$wp_query->is_archive  = false;

		\do_action( 'ppc_handle_404' );
	}

	/**
	 * Get login url.
	 *
	 * @return void
	 */
	private function get_login_url_with_current_page_redirect() {
		return \apply_filters( 'ppc_user_protected_login_url', \wp_login_url( \add_query_arg( null, null ) ) );
	}

	private function handle_password_protected_category() {
		global $wp, $wp_query;

		$post = $this->get_spoofed_post();

		// Add our fake post to the cache
		\wp_cache_add( $post->ID, $post, 'posts' );
		\wp_cache_add( $post->ID, [ true ], 'post_meta' );

		// Override main query
		$wp_query->post                 = $post;
		$wp_query->posts                = [ $post ];
		$wp_query->queried_object       = $post;
		$wp_query->queried_object_id    = $post->ID;
		$wp_query->found_posts          = 1;
		$wp_query->post_count           = 1;
		$wp_query->max_num_pages        = 1;
		$wp_query->comment_count        = 0;
		$wp_query->comments             = [];
		$wp_query->is_singular          = true;
		$wp_query->is_page              = true;
		$wp_query->is_single            = false;
		$wp_query->is_attachment        = false;
		$wp_query->is_archive           = false;
		$wp_query->is_category          = false;
		$wp_query->is_tag               = false;
		$wp_query->is_tax               = false;
		$wp_query->is_author            = false;
		$wp_query->is_date              = false;
		$wp_query->is_year              = false;
		$wp_query->is_month             = false;
		$wp_query->is_day               = false;
		$wp_query->is_time              = false;
		$wp_query->is_search            = false;
		$wp_query->is_feed              = false;
		$wp_query->is_comment_feed      = false;
		$wp_query->is_trackback         = false;
		$wp_query->is_home              = false;
		$wp_query->is_embed             = false;
		$wp_query->is_404               = false;
		$wp_query->is_paged             = false;
		$wp_query->is_admin             = false;
		$wp_query->is_preview           = false;
		$wp_query->is_robots            = false;
		$wp_query->is_posts_page        = false;
		$wp_query->is_post_type_archive = false;

		// Update globals
		$GLOBALS['wp_query'] = $wp_query;

		$wp->register_globals();

		\do_action( 'ppc_handle_password_protected_category' );
	}

	private function get_spoofed_post() {
		$post_id              = \rand( 1000000, 10000000 ); // attempt to avoid clash with a valid post
		$post                 = new \stdClass();
		$post->ID             = $post_id;
		$post->post_author    = 1;
		$post->post_date      = \current_time( 'mysql' );
		$post->post_date_gmt  = \current_time( 'mysql', 1 );
		$post->post_status    = 'publish';
		$post->comment_status = 'closed';
		$post->comment_count  = 0;
		$post->ping_status    = 'closed';
		$post->post_type      = 'page';
		$post->filter         = 'raw'; // important
		$post->post_name      = 'term-login-' . $post_id; // append post ID to avoid clash
		$post->post_title     = PPC_Util::get_login_page_title();

		$password_form      = new Password_Form();
		$post->post_content = $password_form->get_password_form( PPC_Util::get_login_form_message(), false );

		// Convert to WP_Post object
		$wp_post = new \WP_Post( $post );

		return \apply_filters( 'ppc_template_spoofed_post', $wp_post );
	}

	private function prevent_caching() {
		// Set headers to prevent caching
		\nocache_headers();

		// Set constants to prevent caching in certain caching plugins
		if ( ! \defined( 'DONOTCACHEPAGE' ) ) {
			\define( 'DONOTCACHEPAGE', true );
		}
		if ( ! \defined( 'DONOTCACHEOBJECT' ) ) {
			\define( 'DONOTCACHEOBJECT', true );
		}
		if ( ! \defined( 'DONOTCACHEDB' ) ) {
			\define( 'DONOTCACHEDB', true );
		}

		\do_action( 'ppc_prevent_caching' );
	}

	private function prevent_indexing() {
		// noindex this page - we add X-Robots-Tag header and set meta robots
		if ( ! \headers_sent() ) {
			\header( 'X-Robots-Tag: noindex, nofollow' );
		}
		\add_action( 'wp_head', [ $this, 'meta_robots_noindex_head' ], 5 );

		\do_action( 'ppc_prevent_indexing' );
	}

	public function meta_robots_noindex_head() {
		echo '<meta name="robots" content="noindex, nofollow" />' . "\n";
	}

}
