<?php

namespace CelerSearch\Utilities;

use WP_Error;

/**
 * Part of the code in this class was adopted from "WP Search with Algolia" plugin
 */
class PostUtilities {

	/**
	 * Retrieve term parents with separator.
	 *
	 * @param  int  $id  Term ID.
	 * @param  string  $taxonomy  The taxonomy.
	 * @param  string  $separator  Optional, default is '/'. How to separate terms.
	 * @param  bool  $nicename  Optional, default is false. Whether to use nice name for display.
	 * @param  array  $visited  Optional. Already linked to terms to prevent duplicates.
	 *
	 * @return string|WP_Error A list of terms parents on success, WP_Error on failure.
	 * @author  WebDevStudios <contact@webdevstudios.com>
	 * @since   1.0.0
	 *
	 */
	public static function get_term_parents( int $id, string $taxonomy, string $separator = '/', bool $nicename = false, array $visited = array() ) : string|WP_Error {
		$chain  = '';
		$parent = get_term( $id, $taxonomy );
		if ( is_wp_error( $parent ) ) {
			return $parent;
		}

		if ( $nicename ) {
			$name = $parent->slug;
		} else {
			$name = html_entity_decode( $parent->name );
		}

		if ( $parent->parent && ( $parent->parent !== $parent->term_id ) && ! in_array( $parent->parent, $visited, true ) ) {
			$visited[] = $parent->parent;
			$chain     .= self::get_term_parents( $parent->parent, $taxonomy, $separator, $nicename, $visited );
		}

		$chain .= $name . $separator;

		return $chain;
	}

	/**
	 * Get taxonomy tree.
	 *
	 * This is useful when building hierarchical menus.
	 *
	 * Returns an array like:
	 * array(
	 *    'lvl0' => ['Sales', 'Marketing'],
	 *    'lvl1' => ['Sales > Strategies', 'Marketing > Tips & Tricks']
	 *    ...
	 * );.
	 *
	 * @link    https://community.algolia.com/instantsearch.js/documentation/#hierarchicalmenu
	 *
	 * @author  WebDevStudios <contact@webdevstudios.com>
	 * @since   1.0.0
	 *
	 * @param  array  $terms  The terms.
	 * @param  string  $taxonomy  The taxonomy.
	 * @param  string  $separator  The separator.
	 *
	 * @return array
	 */
	public static function get_taxonomy_tree( array $terms, string $taxonomy, string $separator = ' > ' ) : array {
		$term_ids = wp_list_pluck( $terms, 'term_id' );

		$parents = array();
		foreach ( $term_ids as $term_id ) {
			$path      = self::get_term_parents( $term_id, $taxonomy, $separator );
			if( is_wp_error( $path ) ) {
				continue;
			}
			$parents[] = rtrim( $path, $separator );
		}

		$terms = array();
		foreach ( $parents as $parent ) {
			$levels = explode( $separator, $parent );

			$previous_lvl = '';
			foreach ( $levels as $index => $level ) {
				$terms[ 'lvl' . $index ][] = $previous_lvl . $level;
				$previous_lvl              .= $level . $separator;

				// Make sure we have not duplicate.
				// The call to `array_values` ensures that we do not end up with an object in JSON.
				$terms[ 'lvl' . $index ] = array_values( array_unique( $terms[ 'lvl' . $index ] ) );
			}
		}

		return $terms;
	}

	/**
	 * Get post images.
	 *
	 * @param  int  $post_id  The post ID.
	 *
	 * @return array
	 *
	 */
	public static function get_post_images( $post_id ) : array {
		$images = array();

		if ( get_post_type( $post_id ) === 'attachment' ) {
			$post_thumbnail_id = (int) $post_id;
		} else {
			$post_thumbnail_id = get_post_thumbnail_id( (int) $post_id );
		}

		if ( $post_thumbnail_id ) {
			$sizes = (array) apply_filters( 'celersearch_post_images_sizes', array_merge(['full'], get_intermediate_image_sizes()) );
			foreach ( $sizes as $size ) {
				$info = wp_get_attachment_image_src( $post_thumbnail_id, $size );
				if ( ! $info ) {
					continue;
				}

				$images[$size] =  $info[0];
			}
		}

		/**
		 * Filters the final array of images and image data.
		 *
		 * Parameter will be an array keyed by image size, and has
		 * URL, width and height details for each image.
		 *
		 * @param  array  $images  Array of images data.
		 * @param  int  $post_id  Current post ID being indexed.
		 *
		 * @return array $images  Final array of images data.
		 * @since 2.8.0 Added `$post_id` as second parameter
		 *
		 * @since 1.0.0
		 */
		return (array) apply_filters( 'celersearch_get_post_images', $images, $post_id );
	}

	/**
	 * Prepare content.
	 *
	 * @param  string  $content  The content to prepare.
	 *
	 * @return string
	 * @author  WebDevStudios <contact@webdevstudios.com>
	 * @since   1.0.0
	 *
	 */
	public static function prepare_content( string $content ) : string {
		$content = self::remove_content_noise( $content );

		return wp_strip_all_tags( $content );
	}

	/**
	 * Remove content noise.
	 *
	 * @param  string  $content  The content to clean.
	 *
	 * @return string
	 * @author  WebDevStudios (Inspiration)
	 */
	public static function remove_content_noise( string $content ) : string {
		$noise_patterns = array(
			// strip out comments.
			"'<!--(.*?)-->'is",
			// strip out cdata.
			"'<!\[CDATA\[(.*?)\]\]>'is",
			// Per sourceforge http://sourceforge.net/tracker/?func=detail&aid=2949097&group_id=218559&atid=1044037
			// Script tags removal now preceeds style tag removal.
			// strip out <script> tags.
			"'<\s*script[^>]*[^/]>(.*?)<\s*/\s*script\s*>'is",
			"'<\s*script\s*>(.*?)<\s*/\s*script\s*>'is",
			// strip out <style> tags.
			"'<\s*style[^>]*[^/]>(.*?)<\s*/\s*style\s*>'is",
			"'<\s*style\s*>(.*?)<\s*/\s*style\s*>'is",
			// strip out preformatted tags.
			"'<\s*(?:code)[^>]*>(.*?)<\s*/\s*(?:code)\s*>'is",
			// strip out <pre> tags.
			"'<\s*pre[^>]*[^/]>(.*?)<\s*/\s*pre\s*>'is",
			"'<\s*pre\s*>(.*?)<\s*/\s*pre\s*>'is",
		);

		// If there is ET builder (Divi), remove shortcodes.
		if ( function_exists( 'et_pb_is_pagebuilder_used' ) ) {
			$noise_patterns[] = '/\[\/?et_pb.*?\]/';
		}

		$noise_patterns = (array) apply_filters( 'celersearch_strip_patterns', $noise_patterns );

		foreach ( $noise_patterns as $pattern ) {
			$content = preg_replace( $pattern, '', $content );
		}

		$content = str_replace( '&nbsp;', ' ', $content );

		// Prevent table content from being concatenated.
		$content = str_replace( [ '</td>', '</th>' ], ' ', $content );

		return html_entity_decode( $content );
	}

	/**
	 * Explode content.
	 *
	 * @param  string  $content  The content to explode.
	 *
	 * @return array
	 * @author  WebDevStudios <contact@webdevstudios.com>
	 * @since   1.0.0
	 *
	 */
	public static function explode_content( string $content ) : array {
		$max_size = 2000;
		if ( defined( 'CELERSEARCH_CONTENT_MAX_SIZE' ) ) {
			$max_size = (int) CELERSEARCH_CONTENT_MAX_SIZE;
		}

		$parts  = array();
		$prefix = '';
		while ( true ) {
			$content = trim( (string) $content );
			if ( strlen( $content ) <= $max_size ) {
				$parts[] = $prefix . $content;

				break;
			}

			$offset          = - ( strlen( $content ) - $max_size );
			$cut_at_position = strrpos( $content, ' ', $offset );

			if ( false === $cut_at_position ) {
				$cut_at_position = $max_size;
			}
			$parts[] = $prefix . substr( $content, 0, $cut_at_position );
			$content = substr( $content, $cut_at_position );

			$prefix = '… ';
		}

		return $parts;
	}

	/**
	 * Get post object ID.
	 *
	 * @param int $post_id
	 * @param int $record_index
	 *
	 * @return string
	 */
	public static function get_post_object_id( int $post_id, int $record_index ) : string {
		return apply_filters(
			'celersearch_get_post_object_id',
			$post_id . '-' . $record_index,
			$post_id,
			$record_index
		);
	}

}