<?php
/**
 * Place schema type
 *
 * Outputs schema.org/Place markup with geographic coordinates.
 * Used for specific locations with latitude/longitude.
 *
 * @package BW_Schema
 * @since 3.0.0-babel
 */

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

class BW_Schema_Schema_Place extends BW_Schema_Schema_Base {

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

	/**
	 * Place data
	 *
	 * @var array
	 */
	private $place;

	/**
	 * Constructor
	 *
	 * @param array $place Place data with coordinates
	 */
	public function __construct( $place = null ) {
		$this->place = $place ?? array();
		parent::__construct();
	}

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

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

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

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

		// Image
		if ( ! empty( $this->place['image'] ) ) {
			$this->set_image( $this->place['image'] );
		}

		// Geo coordinates
		if ( ! empty( $this->place['latitude'] ) && ! empty( $this->place['longitude'] ) ) {
			$this->set_geo_coordinates(
				$this->place['latitude'],
				$this->place['longitude']
			);
		}

		// Address
		if ( ! empty( $this->place['street'] ) || ! empty( $this->place['city'] ) ) {
			$this->set_address();
		}

		// Telephone
		if ( ! empty( $this->place['telephone'] ) ) {
			$this->set( 'telephone', $this->place['telephone'] );
		}

		// Email
		if ( ! empty( $this->place['email'] ) ) {
			$this->set( 'email', $this->place['email'] );
		}

		// Opening hours
		if ( ! empty( $this->place['opening_hours'] ) ) {
			$this->set_opening_hours( $this->place['opening_hours'] );
		}
	}

	/**
	 * Set geo coordinates
	 *
	 * @param float $latitude Latitude coordinate
	 * @param float $longitude Longitude coordinate
	 * @return void
	 */
	private function set_geo_coordinates( $latitude, $longitude ) {
		$geo = array(
			'@type'     => 'GeoCoordinates',
			'latitude'  => floatval( $latitude ),
			'longitude' => floatval( $longitude ),
		);

;

		$this->set_nested( 'geo', $geo );
	}

	/**
	 * Set address from place data
	 *
	 * @return void
	 */
	private function set_address() {
		$address = array(
			'@type' => 'PostalAddress',
		);

		if ( ! empty( $this->place['street'] ) ) {
			$address['streetAddress'] = $this->place['street'];
		}

		if ( ! empty( $this->place['city'] ) ) {
			$address['addressLocality'] = $this->place['city'];
		}

		if ( ! empty( $this->place['state'] ) ) {
			$address['addressRegion'] = $this->place['state'];
		}

		if ( ! empty( $this->place['postal_code'] ) ) {
			$address['postalCode'] = $this->place['postal_code'];
		}

		if ( ! empty( $this->place['country'] ) ) {
			$address['addressCountry'] = $this->place['country'];
		}

		if ( count( $address ) > 1 ) {
			$this->set_nested( 'address', $address );
		}
	}

	/**
	 * Set image
	 *
	 * @param string|int $image Image URL or attachment ID
	 * @return void
	 */
	private function set_image( $image ) {
		if ( is_numeric( $image ) ) {
			$url = wp_get_attachment_url( $image );
		} else {
			$url = $image;
		}

		$url = $this->sanitize_url( $url );
		if ( $url ) {
			$this->set_nested( 'image', array(
				'@type' => 'ImageObject',
				'url'   => $url,
			));
		}
	}

	/**
	 * Set opening hours
	 *
	 * @param array $hours Opening hours data
	 * @return void
	 */
	private function set_opening_hours( $hours ) {
		if ( empty( $hours ) || ! is_array( $hours ) ) {
			return;
		}

		$day_map = array(
			'monday'    => 'Monday',
			'tuesday'   => 'Tuesday',
			'wednesday' => 'Wednesday',
			'thursday'  => 'Thursday',
			'friday'    => 'Friday',
			'saturday'  => 'Saturday',
			'sunday'    => 'Sunday',
		);

		$opening_hours = array();

		foreach ( $hours as $day => $time ) {
			if ( empty( $time ) || 'closed' === strtolower( $time ) ) {
				continue;
			}

			$day_label = $day_map[ strtolower( $day ) ] ?? '';
			if ( ! $day_label ) {
				continue;
			}

			if ( strpos( $time, '-' ) ) {
				list( $open, $close ) = explode( '-', $time );

				$opening_hours[] = array(
					'@type'         => 'OpeningHoursSpecification',
					'dayOfWeek'     => $day_label,
					'opens'         => trim( $open ),
					'closes'        => trim( $close ),
				);
			}
		}

		if ( ! empty( $opening_hours ) ) {
			$this->set_nested( 'openingHoursSpecification', $opening_hours );
		}
	}

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

		// Name is required
		if ( ! $this->has_required( array( 'name' ) ) ) {
			$errors[] = 'Place schema requires a name';
		}

		// Geo coordinates recommended but not required
		if ( ! isset( $this->schema['geo'] ) ) {
			// Not an error, but recommended for mapping
		}

		return $errors;
	}
}
