<?php
require_once 'rover-shortcodes-seorets.php';
require_once 'rover-shortcodes-dsidxpress.php';

class Rover_IDX_Shortcodes
	{
	private Rover_IDX $rover_idx;

	private const SHORTCODES							= [
		'rover_idx_listings'							=> 'ROVER_COMPONENT_LISTINGS',
		'rover_idx_full_page'							=> 'ROVER_COMPONENT_FULL_PAGE',
		'rover_idx_results'								=> 'ROVER_COMPONENT_RESULTS',
		'rover_idx_results_as_table'					=> 'ROVER_COMPONENT_RESULTS_AS_TABLE',
		'rover_idx_results_as_map'						=> 'ROVER_COMPONENT_RESULTS_AS_MAP',
		'rover_idx_property_details'					=> 'ROVER_COMPONENT_PROP_DETAILS',
		'rover_idx_search_panel'						=> 'ROVER_COMPONENT_SEARCH_PANEL',
		'rover_idx_testimonials'						=> 'ROVER_COMPONENT_TESTIMONIALS',
		'rover_idx_plugin'								=> 'ROVER_COMPONENT_PLUGIN',
		'rover_idx_report'								=> 'rover-report-panel',
		'rover_idx_home_worth'							=> 'ROVER_COMPONENT_HOME_WORTH',
		'rover_idx_home_worth_two_page'					=> 'ROVER_COMPONENT_HOME_WORTH_TWO_PAGE',
		'rover_idx_cta'									=> 'ROVER_COMPONENT_CTA',
		'rover_idx_contact'								=> 'ROVER_COMPONENT_CONTACT',
		'rover_idx_register'							=> 'ROVER_COMPONENT_REGISTER',
		'rover_idx_slider'								=> 'ROVER_COMPONENT_SLIDER',
		'rover_idx_searchslider'						=> 'ROVER_COMPONENT_SEARCHSLIDER',
		'rover_idx_marketconditions'					=> 'rover-market-conditions',
		'rover_idx_unsubscribe'							=> 'ROVER_COMPONENT_UNSUBSCRIBE',
		'rover_idx_settings'							=> 'rover-settings-panel',
		'rover_idx_agent'								=> 'ROVER_COMPONENT_AGENT_DETAIL_PAGE',
		'rover_idx_agents'								=> 'ROVER_COMPONENT_AGENT_LIST',
		'rover_idx_endpoint'							=> 'ROVER_COMPONENT_AUTHENTICATED_USER_ENDPOINT',
		];

	#	Make sure ROVER_COMPONENT_SITE_SEARCH is not added to CACHEABLE_COMPONENTS - it contains a nonce

	private const CACHEABLE_COMPONENTS					= [
		'ROVER_COMPONENT_RESULTS'						=> true,
		'ROVER_COMPONENT_RESULTS_AS_TABLE'				=> true,
		'ROVER_COMPONENT_RESULTS_AS_MAP'				=> true,
		'ROVER_COMPONENT_LISTINGS'						=> true,
		'rover-market-conditions'						=> true,
		];

	private const WP_CACHE_GROUP						= 'rover_idx';

	public function __construct(Rover_IDX $rover_idx)
		{
		$this->rover_idx						= $rover_idx;
		}

	public function add_shortcodes(): void
		{
		foreach (self::SHORTCODES as $shortcode => $component) {
			add_shortcode($shortcode, function ($atts = []) use ($shortcode, $component) {
				return $this->render_component($shortcode, $component, (array) $atts);
				});
			}

		#	Special cases
		add_shortcode('rover_idx_site_search', [$this, 'site_search']);
		add_shortcode('rover_idx_links', [$this, 'links']);

		#	Vendor mappings
		add_shortcode('sr-listings', [$this, 'seo_rets_listings']);
		add_shortcode('sr-list', [$this, 'seo_rets_links']);
		add_shortcode('idx-listings', [$this, 'dsidxpress_listings']);
		add_shortcode('idx-quick-search', [$this, 'dsidxpress_search']);

		add_shortcode('rover_idx_widget', [$this, 'rover_idx_widget']);

		add_filter('no_texturize_shortcodes', [$this, 'rover_no_wptexturize']);
		}


	public function site_search($atts)
		{
		$html											= $this->render_component('rover_idx_site_search', 'ROVER_COMPONENT_SITE_SEARCH', (array) $atts);

		if (!$html) {
			return null;
			}

		return str_replace(
				['IDX_AJAX', 'IDX_SS_NONCE'],
				[admin_url('admin-ajax.php'), wp_create_nonce(roverKEYS::NONCE_SS)],
				$html
			);
		}

	public function links($atts)
		{
		$html											= $this->render_component('rover_idx_links', 'rover_idx_links', (array) $atts);

		if (!$html) {
			return null;
			}

		return str_replace(
				['IDX_AJAX', 'IDX_SS_NONCE'],
				[admin_url('admin-ajax.php'), wp_create_nonce(roverKEYS::NONCE_SS)],
				$html
			);
		}


	/*	Map other vendors shortcodes to our functions	*/

	public function seo_rets_listings($atts)	{

		$seorets										= new Rover_IDX_Shortcodes_SEORETS();
		$atts											= $seorets->map_seorets_to_rover($atts);

		return $this->render_component(
				'sr-listings',
				'ROVER_COMPONENT_RESULTS_AS_TABLE',
				$atts
				);
		}

	public function seo_rets_links($atts)		{

		$seorets										= new Rover_IDX_Shortcodes_SEORETS();
		$atts											= $seorets->map_seorets_to_rover($atts);

		$atts['plugin_type']							= 'quickSearchLinks';
		$atts['plugin_height']							= 'auto';
		$atts['all_cities']								= ($atts['object'] == 'city')
																? '*'
																: null;
		$atts['quick_search_include_counts']			= $atts['include_counts'];
		$atts['quick_search_include_areas']				= $atts['include_areas'];

		return $this->render_component(
				'sr-list',
				'ROVER_COMPONENT_PLUGIN',
				$atts
				);
		}

	public function dsidxpress_listings($atts)	{

		$dsexp											= new Rover_IDX_Shortcodes_DS();
		$atts											= $dsexp->map_dsidxpress_to_rover($atts);

		return $this->render_component(
				'idx-listings',
				'ROVER_COMPONENT_RESULTS',
				$atts
				);
		}

	public function dsidxpress_search($atts)	{

		if (isset($atts['format']) && ($atts['format'] == 'horizontal') )
			{
			$atts['search_panel_layout']				= 'custom';
			$atts['prop_type_control_style']			= 1;
			$atts['template_fields']					= 'buildCitySelect,buildBeds,buildBaths,buildPrice,buildSqFt';
			$atts['all_per_row']						= 6;
			}
		else
			{
			$atts['search_panel_layout']				= 'custom';
			$atts['city_buttons_per_row']				= 1;
			$atts['prop_type_control_style']			= 1;
			$atts['proptype_buttons_per_row']			= 1;
			$atts['template_fields']					= 'buildCitySelect,buildBeds,buildBaths,buildPrice,buildSqFt,buildAcres';
			}

		unset($atts['format']);


		return $this->render_component(
				'idx-quick-search',
				'ROVER_COMPONENT_SEARCH_PANEL',
				$atts
				);
		}

	public function rover_idx_widget($atts) {

		global											$wp_widget_factory;

		extract(shortcode_atts(array(
			'widget_name' => false
			), $atts));

		$widget_name									= esc_html($widget_name);

		if (!is_a($wp_widget_factory->widgets[$widget_name], 'WP_Widget')):
			$wp_class									= 'WP_Widget_'.ucwords(strtolower($widget_name));

			if (!is_a($wp_widget_factory->widgets[$wp_class], 'WP_Widget')):
				return '<p>'.sprintf(__("%s: Widget class not found. Make sure this widget exists and the class name is correct"),'<strong>'.$widget_name.'</strong>').'</p>';
			else:
				$class									= $wp_class;
			endif;
		endif;

		ob_start();
		the_widget($widget_name, $atts, array('widget_id'=>'arbitrary-instance-'.rand(10000,99999),
			'before_widget' => '',
			'after_widget' => '',
			'before_title' => '',
			'after_title' => ''
		));
		$output = ob_get_contents();
		ob_end_clean();

		return $output;
		}

	public function rover_no_wptexturize(array $shortcodes): array
		{
		return array_merge($shortcodes, array_keys(self::SHORTCODES), [
			'sr-listings',
			'sr-list',
			'rover_idx_widget',
			]);
		}

	private function render_component(string $shortcode, string $component, $atts): ?string
		{
		static $request_cache							= [];

		$atts											= $this->normalize_atts($shortcode, $atts);

		ksort($atts);
		$hash											= md5(json_encode($atts));

		$object_key										= null;
		$request_key									= $component . ':' . $hash;
		$object_caching_disabled						= (
															isset($this->rover_idx->roveridx_theming['wp_prevent_object_caching']) &&
															$this->rover_idx->roveridx_theming['wp_prevent_object_caching']  == 'yes'
															)
															? true
															: false;


		#	1. Request cache
		if (isset($request_cache[$request_key])) {
#			$this->rover_idx->COMMON->error_log(__FILE__, __FUNCTION__, __LINE__, ' returning request_cache value for ['.$request_key.']');
			return $request_cache[$request_key];
			}

		$html											= null;

		#	2. Persistent object cache
		if (!$object_caching_disabled && isset(self::CACHEABLE_COMPONENTS[$component]))
			{
			$object_key									= 'rover:' . $component . ':' . $hash;

			$html										= wp_cache_get($object_key, self::WP_CACHE_GROUP);

			if ($html !== false) {
#				$this->rover_idx->COMMON->error_log(__FILE__, __FUNCTION__, __LINE__, ' returning wp_cache_get for ['.$object_key.']');
				return $request_cache[$request_key] = $html;
				}
			}

		#	3. Render

		$content										= $this->rover_idx->CONTENT->rover_content($component, $atts);
		$html											= $content['the_html'] ?? null;

		#	4. Store
		if (
			!$object_caching_disabled &&
			!is_null($html) &&
			!is_null($object_key) &&
			isset(self::CACHEABLE_COMPONENTS[$component])
			)
			{
			wp_cache_set(
				$object_key,
				$html,
				self::WP_CACHE_GROUP,
				$this->component_ttl($component)
				);
			}

		return $request_cache[$request_key] = $html;
		}


	private function component_ttl(string $component): int
		{
		switch ($component) {
			case 'ROVER_COMPONENT_LISTINGS':
			case 'ROVER_COMPONENT_RESULTS':
			case 'ROVER_COMPONENT_RESULTS_AS_TABLE':
				return 300;								#	5 minutes
			default:
				return 900;								#	15 minutes
			}
		}

	private function normalize_atts(string $shortcode, $atts): array
		{
		static $cache									= [];

		#	Ensure array
		$atts											= (array) $atts;

#		$this->rover_idx->COMMON->error_log(__FILE__, __FUNCTION__, __LINE__, ' atts ['.print_r($atts, true).']');

		#	Stable cache key (order-independent)
		ksort($atts);
		$key											= $shortcode . ':' . md5(serialize($atts));

		if (isset($cache[$key])) {
#			$this->rover_idx->COMMON->error_log(__FILE__, __FUNCTION__, __LINE__, ' returning static cache value for ['.$key.']');
			return $cache[$key];
			}

		/*
		* Canonicalization
		*/
		foreach ($atts as $k => $v) {
			if (is_string($v)) {
				$v = trim($v);

				if ($v === 'true' || $v === 'yes' || $v === '1') {
					$v = true;
					}
				else if ($v === 'false' || $v === 'no' || $v === '0') {
					$v = false;
					}

				$atts[$k] = $v;
				}
			}

		/*
		* Derived values
		*/
		if (($atts['object'] ?? null) === 'city') {
			$atts['all_cities']							= '*';
			}

#		$this->rover_idx->COMMON->error_log(__FILE__, __FUNCTION__, __LINE__, ' atts ['.print_r($atts, true).']');

		return $cache[$key] = $atts;
		}


	}
?>