<?php
/**
 * Per-page maintenance exceptions.
 *
 * @package MacaHold
 */

defined( 'ABSPATH' ) || exit;

/**
 * Keeps selected pages open or closed relative to global maintenance mode.
 */
class Maca_Hold_Page_Exceptions {

	/**
	 * Register hooks.
	 *
	 * @return void
	 */
	public function register() {
		add_filter( 'maca_hold_should_bypass', array( $this, 'filter_should_bypass' ), 10, 2 );
		add_filter( 'maca_hold_force_maintenance', array( $this, 'filter_force_maintenance' ) );
		add_filter( 'maca_hold_maintenance_scope', array( $this, 'filter_maintenance_scope' ), 10, 2 );
	}

	/**
	 * @param bool $bypass            Current bypass state.
	 * @param bool $force_maintenance Whether maintenance is forced for this request.
	 * @return bool
	 */
	public function filter_should_bypass( $bypass, $force_maintenance = false ) {
		if ( $bypass ) {
			return $bypass;
		}

		$settings         = Maca_Hold_Settings::get();
		$mode             = (string) ( $settings['page_exception_mode'] ?? '' );
		$page_ids         = $settings['page_exception_ids'] ?? array();
		$on_selected_page = self::is_current_page_selected( $page_ids );

		if ( 'open' === $mode ) {
			if ( ! Maca_Hold_Settings::is_active() ) {
				return false;
			}

			return $on_selected_page;
		}

		if ( 'closed' === $mode && ! empty( $page_ids ) ) {
			return ! $on_selected_page;
		}

		return false;
	}

	/**
	 * @param bool $force Current force-maintenance state.
	 * @return bool
	 */
	public function filter_force_maintenance( $force ) {
		if ( $force ) {
			return $force;
		}

		$settings = Maca_Hold_Settings::get();

		if ( 'closed' !== ( $settings['page_exception_mode'] ?? '' ) ) {
			return false;
		}

		if ( ! Maca_Hold_Settings::is_active() ) {
			return false;
		}

		return self::is_current_page_selected( $settings['page_exception_ids'] ?? array() );
	}

	/**
	 * @param array{effect: string, page_titles: array<int, string>} $scope        Scope data.
	 * @param bool                                                    $enabled_after Whether manual maintenance will be enabled.
	 * @return array{effect: string, page_titles: array<int, string>}
	 */
	public function filter_maintenance_scope( $scope, $enabled_after ) {
		if ( ! is_array( $scope ) ) {
			return $scope;
		}

		$settings  = Maca_Hold_Settings::get();
		$mode      = (string) ( $settings['page_exception_mode'] ?? '' );
		$page_ids  = self::sanitize_page_ids( $settings['page_exception_ids'] ?? array() );
		$page_list = self::get_page_titles( $page_ids );

		if ( empty( $page_ids ) || ( 'open' !== $mode && 'closed' !== $mode ) ) {
			return $scope;
		}

		if ( 'closed' === $mode ) {
			$scope['page_titles'] = $page_list;
			$scope['effect']      = $enabled_after ? 'selected_pages' : 'whole_site_public';

			return $scope;
		}

		if ( $enabled_after ) {
			$scope['page_titles'] = $page_list;
			$scope['effect']      = 'all_except_pages';
		}

		return $scope;
	}

	/**
	 * @param mixed $page_ids Raw page IDs.
	 * @return array<int, int>
	 */
	private static function sanitize_page_ids( $page_ids ) {
		if ( ! is_array( $page_ids ) ) {
			return array();
		}

		return array_values(
			array_unique(
				array_filter(
					array_map( 'absint', $page_ids )
				)
			)
		);
	}

	/**
	 * @param array<int, int> $page_ids Page IDs.
	 * @return array<int, string>
	 */
	public static function get_page_titles( array $page_ids ) {
		$titles = array();

		foreach ( $page_ids as $page_id ) {
			$title = get_the_title( $page_id );

			if ( is_string( $title ) && '' !== trim( $title ) ) {
				$titles[] = $title;
				continue;
			}

			$titles[] = sprintf(
				/* translators: %d: page ID */
				__( 'Page #%d', 'maca-hold' ),
				$page_id
			);
		}

		return $titles;
	}

	/**
	 * @param mixed $page_ids Selected page IDs.
	 * @return bool
	 */
	public static function is_current_page_selected( $page_ids ) {
		if ( ! is_array( $page_ids ) || empty( $page_ids ) ) {
			return false;
		}

		$page_ids = self::sanitize_page_ids( $page_ids );

		if ( empty( $page_ids ) ) {
			return false;
		}

		$queried_id = (int) get_queried_object_id();
		if ( $queried_id > 0 && in_array( $queried_id, $page_ids, true ) ) {
			return true;
		}

		$front_page_id = (int) get_option( 'page_on_front' );
		if ( $front_page_id > 0 && is_front_page() && in_array( $front_page_id, $page_ids, true ) ) {
			return true;
		}

		$posts_page_id = (int) get_option( 'page_for_posts' );
		if ( $posts_page_id > 0 && is_home() && ! is_front_page() && in_array( $posts_page_id, $page_ids, true ) ) {
			return true;
		}

		global $wp;

		if ( isset( $wp->query_vars['page_id'] ) ) {
			$page_id = (int) $wp->query_vars['page_id'];
			if ( $page_id > 0 && in_array( $page_id, $page_ids, true ) ) {
				return true;
			}
		}

		if ( ! empty( $wp->query_vars['pagename'] ) ) {
			$page = get_page_by_path( (string) $wp->query_vars['pagename'] );
			if ( $page instanceof WP_Post && in_array( (int) $page->ID, $page_ids, true ) ) {
				return true;
			}
		}

		if ( ! empty( $wp->query_vars['name'] ) && is_singular( 'page' ) ) {
			$page = get_page_by_path( (string) $wp->query_vars['name'] );
			if ( $page instanceof WP_Post && in_array( (int) $page->ID, $page_ids, true ) ) {
				return true;
			}
		}

		return false;
	}
}
