<?php
/**
 * Custom Breadcrumb Class
 *
 * @package errigal
 * @since 1.2.0
 */

namespace Errigal\Customiser;

defined( 'ABSPATH' ) || exit;

use Errigal\Content\Errigal_Content as Content;
use Errigal\Icon\Errigal_Icon as Icon;
use Errigal\Shared\{
	Errigal_Query as Query,
	Errigal_Site_Info as Site_Info,
	Errigal_Utility as Utility,
	Errigal_Validator as Validator,
};

/**
 * This class is in charge of providing breadcrumb settings and functions.
 *
 * @since 1.2.0
 */
class Errigal_Custom_Breadcrumb extends Errigal_Custom_Base {
	/**
	 * Setting ID: Label: Breadcrumb.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	public const ID_LABEL_BREADCRUMB = 'breadcrumb_label';

	/**
	 * Setting ID: Breadcrumb.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	public const ID_BREADCRUMB = 'breadcrumb';

	/**
	 * Default setting value: Breadcrumb.
	 *
	 * @since 1.2.0
	 *
	 * @var bool
	 */
	public const DEFAULT_BREADCRUMB = ! self::CAN_SHOW;

	/**
	 * Breadcrumb home item.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	public const HOME_ITEM = 'home_item';

	/**
	 * Breadcrumb ancestor items.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	public const ANCESTOR_ITEMS = 'ancestor_items';

	/**
	 * Breadcrumb current item.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	public const CURRENT_ITEM = 'current_item';

	/**
	 * Breadcrumb ancestor count.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	public const ANCESTOR_COUNT = 'ancestor_count';

	/**
	 * Breadcrumb item text.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	public const ITEM_TEXT = 'text';

	/**
	 * Breadcrumb item URL.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	public const ITEM_URL = 'url';

	/**
	 * Indicates breadcrumb can be displayed.
	 *
	 * @since 1.2.0
	 *
	 * @var bool
	 */
	private const CAN_SHOW = true;

	/**
	 * Gets name.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	public static function get_name() {
		return esc_html__( 'Breadcrumbs', 'errigal' );
	}

	/**
	 * Determines whether breadcrumb can be displayed.
	 *
	 * @since 1.2.0
	 *
	 * @return bool
	 */
	public static function can_show() {
		return ! is_front_page() &&
			self::get_theme_mod_bool(
				self::ID_BREADCRUMB,
				self::DEFAULT_BREADCRUMB
			);
	}

	/**
	 * Determines whether to add breadcrumb in customise preview.
	 *
	 * @since 1.2.0
	 *
	 * @return bool
	 */
	public static function should_add_in_customise_preview() {
		return is_customize_preview() && ! is_front_page();
	}

	/**
	 * Gets arguments.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	public static function get_args() {
		$home_item      = self::get_home_item();
		$ancestor_items = self::get_ancestor_items();
		$current_item   = self::get_current_item();

		if (
			! Validator::is_associative_array( $home_item ) ||
			! is_array( $ancestor_items ) ||
			! Validator::is_associative_array( $current_item )
		) {
			return array();
		}

		return array(
			self::HOME_ITEM      => $home_item,
			self::ANCESTOR_ITEMS => $ancestor_items,
			self::ANCESTOR_COUNT => count( $ancestor_items ),
			self::CURRENT_ITEM   => $current_item,
		);
	}

	/**
	 * Gets has ancestor class.
	 *
	 * @since 1.2.0
	 *
	 * @param int $ancestor_count Ancestor count.
	 * @return string|null
	 */
	public static function get_has_ancestor_class( $ancestor_count ) {
		return Validator::is_positive_int( $ancestor_count )
			? 'has-ancestor'
			: null;
	}

	/**
	 * Gets reveal button aria-label.
	 *
	 * @since 1.2.0
	 *
	 * @param int $ancestor_count Ancestor count.
	 * @return string
	 */
	public static function get_reveal_button_aria_label( $ancestor_count ) {
		return esc_attr(
			sprintf(
				/* translators: 1: Breadcrumb count number. */
				_n( 'Reveal %1$s additional list item', 'Reveal %1$s additional list items', $ancestor_count, 'errigal' ),
				$ancestor_count
			)
		);
	}

	/**
	 * Gets reveal button aria-controls.
	 *
	 * @since 1.2.0
	 *
	 * @param int $ancestor_count Ancestor count.
	 * @return string
	 */
	public static function get_reveal_button_aria_controls( $ancestor_count ) {
		$ids = array();

		for ( $i = 0; $i < $ancestor_count; $i++ ) {
			$ids[] = self::get_ancestor_id( $i );
		}

		return esc_attr(
			Utility::join(
				Utility::SEPARATOR_SPACE,
				...$ids
			)
		);
	}

	/**
	 * Renders separator.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public static function render_separator() {
		Icon::render_icon( Icon::NEXT, Icon::SIZE_XS );
	}

	/**
	 * Gets ancestor ID.
	 *
	 * @since 1.2.0
	 *
	 * @param int $index Index of ancestor.
	 * @return string
	 */
	public static function get_ancestor_id( $index ) {
		return is_int( $index )
			? 'errigal-breadcrumb-ancestor-' . ( $index + 1 )
			: '';
	}

	/**
	 * Gets home breadcrumb item.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_home_item() {
		if ( is_front_page() ) {
			return array();
		}

		return self::get_item(
			Site_Info::get_home_link_text(),
			Site_Info::get_home_url()
		);
	}

	/**
	 * Gets ancestor breadcrumb items.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_ancestor_items() {
		if ( is_front_page() ) {
			return array();
		}

		if ( is_archive() ) {
			return self::get_archive_ancestors();
		}

		if ( is_attachment() ) {
			return self::get_attachment_ancestors();
		}

		if ( is_home() ) {
			return self::get_posts_page_ancestors();
		}

		if ( is_page() ) {
			return self::get_page_ancestors();
		}

		if ( is_single() ) {
			return self::get_post_ancestors();
		}

		return array();
	}

	/**
	 * Gets current page breadcrumb item.
	 *
	 * @since 1.2.0
	 *
	 * @return array.
	 */
	private static function get_current_item() {
		if ( is_front_page() ) {
			return array();
		}

		if ( is_404() ) {
			return self::get_404_current_item();
		}

		if ( is_archive() ) {
			return self::get_archive_current_item();
		}

		if ( is_home() ) {
			return self::get_posts_page_current_item();
		}

		if ( is_search() ) {
			return self::get_search_current_item();
		}

		return self::get_default_current_item();
	}

	/**
	 * Gets ancestors for archive.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_archive_ancestors() {
		if ( is_category() ) {
			return self::get_category_archive_ancestors();
		}

		if ( is_day() ) {
			return self::get_day_archive_ancestors();
		}

		if ( is_month() ) {
			return self::get_month_archive_ancestors();
		}

		return array();
	}

	/**
	 * Gets ancestors for category archive.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_category_archive_ancestors() {
		$category = get_queried_object();

		if ( ! Content::is_wp_term( $category ) ) {
			return array();
		}

		$items = array();

		foreach ( array_reverse( get_ancestors( $category->term_id, Query::CATEGORY ) ) as $ancestor_id ) {
			$term = get_category( $ancestor_id );

			if ( ! Content::is_wp_term( $term ) ) {
				continue;
			}

			$items[] = self::get_item(
				$term->name,
				get_category_link( $term )
			);
		}

		return $items;
	}

	/**
	 * Gets ancestors for day archive.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_day_archive_ancestors() {
			$year  = self::get_post_year();
			$month = self::get_post_month();

			return array(
				self::get_item(
					self::get_year_label(),
					get_year_link( $year )
				),
				self::get_item(
					self::get_month_label(),
					get_month_link( $year, $month )
				),
			);
	}

	/**
	 * Gets ancestors for month archive.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_month_archive_ancestors() {
		return array(
			self::get_item(
				self::get_year_label(),
				get_year_link( self::get_post_year() )
			),
		);
	}

	/**
	 * Gets year of current post.
	 *
	 * @since 1.2.0
	 *
	 * @return int
	 */
	private static function get_post_year() {
		return (int) get_the_time( 'Y' );
	}

	/**
	 * Gets month of current post.
	 *
	 * @since 1.2.0
	 *
	 * @return int
	 */
	private static function get_post_month() {
		return (int) get_the_time( 'm' );
	}

	/**
	 * Gets label for year archive.
	 *
	 * @since 1.2.0
	 *
	 * @link https://developer.wordpress.org/reference/functions/get_the_archive_title/
	 *
	 * @return string
	 */
	private static function get_year_label() {
		return esc_html(
			get_the_time(
				esc_html_x( 'Y', 'yearly archives date format', 'errigal' )
			)
		);
	}

	/**
	 * Gets label for month archive.
	 *
	 * @since 1.2.0
	 *
	 * @link https://developer.wordpress.org/reference/functions/get_the_archive_title/
	 *
	 * @return string
	 */
	private static function get_month_label() {
		return esc_html(
			get_the_time(
				esc_html_x( 'F', 'monthly archives date format', 'errigal' )
			)
		);
	}

	/**
	 * Gets ancestors for attachment.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_attachment_ancestors() {
		$parent_id = wp_get_post_parent_id( get_the_ID() );

		return Validator::is_positive_int( $parent_id )
			? array_merge(
				self::get_ancestors( $parent_id ),
				array(
					self::get_item(
						get_the_title( $parent_id ),
						get_permalink( $parent_id )
					),
				),
			)
			: array();
	}

	/**
	 * Gets ancestors for posts page.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_posts_page_ancestors() {
		return self::get_page_ancestors( self::get_posts_page_id() );
	}

	/**
	 * Gets ancestors for page.
	 *
	 * @since 1.2.0
	 *
	 * @param int|null $page_id Optional. Page ID. Default null.
	 * @return array
	 */
	private static function get_page_ancestors( $page_id = null ) {
		$page_id = $page_id ?? get_the_ID();

		return Validator::is_positive_int( $page_id )
			? self::get_ancestors( $page_id )
			: array();
	}

	/**
	 * Gets ancestors for post.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_post_ancestors() {
		$posts_page_id = self::get_posts_page_id();

		return Validator::is_positive_int( $posts_page_id )
			? array(
				self::get_item(
					get_the_title( $posts_page_id ),
					get_permalink( $posts_page_id )
				),
			)
			: array();
	}

	/**
	 * Gets current item for 404.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_404_current_item() {
		return self::get_item( __( 'Page not found', 'errigal' ) );
	}

	/**
	 * Gets current item for archive.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_archive_current_item() {
		return self::get_item(
			wp_strip_all_tags( get_the_archive_title() )
		);
	}

	/**
	 * Gets current item for posts page.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_posts_page_current_item() {
		$posts_page_id = self::get_posts_page_id();

		return Validator::is_positive_int( $posts_page_id )
			? self::get_item(
				wp_strip_all_tags( get_the_title( $posts_page_id ) )
			)
			: self::get_item( __( 'Posts', 'errigal' ) );
	}

	/**
	 * Gets current item for search.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_search_current_item() {
		return self::get_item( __( 'Search results', 'errigal' ) );
	}

	/**
	 * Gets current item for unhandled page types.
	 *
	 * @since 1.2.0
	 *
	 * @return array
	 */
	private static function get_default_current_item() {
		return self::get_item(
			wp_strip_all_tags( get_the_title() )
		);
	}

	/**
	 * Gets breadcrumb items.
	 *
	 * @since 1.2.0
	 *
	 * @param string $text Text.
	 * @param string $url  Optional. URL. Default empty string.
	 * @return array
	 */
	private static function get_item( $text, $url = '' ) {
		if ( ! Validator::is_non_empty_string( $text ) ) {
			return array();
		}

		$item = array( self::ITEM_TEXT => esc_html( $text ) );

		if ( Validator::is_non_empty_string( $url ) ) {
			$item[ self::ITEM_URL ] = esc_url( $url );
		}

		return $item;
	}

	/**
	 * Gets ancestors.
	 *
	 * @since 1.2.0
	 *
	 * @param int $post_id Post ID.
	 * @return array
	 */
	private static function get_ancestors( $post_id ) {
		if ( ! Validator::is_positive_int( $post_id ) ) {
			return array();
		}

		$items = array();

		foreach ( array_reverse( get_post_ancestors( $post_id ) ) as $ancestor_id ) {
			if ( ! Validator::is_positive_int( $ancestor_id ) ) {
				continue;
			}

			$items[] = self::get_item(
				get_the_title( $ancestor_id ),
				get_permalink( $ancestor_id )
			);
		}

		return $items;
	}

	/**
	 * Gets posts page ID.
	 *
	 * @since 1.2.0
	 *
	 * @return int
	 */
	private static function get_posts_page_id() {
		return (int) get_option( 'page_for_posts' );
	}
}
