<?php
/**
 * Recipe schema type
 *
 * Outputs schema.org/Recipe markup for recipe posts.
 *
 * @package BW_Schema
 * @since 3.0.0-babel
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class BW_Schema_Schema_Recipe extends BW_Schema_Schema_Base {

	/**
	 * Schema type
	 *
	 * @var string
	 */
	protected $type = 'Recipe';

	/**
	 * Recipe data
	 *
	 * @var WP_Post|array
	 */
	private $recipe;

	/**
	 * Constructor
	 *
	 * @param WP_Post|array $recipe Recipe post or data array
	 */
	public function __construct( $recipe = null ) {
		if ( is_numeric( $recipe ) ) {
			$this->recipe = get_post( $recipe );
		} else {
			$this->recipe = $recipe;
		}

		parent::__construct();
	}

	/**
	 * Build the recipe schema
	 *
	 * @return void
	 */
	protected function build() {
		if ( ! $this->recipe ) {
			return;
		}

		if ( is_a( $this->recipe, 'WP_Post' ) ) {
			$this->build_from_post();
		} elseif ( is_array( $this->recipe ) ) {
			$this->build_from_array();
		}
	}

	/**
	 * Build from post object
	 *
	 * @return void
	 */
	private function build_from_post() {
		$post = $this->recipe;

		// Recipe name
		$this->set( 'name', get_the_title( $post->ID ) );

		// Description
		if ( $post->post_excerpt ) {
			$this->set( 'description', wp_strip_all_tags( $post->post_excerpt ) );
		}

		// Image
		if ( has_post_thumbnail( $post->ID ) ) {
			$image_url = wp_get_attachment_url( get_post_thumbnail_id( $post->ID ) );
			if ( $image_url ) {
				$this->set_nested( 'image', array(
					'@type' => 'ImageObject',
					'url'   => esc_url_raw( $image_url ),
				));
			}
		}

		// Author (chef)
		$author = get_userdata( $post->post_author );
		if ( $author ) {
			$this->set_nested( 'author', array(
				'@type' => 'Person',
				'name'  => $author->display_name,
			));
		}

		// Prep time (from meta)
		$prep_time = get_post_meta( $post->ID, '_recipe_prep_time', true );
		if ( $prep_time ) {
			$this->set( 'prepTime', $prep_time );
		}

		// Cook time (from meta)
		$cook_time = get_post_meta( $post->ID, '_recipe_cook_time', true );
		if ( $cook_time ) {
			$this->set( 'cookTime', $cook_time );
		}

		// Total time (from meta)
		$total_time = get_post_meta( $post->ID, '_recipe_total_time', true );
		if ( $total_time ) {
			$this->set( 'totalTime', $total_time );
		}

		// Yield (servings)
		$yield = get_post_meta( $post->ID, '_recipe_yield', true );
		if ( $yield ) {
			$this->set( 'recipeYield', $yield );
		}

		// Ingredients (from meta as array)
		$ingredients = get_post_meta( $post->ID, '_recipe_ingredients', true );
		if ( ! empty( $ingredients ) && is_array( $ingredients ) ) {
			$this->set( 'recipeIngredient', $ingredients );
		}

		// Instructions (from meta as array)
		$instructions = get_post_meta( $post->ID, '_recipe_instructions', true );
		if ( ! empty( $instructions ) ) {
			$this->set( 'recipeInstructions', $instructions );
		}

		// Cuisine (from meta)
		$cuisine = get_post_meta( $post->ID, '_recipe_cuisine', true );
		if ( $cuisine ) {
			$this->set( 'recipeCuisine', $cuisine );
		}

		// Category (from meta)
		$category = get_post_meta( $post->ID, '_recipe_category', true );
		if ( $category ) {
			$this->set( 'recipeCategory', $category );
		}

		// Rating (if available)
		$rating = get_post_meta( $post->ID, '_recipe_rating', true );
		if ( $rating ) {
			$this->set_rating( $rating );
		}

		// URL
		$this->set( 'url', get_permalink( $post->ID ) );
	}

	/**
	 * Build from array data
	 *
	 * @return void
	 */
	private function build_from_array() {
		$recipe = $this->recipe;

		// Name
		if ( ! empty( $recipe['name'] ) ) {
			$this->set( 'name', $recipe['name'] );
		}

		// Description
		if ( ! empty( $recipe['description'] ) ) {
			$this->set( 'description', $recipe['description'] );
		}

		// Times
		if ( ! empty( $recipe['prep_time'] ) ) {
			$this->set( 'prepTime', $recipe['prep_time'] );
		}

		if ( ! empty( $recipe['cook_time'] ) ) {
			$this->set( 'cookTime', $recipe['cook_time'] );
		}

		if ( ! empty( $recipe['total_time'] ) ) {
			$this->set( 'totalTime', $recipe['total_time'] );
		}

		// Yield
		if ( ! empty( $recipe['yield'] ) ) {
			$this->set( 'recipeYield', $recipe['yield'] );
		}

		// Ingredients
		if ( ! empty( $recipe['ingredients'] ) && is_array( $recipe['ingredients'] ) ) {
			$this->set( 'recipeIngredient', $recipe['ingredients'] );
		}

		// Instructions
		if ( ! empty( $recipe['instructions'] ) ) {
			$this->set( 'recipeInstructions', $recipe['instructions'] );
		}

		// Cuisine & Category
		if ( ! empty( $recipe['cuisine'] ) ) {
			$this->set( 'recipeCuisine', $recipe['cuisine'] );
		}

		if ( ! empty( $recipe['category'] ) ) {
			$this->set( 'recipeCategory', $recipe['category'] );
		}

		// URL
		if ( ! empty( $recipe['url'] ) ) {
			$this->set( 'url', $this->sanitize_url( $recipe['url'] ) );
		}
	}

	/**
	 * Set rating
	 *
	 * @param array|float $rating Rating data or score
	 * @return void
	 */
	private function set_rating( $rating ) {
		if ( is_numeric( $rating ) ) {
			$rating_obj = array(
				'@type'       => 'AggregateRating',
				'ratingValue' => floatval( $rating ),
			);
		} elseif ( is_array( $rating ) ) {
			$rating_obj = array(
				'@type' => 'AggregateRating',
			);

			if ( ! empty( $rating['value'] ) ) {
				$rating_obj['ratingValue'] = floatval( $rating['value'] );
			}

			if ( ! empty( $rating['count'] ) ) {
				$rating_obj['ratingCount'] = absint( $rating['count'] );
			}

			if ( ! empty( $rating['best'] ) ) {
				$rating_obj['bestRating'] = floatval( $rating['best'] );
			}

			if ( ! empty( $rating['worst'] ) ) {
				$rating_obj['worstRating'] = floatval( $rating['worst'] );
			}
		} else {
			return;
		}

		$this->set_nested( 'aggregateRating', $rating_obj );
	}

	/**
	 * Validate recipe schema
	 *
	 * @return array Validation errors
	 */
	public function validate() {
		$errors = parent::validate();

		if ( ! $this->has_required( array( 'name' ) ) ) {
			$errors[] = 'Recipe schema requires a name';
		}

		// Ingredients recommended
		if ( ! isset( $this->schema['recipeIngredient'] ) ) {
			// Recommended but not required
		}

		return $errors;
	}
}
