<?php

namespace wpbuddy\rich_snippets\pro;

use wpbuddy\rich_snippets\Rich_Snippet;
use wpbuddy\rich_snippets\Snippets_Model;

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


/**
 * Class Values.
 *
 * Prepares and fills registered values.
 *
 * @package wpbuddy\rich_snippets\pro
 *
 * @since   2.19.0
 */
class Values_Model extends \wpbuddy\rich_snippets\Values_Model {

	/**
	 * Values_Model constructor.
	 * @since 2.20.0
	 */
	public function __construct() {
		add_filter( 'wpbuddy/rich_snippets/fields/link_to_subselect/values', [
			'wpbuddy\rich_snippets\pro\Fields_Model',
			'more_references'
		] );

		parent::__construct();
	}

	/**
	 * Returns all possible methods.
	 *
	 * @return array
	 * @since 2.19.5
	 */
	protected function get_methods() {
		$fields  = new Fields_Model();
		$methods = $fields->get_internal_values_methods();

		return array_merge( $methods, $fields->get_reference_values_ids() );
	}

	/**
	 * Fetches a call to function that doesn't exist.
	 *
	 * @param string $name
	 * @param array $args
	 *
	 * @return mixed
	 * @since 2.19.0
	 */
	public function __call( $name, $args ) {

		if ( false !== stripos( $name, 'global_snippet_' ) ) {
			$args[2]['snippet_uid'] = str_replace( 'global_snippet_', '', $name );

			return $this->global_snippet( $args[0], $args[1], $args[2], $args[3] );
		}

		if ( 0 === stripos( $name, 'taxonomy_' ) ) {
			$tax = str_replace( 'taxonomy_', '', $name );

			return $this->taxonomy_list( $tax, $args[0], $args[1], $args[2], $args[3] );
		}

		return parent::__call( $name, $args );
	}


	/**
	 * Returns a comma separated list of taxonomies.
	 *
	 * @param string $taxonomy
	 * @param string $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.10.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	private function taxonomy_list( $taxonomy, $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): string {
		$terms = get_terms( [
			'taxonomy'   => $taxonomy,
			'object_ids' => $meta_info['current_post_id']
		] );

		if ( ! is_array( $terms ) ) {
			return '';
		}

		return implode( ', ', wp_list_pluck( $terms, 'name' ) );
	}


	/**
	 * Fetches the data from a meta table.
	 *
	 * @param mixed $val
	 * @param int $object_id
	 * @param string $meta_type
	 *
	 * @return string|array
	 *
	 * @since 2.13.2
	 */
	private function meta( $val, $object_id, $meta_type = 'post' ) {
		if ( ! is_scalar( $val ) ) {
			return '';
		}

		if ( empty( $val ) ) {
			return '';
		}

		if ( false !== stripos( $val, '-&gt;' ) ) {
			$val = htmlspecialchars_decode( $val );
		}

		# check if array or object should be accessed
		if ( false !== stripos( $val, '->' ) ) {

			$meta_key = strstr( $val, '->', true );

			$meta_value = get_metadata( $meta_type, $object_id, $meta_key, true );

			$f = Helper_Model::instance()->get_deep( $meta_value, str_replace( $meta_key, '', $val ) );

			if ( ! is_scalar( $f ) ) {
				return '';
			}

			return (string) $f;
		}

		$meta_value = get_metadata( $meta_type, $object_id, $val );

		if ( is_array( $meta_value ) ) {
			if ( count( $meta_value ) <= 1 ) {
				$meta_value = array_shift( $meta_value );
			} else {
				return array_values( $meta_value );
			}

		}

		if ( is_scalar( $meta_value ) ) {
			return (string) $meta_value;
		}

		return '';
	}

	/**
	 * Returns a sub element to be included into JSON-LD.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return null|\wpbuddy\rich_snippets\Rich_Snippet
	 * @since 2.0.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function global_snippet( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return $val;
		}

		$post_id = Helper_Model::instance()->get_post_id_by_snippet_uid( $meta_info['snippet_uid'] );

		$rich_snippets = Snippets_Model::get_snippets( $post_id );

		if ( count( $rich_snippets ) <= 0 ) {
			return null;
		}

		if ( ! isset( $rich_snippets[ $meta_info['snippet_uid'] ] ) ) {
			return null;
		}

		$child_snippet = $rich_snippets[ $meta_info['snippet_uid'] ];

		$child_snippet->prepare_for_output( array(
			'current_post_id' => $meta_info['current_post_id'],
			'snippet_post_id' => $post_id,
		) );

		return $child_snippet;

	}

	/**
	 * Returns the value of a meta field.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.0.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function textfield_meta( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return (string) $val;
		}

		return $this->meta( $val, $meta_info['current_post_id'] );
	}

	/**
	 * Returns the value of a user meta field.
	 *
	 * @param $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.30.0
	 */
	public function textfield_usermeta( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return (string) $val;
		}

		if ( $meta_info['queried_object'] instanceof \WP_Post ) {
			$user = get_user_by( 'ID', $meta_info['queried_object']->post_author );
		} else if ( $meta_info['queried_object'] instanceof \WP_User ) {
			$user = $meta_info['queried_object'];
		}

		if ( ! $user instanceof \WP_User ) {
			return '';
		}

		return $this->meta( $val, $user->ID, 'user' );
	}

	/**
	 * Returns a specific value from the user database table.
	 *
	 * @param $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.30.0
	 */
	public function textfield_user( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return (string) $val;
		}

		if ( ! $meta_info['queried_object'] instanceof \WP_User ) {
			return (string) $val;
		}

		$user      = $meta_info['queried_object'];
		$prop_name = (string) $val;

		# filter some fields
		if ( 'user_pass' === $prop_name || 'pass' === $prop_name || false !== stripos( 'activation_key', $prop_name ) ) {
			return '';
		}

		if ( isset( $user->{$prop_name} ) ) {
			return $user->{$prop_name};
		}

		if ( isset( $user->{'user_' . $prop_name} ) ) {
			return $user->{'user_' . $prop_name};
		}

		return '';
	}

	/**
	 * Returns the value of a term meta field.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 *
	 * @since 2.13.2
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function textfield_termmeta( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return $val;
		}

		$term = get_queried_object();

		if ( ! $term instanceof \WP_Term ) {
			return '';
		}

		return $this->meta( $val, $term->term_id, 'term' );
	}

	/**
	 * Returns an option value for a string.
	 *
	 * @param string $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.10.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function textfield_option_string( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): string {

		if ( $overwritten ) {
			return (string) $val;
		}

		return (string) $this->get_option( $val );
	}


	/**
	 * Returns an option value and formats it for a date string. @see https://schema.org/Date
	 *
	 * @param string $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @since 2.10.0
	 * @since 2.14.25 New parameter $overwritten
	 *
	 * @return string
	 */
	public function textfield_option_date( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): string {

		if ( $overwritten ) {
			return (string) $val;
		}

		$option_value = $this->get_option( $val );

		$time = strtotime( $option_value, current_time( 'timestamp' ) );

		if ( false === $time ) {
			return '';
		}

		return date_i18n( 'Y-m-d', $time );
	}


	/**
	 * Returns an option value for a string.
	 *
	 * @param string $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return int
	 * @since 2.10.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function textfield_option_integer( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): int {
		if ( $overwritten ) {
			return (int) $val;
		}

		return intval( $this->get_option( $val ) );
	}


	/**
	 * Returns an option value and formats it for a time string. @see https://schema.org/Time
	 *
	 * @param string $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @since 2.10.0
	 * @since 2.14.25 New parameter $overwritten
	 *
	 * @return string
	 */
	public function textfield_option_time( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): string {

		if ( $overwritten ) {
			return (string) $val;
		}

		$option_value = $this->get_option( $val );

		$time = strtotime( $option_value, current_time( 'timestamp' ) );

		if ( false === $time ) {
			return '';
		}

		return date_i18n( 'G:i:sP', $time );
	}


	/**
	 * Returns an option value and formats it for a URL. @see https://schema.org/Date
	 *
	 * @param string $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @since 2.10.0
	 * @since 2.14.25 New parameter $overwritten
	 *
	 * @return string
	 */
	public function textfield_option_url( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): string {

		if ( $overwritten ) {
			return (string) $val;
		}

		$option_value = $this->get_option( $val );

		return esc_url( $option_value );
	}


	/**
	 * Returns user data.
	 *
	 * @param $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.19.0
	 */
	public function textfield_userdata( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): string {
		if ( $overwritten ) {
			return (string) $val;
		}

		if ( $meta_info['queried_object'] && $meta_info['queried_object'] instanceof \WP_Post ) {
			$user = get_user_by( 'ID', $meta_info['queried_object']->post_author );
		}

		if ( $meta_info['queried_object'] && $meta_info['queried_object'] instanceof \WP_User ) {
			$user = $meta_info['queried_object'];
		}

		if ( ! isset( $user ) ) {
			return '';
		}

		return (string) Helper_Model::instance()->get_deep( $user, $val );
	}


	/**
	 * Returns a sequential number.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.8.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function textfield_sequential_number( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return $val;
		}

		static $sequences;

		if ( ! isset( $sequences ) ) {
			$sequences = [];
		}

		if ( ! is_scalar( $val ) ) {
			return '';
		}

		if ( empty( $val ) ) {
			$val = 'global';
		}

		if ( ! isset( $sequences[ $val ] ) ) {

			# Check if we have a starting number
			$val_arr = explode( ':', $val );
			if ( isset( $val_arr[1] ) ) {
				$sequences[ $val ] = absint( $val_arr[1] );
			} else {
				$sequences[ $val ] = 0;
			}
		}

		$sequences[ $val ] = intval( $sequences[ $val ] ) + 1;

		return $sequences[ $val ];
	}


	/**
	 * Returns the term title.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.8.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function term_title( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return $val;
		}

		$in_the_loop = $meta_info['in_the_loop'] ?? false;

		if ( ( is_tax() || is_archive() ) && ! $in_the_loop ) {
			$term = get_queried_object();
			if ( ! $term instanceof \WP_Term ) {
				return '';
			} else {
				return esc_html( $term->name );
			}
		}

		if ( ! isset( $meta_info['object'] ) ) {
			return '';
		}

		$term = $meta_info['object'];

		if ( ! $term instanceof \WP_Term ) {
			return '';
		}


		return $term->name;
	}

	/**
	 * Returns the current post content.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.0.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function current_post_content( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): string {

		if ( $overwritten ) {
			return (string) $val;
		}

		$post = get_post( $meta_info['current_post_id'] );

		if ( ! $post instanceof \WP_Post ) {
			return '';
		}

		ob_start();
		$content = apply_filters( 'the_content', $post->post_content );
		ob_end_clean();

		return (string) esc_attr( strip_tags( $content ) );
	}

	/**
	 * Returns description of a term.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.13.2
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function term_description( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return (string) $val;
		}

		$object = get_queried_object();

		return esc_html( strip_tags( term_description( $object ) ) );
	}


	/**
	 * Returns the search URL with {search_url_search_term} parameter.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.8.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function search_url_search_term( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {
		return add_query_arg( [ 's' => '{search_term_string}' ], $overwritten ? $val : home_url() );
	}

	/**
	 * Returns the search URL.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return \string
	 * @since 2.8.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function search_url( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return $val;
		}

		return get_search_link();
	}

	/**
	 * Returns the term URL.
	 *
	 * @param                                     $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string
	 * @since 2.8.0
	 * @since 2.14.25 New parameter $overwritten
	 */
	public function term_url( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ) {

		if ( $overwritten ) {
			return $val;
		}

		$in_the_loop = $meta_info['in_the_loop'] ?? false;

		if ( ( is_tax() || is_archive() ) && ! $in_the_loop ) {
			$term = get_queried_object();
			if ( ! $term instanceof \WP_Term ) {
				return '';
			} else {
				return esc_html( get_term_link( $term ) );
			}
		}

		if ( ! isset( $meta_info['object'] ) ) {
			return '';
		}

		$term = $meta_info['object'];

		if ( ! $term instanceof \WP_Term ) {
			return '';
		}

		$link = get_term_link( $term->term_id, $term->taxonomy );

		if ( is_wp_error( $link ) ) {
			return '';
		}

		return $link;
	}


	/**
	 * Fetches an option from the database.
	 *
	 * @param string $option_name
	 *
	 * @return mixed
	 * @since 2.10.0
	 */
	private function get_option( $option_name ) {

		if ( false !== stripos( $option_name, '->' ) ) {

			$real_option_name = strstr( $option_name, '->', true );

			$option_value = get_option( $real_option_name );

			$f = Helper_Model::instance()->get_deep( $option_value, str_replace( $real_option_name, '', $option_name ) );

			if ( ! is_scalar( $f ) ) {
				return '';
			}

			return (string) $f;
		}

		$option_value = get_option( $option_name );

		if ( ! is_scalar( $option_value ) ) {
			return '';
		}

		return $option_value;
	}


	/**
	 * Returns a value from a block.
	 *
	 * @param string $val
	 * @param Rich_Snippet $rich_snippet
	 * @param array $meta_info
	 * @param bool $overwritten
	 *
	 * @return string|array
	 * @since 2.26.0
	 */
	public function textfield_block_content( $val, Rich_Snippet $rich_snippet, array $meta_info, bool $overwritten ): string {

		$blocks = parse_blocks( $meta_info['queried_object']->post_content );

		$helper  = Helper_Model::instance();
		$content = '';

		foreach ( $blocks as $block ) {
			if ( ! isset( $block['attrs'] ) ) {
				continue;
			}
			if ( ! isset( $block['attrs']['className'] ) ) {
				continue;
			}

			if ( false !== strpos( $block['attrs']['className'], $val ) ) {
				$content .= render_block( $block );
			}

		}

		return $helper->filter_html( $content );
	}

}