<?php
/**
 * Archives Meta Data
 *
 * This file will handle functionality to print meta data for archives in frontend.
 *
 * @package surerank
 * @since 0.0.1
 */

namespace SureRank\Inc\Frontend;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

use SureRank\Inc\Functions\Settings;
use SureRank\Inc\Functions\Validate;
use SureRank\Inc\Traits\Get_Instance;

/**
 * Archives Meta Data
 * This class will handle functionality to print meta data for archives in frontend.
 *
 * @since X.X.X
 */
class Archives {

	use Get_Instance;

	/**
	 * Constructor.
	 *
	 * @since X.X.X
	 */
	public function __construct() {
		add_action( 'template_redirect', [ $this, 'redirect_to_home' ], 1, 1 );
		add_action( 'surerank_print_meta', [ $this, 'paginated_link_relationships' ], 1, 1 );
		add_filter( 'surerank_robots_meta_array', [ $this, 'noindex_paginated_pages' ], 1, 1 );
		add_filter( 'surerank_is_singular_archive', [ $this, 'set_singular_archive' ], 10, 2 );
	}

	/**
	 * Determine if the current page is a singular archive using the filter.
	 *
	 * @param mixed $post Optional. Post object or ID. Defaults to null for the current post.
	 *
	 * @return bool True if it's a singular archive, false otherwise.
	 */
	public static function is_singular_archive( $post = null ) {
		$id = $post ? ( is_int( $post ) ? $post : ( get_post( $post )->ID ?? 0 ) ) : null;
		// Apply filter to determine singular archive status.
		return (bool) apply_filters(
			'surerank_is_singular_archive',
			static::is_static_front_page_as_blog( $id ),
			$id
		);
	}

	/**
	 * Set Singular Archive using the filter.
	 *
	 * @param bool     $is_singular_archive Current singular archive state.
	 * @param int|null $id                  The ID of the post or page being checked.
	 *
	 * @return bool Updated singular archive state.
	 */
	public function set_singular_archive( $is_singular_archive, $id ) {
		$shop_page_id = $this->get_shop_page_id();
		return $is_singular_archive || ( $shop_page_id && $this->is_shop_page( $id ) );
	}

	/**
	 * Check if the front page is configured to display blog posts.
	 *
	 * @param int|null $id Optional. Post ID to check. Defaults to null.
	 *
	 * @return bool True if the front page is set as the blog, false otherwise.
	 */
	public static function is_static_front_page_as_blog( $id ) {

		if ( self::check_front_page_condition() && \is_home() ) {
			return true;
		}

		if ( 'page' !== get_option( 'show_on_front' ) ) {
			return false;
		}

		$posts_page_id = get_option( 'page_for_posts' );
		return $posts_page_id && ( $id ? $posts_page_id === $id : is_page( $posts_page_id ) );
	}

	/**
	 * Paginated Link Relationships.
	 *
	 * Helps search engines locate the correct page in paginated archives.
	 *
	 * @since 0.0.1
	 *
	 * @return void
	 */
	public function paginated_link_relationships() {
		if ( ! ( is_paged() || is_archive() || is_home() ) ) {
			return;
		}

		$settings = Settings::get();
		if ( empty( $settings['paginated_link_relationships'] ) ) {
			return;
		}
		$single_archive        = $this->is_singular_archive();
		$screen_types          = [
			'homepage' => is_home() && ! $single_archive,
			'archives' => is_archive() && ! $single_archive,
			'pages'    => $single_archive,
		];
		$relationship_settings = Validate::array( $settings['paginated_link_relationships'], array_keys( $screen_types ) );

		foreach ( $screen_types as $type => $is_type ) {
			if ( $is_type && in_array( $type, $relationship_settings ) ) {
				$this->print_rel_links();
				break;
			}
		}
	}

	/**
	 * Set Noindex for paginated archive pages beyond the first.
	 *
	 * @param array<string, mixed>|array<int, array<string, mixed>> $robots_meta Robots meta array.
	 *
	 * @since 0.0.1
	 *
	 * @return array<string, mixed>|array<int, array<string, mixed>>
	 */
	public function noindex_paginated_pages( $robots_meta ) {
		if ( ! is_paged() ) {
			return $robots_meta;
		}

		$settings = Settings::get();
		if ( ! empty( $settings['noindex_paginated_pages'] ) || apply_filters( 'surerank_noindex_paginated_pages', false ) ) {
			$robots_meta = array_map(
				static function ( $value ) {
					return 'index' === $value ? 'noindex' : $value;
				},
				$robots_meta
			);
		}
		return $robots_meta;
	}

	/**
	 * Redirect to home if archive or search is disabled.
	 *
	 * @since 0.0.1
	 * @return void
	 */
	public function redirect_to_home() {
		if ( ! is_archive() && ! is_search() ) {
			return;
		}

		$settings      = Settings::get();
		$archive_types = [
			'author' => 'author_archive',
			'date'   => 'date_archive',
		];

		foreach ( $archive_types as $type => $setting_key ) {
			if ( call_user_func( "is_{$type}" ) && empty( $settings[ $setting_key ] ) ) {
				wp_safe_redirect( home_url(), 301 );
				exit;
			}
		}
	}

	/**
	 * Check if the current front page is not a static page or if there is a page for posts.
	 *
	 * @return bool True if the condition is met, false otherwise.
	 */
	public static function check_front_page_condition() {
		$show_on_front  = \get_option( 'show_on_front' );
		$page_for_posts = \get_option( 'page_for_posts' );

		return 'page' === $show_on_front || $page_for_posts;
	}

	/**
	 * Check if the given page ID is the WooCommerce shop page.
	 *
	 * @param int|null $id Post ID to check.
	 *
	 * @return bool True if it's the WooCommerce shop page, false otherwise.
	 */
	private function is_shop_page( $id ) {
		return function_exists( 'is_shop' ) && is_shop();
	}

	/**
	 * Get the ID of the WooCommerce shop page.
	 *
	 * @return int|null Shop page ID, or null if WooCommerce is not active.
	 */
	private function get_shop_page_id() {
		return function_exists( 'wc_get_page_id' ) ? wc_get_page_id( 'shop' ) : null;
	}

	/**
	 * Print rel links for next and previous pages.
	 *
	 * @since 0.0.1
	 *
	 * @return void
	 */
	private function print_rel_links() {
		global $paged;
		if ( get_next_posts_link() ) {
			echo '<link rel="next" href="' . esc_url( get_pagenum_link( $paged + 1 ) ) . '">' . PHP_EOL;
		}
		if ( get_previous_posts_link() ) {
			echo '<link rel="prev" href="' . esc_url( get_pagenum_link( $paged - 1 ) ) . '">' . PHP_EOL;
		}
	}
}
