<?php
/**
 * Visibility Handler — manages hiding protected content from search, excerpts, and feeds.
 *
 * @package PrivexPasswordProtect
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class PPMSTR_Visibility_Handler
 */
class PPMSTR_Visibility_Handler {

	/**
	 * @var PPMSTR_Access_Checker
	 */
	private $access;

	/**
	 * Constructor.
	 *
	 * @param PPMSTR_Access_Checker $access Access checker.
	 */
	public function __construct( PPMSTR_Access_Checker $access ) {
		$this->access = $access;
	}

	/**
	 * Register hooks.
	 */
	public function init() {
		$settings = get_option( 'ppmstr_settings', array() );

		// Hide from search.
		if ( ! empty( $settings['hide_from_search'] ) ) {
			add_action( 'pre_get_posts', array( $this, 'hide_protected_from_search' ) );
		}

		// No Index.
		if ( ! empty( $settings['no_index_protected'] ) ) {
			add_action( 'wp_head', array( $this, 'add_noindex_tag' ) );
		}

		// Hide from RSS.
		if ( ! empty( $settings['hide_from_rss'] ) ) {
			add_filter( 'the_content_feed', array( $this, 'filter_feeds' ), 999 );
			add_filter( 'the_excerpt_rss', array( $this, 'filter_feeds' ), 999 );
		}

		// Excerpt Filtering.
		add_filter( 'the_excerpt', array( $this, 'filter_protected_excerpt' ), 999 );
		add_filter( 'get_the_excerpt', array( $this, 'filter_protected_excerpt' ), 999 );

		// Cache invalidation.
		add_action( 'save_post', array( $this, 'clear_cache' ) );
		add_action( 'deleted_post', array( $this, 'clear_cache' ) );
		add_action( 'ppmstr_settings_updated', array( $this, 'clear_cache' ) );
	}

	/**
	 * Clear the locked post IDs cache.
	 */
	public function clear_cache() {
		delete_transient( 'ppmstr_locked_ids' );
	}

	/**
	 * Exclude protected posts from search queries.
	 *
	 * @param WP_Query $query The query object.
	 */
	public function hide_protected_from_search( $query ) {
		if ( is_admin() || ! $query->is_main_query() || ! $query->is_search() ) {
			return;
		}

		// This approach is a bit expensive but precise.
		// For a more performant approach on huge sites, we'd need a custom meta query.
		// However, for standard WP sites, we can gather IDs to exclude.
		$exclude_ids = $this->get_locked_post_ids();
		if ( ! empty( $exclude_ids ) ) {
			$query->set( 'post__not_in', array_merge( (array) $query->get( 'post__not_in' ), $exclude_ids ) );
		}
	}

	/**
	 * Filter excerpts for protected posts.
	 *
	 * @param string $excerpt Original excerpt.
	 * @return string
	 */
	public function filter_protected_excerpt( $excerpt ) {
		if ( is_admin() ) {
			return $excerpt;
		}

		$post_id = get_the_ID();
		if ( ! $post_id ) {
			return $excerpt;
		}

		if ( ! $this->access->can_fully_access_post( $post_id ) ) {
			return '<em>' . esc_html__( 'This content is protected and requires a password to view.', 'privex-password-protect' ) . '</em>';
		}

		return $excerpt;
	}

	/**
	 * Filter RSS feed content for protected posts.
	 *
	 * @param string $content Original feed content.
	 * @return string
	 */
	public function filter_feeds( $content ) {
		$post_id = get_the_ID();
		if ( ! $post_id ) {
			return $content;
		}

		if ( ! $this->access->can_fully_access_post( $post_id ) ) {
			return esc_html__( 'This content is protected. Please visit the website and enter the password to read the full post.', 'privex-password-protect' );
		}

		return $content;
	}

	/**
	 * Helper to get all locked post IDs (those with individual passwords or in locked containers).
	 *
	 * @return array
	 */
	private function get_locked_post_ids() {
		$cached = get_transient( 'ppmstr_locked_ids' );
		if ( false !== $cached ) {
			return (array) $cached;
		}

		// 1. Posts with individual protection enabled.
		// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_query
		$locked_posts = get_posts(
			array(
				'post_type'      => 'any',
				'post_status'    => 'any',
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'meta_query'     => array(
					array(
						'key'   => '_ppmstr_protected',
						'value' => '1',
					),
				),
			)
		);
		// phpcs:enable

		$exclude = array();
		foreach ( (array) $locked_posts as $post_id ) {
			if ( ! $this->access->can_fully_access_post( (int) $post_id ) ) {
				$exclude[] = (int) $post_id;
			}
		}

		$exclude = array_unique( $exclude );
		set_transient( 'ppmstr_locked_ids', $exclude, HOUR_IN_SECONDS );

		return $exclude;
	}

	/**
	 * Add noindex meta tag to protected posts.
	 */
	public function add_noindex_tag() {
		if ( ! is_singular() ) {
			return;
		}

		$post_id = get_the_ID();
		if ( ! $post_id ) {
			return;
		}

		if ( ! $this->access->can_fully_access_post( $post_id ) ) {
			echo '<meta name="robots" content="noindex, nofollow" />' . "\n";
		}
	}
}
