<?php
/**
 * Tour post type, meta schemas, and sanitizers.
 *
 * @package Vist360
 */

namespace Vist360\Domain;

defined( 'ABSPATH' ) || exit;

/**
 * Registers the vist360_tour post type and its structured meta.
 */
class TourPostType {

	const POST_TYPE = 'vist360_tour';

	/**
	 * Scene types supported by the viewer.
	 *
	 * @var string[]
	 */
	const SCENE_TYPES = array( 'sphere', 'cube', 'flat', 'little_planet', 'cylinder' );

	/**
	 * Hotspot types supported by the viewer.
	 *
	 * @var string[]
	 */
	const HOTSPOT_TYPES = array( 'info', 'navigation', 'link', 'media' );

	/**
	 * Hook registrations.
	 */
	public function register_hooks() {
		add_action( 'init', array( $this, 'register_post_type' ) );
		add_action( 'init', array( $this, 'register_meta' ) );
	}

	/**
	 * Maximum scenes allowed per tour.
	 *
	 * @return int
	 */
	public static function max_scenes() {
		/**
		 * Filters the maximum number of scenes allowed per tour.
		 *
		 * @since 1.0.0
		 *
		 * @param int $max_scenes Maximum scene count. Default 7.
		 */
		return (int) apply_filters( 'vist360_max_scenes', 7 );
	}

	/**
	 * Register the tour post type.
	 */
	public function register_post_type() {
		register_post_type(
			self::POST_TYPE,
			array(
				'labels'       => array(
					'name'          => __( 'Tours', 'vistour-virtual-tour-builder' ),
					'singular_name' => __( 'Tour', 'vistour-virtual-tour-builder' ),
					'add_new_item'  => __( 'Add New Tour', 'vistour-virtual-tour-builder' ),
					'edit_item'     => __( 'Edit Tour', 'vistour-virtual-tour-builder' ),
					'search_items'  => __( 'Search Tours', 'vistour-virtual-tour-builder' ),
					'not_found'     => __( 'No tours found.', 'vistour-virtual-tour-builder' ),
				),
				'public'       => true,
				'show_ui'      => false,
				'show_in_menu' => false,
				'show_in_rest' => true,
				'rest_base'    => 'vist360-tours',
				'supports'     => array( 'title', 'excerpt', 'author', 'thumbnail', 'custom-fields' ),
				'has_archive'  => false,
				'rewrite'      => array( 'slug' => 'panorama-tour' ),
				'map_meta_cap' => true,
			)
		);
	}

	/**
	 * Register tour meta exposed to the REST API.
	 */
	public function register_meta() {
		register_post_meta(
			self::POST_TYPE,
			'_vist360_scenes',
			array(
				'single'            => true,
				'type'              => 'array',
				'default'           => array(),
				'show_in_rest'      => array(
					'schema' => array(
						'type'  => 'array',
						'items' => self::scene_schema(),
					),
				),
				'sanitize_callback' => array( $this, 'sanitize_scenes' ),
				'auth_callback'     => function ( $allowed, $meta_key, $post_id ) {
					return current_user_can( 'edit_post', $post_id );
				},
			)
		);

		register_post_meta(
			self::POST_TYPE,
			'_vist360_viewer',
			array(
				'single'            => true,
				'type'              => 'object',
				'default'           => array(),
				'show_in_rest'      => array(
					'schema' => self::viewer_schema(),
				),
				'sanitize_callback' => array( $this, 'sanitize_viewer' ),
				'auth_callback'     => function ( $allowed, $meta_key, $post_id ) {
					return current_user_can( 'edit_post', $post_id );
				},
			)
		);

		register_post_meta(
			self::POST_TYPE,
			'_vist360_views',
			array(
				'single'        => true,
				'type'          => 'integer',
				'default'       => 0,
				'show_in_rest'  => true,
				'auth_callback' => '__return_false',
			)
		);
	}

	/**
	 * JSON schema for a single scene.
	 *
	 * @return array
	 */
	private static function scene_schema() {
		return array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'title'    => array( 'type' => 'string' ),
				'type'     => array(
					'type' => 'string',
					'enum' => self::SCENE_TYPES,
				),
				'image_id' => array( 'type' => 'integer' ),
				'hotspots' => array(
					'type'  => 'array',
					'items' => self::hotspot_schema(),
				),
			),
		);
	}

	/**
	 * JSON schema for a single hotspot.
	 *
	 * @return array
	 */
	private static function hotspot_schema() {
		return array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'type'         => array(
					'type' => 'string',
					'enum' => self::HOTSPOT_TYPES,
				),
				'title'        => array( 'type' => 'string' ),
				'content'      => array( 'type' => 'string' ),
				'yaw'          => array( 'type' => 'number' ),
				'pitch'        => array( 'type' => 'number' ),
				'target_scene' => array( 'type' => 'integer' ),
				'url'          => array( 'type' => 'string' ),
				'media_id'     => array( 'type' => 'integer' ),
				'icon'         => array( 'type' => 'string' ),
			),
		);
	}

	/**
	 * JSON schema for viewer settings.
	 *
	 * @return array
	 */
	private static function viewer_schema() {
		return array(
			'type'                 => 'object',
			'additionalProperties' => false,
			'properties'           => array(
				'autorotate'    => array( 'type' => 'boolean' ),
				'show_controls' => array( 'type' => 'boolean' ),
				'show_share'    => array( 'type' => 'boolean' ),
				'gyroscope'     => array( 'type' => 'boolean' ),
				'start_scene'   => array( 'type' => 'integer' ),
				'height'        => array( 'type' => 'integer' ),
			),
		);
	}

	/**
	 * Sanitize the scenes meta value.
	 *
	 * @param mixed $scenes Raw meta value.
	 * @return array Sanitized scenes.
	 */
	public function sanitize_scenes( $scenes ) {
		if ( ! is_array( $scenes ) ) {
			return array();
		}

		$scenes = array_slice( array_values( $scenes ), 0, self::max_scenes() );
		$clean  = array();

		foreach ( $scenes as $scene ) {
			if ( ! is_array( $scene ) ) {
				continue;
			}

			$clean[] = array(
				'title'    => sanitize_text_field( $scene['title'] ?? '' ),
				'type'     => in_array( $scene['type'] ?? '', self::SCENE_TYPES, true ) ? $scene['type'] : 'sphere',
				'image_id' => absint( $scene['image_id'] ?? 0 ),
				'hotspots' => $this->sanitize_hotspots( $scene['hotspots'] ?? array() ),
			);
		}

		return $clean;
	}

	/**
	 * Sanitize a list of hotspots.
	 *
	 * @param mixed $hotspots Raw hotspots.
	 * @return array Sanitized hotspots.
	 */
	private function sanitize_hotspots( $hotspots ) {
		if ( ! is_array( $hotspots ) ) {
			return array();
		}

		$clean = array();

		foreach ( array_values( $hotspots ) as $hotspot ) {
			if ( ! is_array( $hotspot ) ) {
				continue;
			}

			$clean[] = array(
				'type'         => in_array( $hotspot['type'] ?? '', self::HOTSPOT_TYPES, true ) ? $hotspot['type'] : 'info',
				'title'        => sanitize_text_field( $hotspot['title'] ?? '' ),
				'content'      => wp_kses_post( $hotspot['content'] ?? '' ),
				'yaw'          => (float) ( $hotspot['yaw'] ?? 0 ),
				'pitch'        => (float) ( $hotspot['pitch'] ?? 0 ),
				'target_scene' => absint( $hotspot['target_scene'] ?? 0 ),
				'url'          => esc_url_raw( $hotspot['url'] ?? '' ),
				'media_id'     => absint( $hotspot['media_id'] ?? 0 ),
				'icon'         => sanitize_key( $hotspot['icon'] ?? 'default' ),
			);
		}

		return $clean;
	}

	/**
	 * Sanitize the viewer settings meta value.
	 *
	 * @param mixed $viewer Raw meta value.
	 * @return array Sanitized settings.
	 */
	public function sanitize_viewer( $viewer ) {
		if ( ! is_array( $viewer ) ) {
			return array();
		}

		$defaults = Settings::viewer_defaults();

		return array(
			'autorotate'    => (bool) ( $viewer['autorotate'] ?? $defaults['autorotate'] ),
			'show_controls' => (bool) ( $viewer['show_controls'] ?? $defaults['show_controls'] ),
			'show_share'    => (bool) ( $viewer['show_share'] ?? $defaults['show_share'] ),
			'gyroscope'     => (bool) ( $viewer['gyroscope'] ?? $defaults['gyroscope'] ),
			'start_scene'   => absint( $viewer['start_scene'] ?? 0 ),
			'height'        => min( 1200, max( 200, absint( $viewer['height'] ?? $defaults['height'] ) ) ),
		);
	}
}
