<?php

namespace MyListing\Src;

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

class Explore {
	public
		$active_listing_type = false,
		$data,
		$types,
		$taxonomies;

	public static
		$custom_taxonomies,
		$explore_page;

	public static function boot() {
		self::$custom_taxonomies = mylisting_custom_taxonomies();

		add_action( 'init', [ __CLASS__, 'add_rewrite_rules' ], 5 );
		add_action( 'template_redirect', [ __CLASS__, 'handle_single_term_page' ], 35 );
	}

	public function __construct( $data ) {
		$this->data = $data;
		$this->types = array_filter( array_map( function( $listing_type ) {
			return \MyListing\Src\Listing_Type::get_by_name( $listing_type );
		}, array_column( (array) $this->data['listing_types'], 'type' ) ) );

		$this->get_active_listing_type();
		$this->get_taxonomies();
	}

	public function get_types_config() {
		$config = [];
		$index = 0;

		// preserve page initially for the active listing type, so that the 'pg' url param has effect
		$pg = ! empty( $_GET['pg'] ) ? absint( $_GET['pg'] ) : 1;

		foreach ( (array) $this->types as $type ) {
			$tabs = $type->get_explore_tabs();
			$fields = $type->get_fields();
			$allowed_tabs = ['search-form'];
			$is_global = $type->is_global();

			// taxonomy tabs config
			$taxonomies = $this->taxonomies;
			foreach ( $taxonomies as $key => $tax ) {
				// determine active term, validate if it belongs to this listing type
				if ( $tax['activeTermId'] && ( $term = \MyListing\Src\Term::get( $tax['activeTermId'] ) ) ) {
			        $term_types = $term->get_listing_types();

			        if ( ! empty( $term_types ) && ! in_array( $type->get_id(), $term_types ) ) {
			        	// term doesn't belong to this listing type, unset it
			            $taxonomies[ $key ]['activeTermId'] = 0;
			        }
				}

				// only show terms from a taxonomy if that taxonomy is used as an explore tab for this listing type,
				// or as an add listing field for this listing type.
				if ( in_array( $tax['tab_id'], array_keys( $tabs ) ) || in_array( $tax['field_name'], array_keys( $fields ) ) ) {
					$allowed_tabs[] = $tax['tab_id'];
				}
			}

			// determine active tab
			// First check if the tab is available as a query var. This has the highest priority.
			if ( in_array( get_query_var( 'explore_tab' ), $allowed_tabs ) ) {
				$active_tab = get_query_var( 'explore_tab' );
			}
			// Then check if the tab is provided as a GET param.
			elseif ( ! empty( $_GET['tab'] ) && in_array( $_GET['tab'], array_keys( $tabs ) ) ) {
				$active_tab = sanitize_text_field( $_GET['tab'] );
			}
			// Otherwise, default to the first tab for each listing type.
			else {
				$active_tab = ! empty( $tabs ) ? reset( $tabs )['type'] : 'search-form';
			}

			$filters = [
				'page' => ( $pg >= 1 ? $pg - 1 : 0 ),
				'preserve_page' => $pg > 1,
			];

			foreach ( (array) $type->get_advanced_filters() as $filter ) {
				if ( $filter->is_ui() ) {
					continue;
				}

				$request_value = $filter->get_request_value();
				if ( is_array( $request_value ) ) {
					$filters += $request_value;
				} else {
					$filters[ $filter->get_form_key() ] = $filter->get_request_value();
				}
			}

			$config[ $type->get_slug() ] = [
				'index' => $index,
				'name' => $type->get_plural_name(),
				'icon' => $type->get_icon(),
				'slug' => $type->get_slug(),
				'id' => $type->get_id(),
				'tabs' => $tabs,
				'is_global' => $is_global,
				'filters' => $filters,
				'tab' => $active_tab,
				'defaultTab' => ! empty( $tabs ) ? reset( $tabs )['type'] : 'search-form',
				'taxonomies' => $taxonomies,
				'max_num_pages' => 0,
				'is_first_load' => true,
				'preview_type' => $type->get_preview_type(),
			];

			$index++;
		}

		return $config;
	}

	// not  used anymore since 2.9.11
	public function get_active_mobile_tab() {
		$tabs = ['f' => 'filters', 'r' => 'results', 'm' => 'map'];
		if ( ! empty( $_GET['mt'] ) && isset( $tabs[ $_GET['mt'] ] ) ) {
			return $tabs[ $_GET['mt'] ];
		}
		return 'results';
	}

	public function get_active_listing_type() {
		if ( empty( $this->types ) ) {
			$this->active_listing_type = false;
			return false;
		}

		$this->active_listing_type = $this->types[0];

		if ( isset( $_GET['type'] ) && ( $getType = sanitize_text_field( $_GET['type'] ) ) ) {
			foreach ( $this->types as $listing_type ) {
				if ( $listing_type->get_slug() == $getType ) {
					$this->active_listing_type = $listing_type;
					break;
				}
			}
		}
	}

	public function get_taxonomies() {
		$taxonomies = array_merge( [ 'categories', 'regions', 'tags' ], array_keys( self::$custom_taxonomies ) );
		$base_taxonomies = [
			'categories' => [ 'tax' => 'job_listing_category', 'query_var' => 'explore_category', 'field' => 'job_category' ],
			'regions' => [ 'tax' => 'region', 'query_var' => 'explore_region', 'field' => 'region' ],
			'tags' => [ 'tax' => 'case27_job_listing_tags', 'query_var' => 'explore_tag', 'field' => 'job_tags' ],
		];

		foreach ( $taxonomies as $taxonomy ) {
			// get the query var for this taxonomy
			$query_var = isset( $base_taxonomies[ $taxonomy ] ) ? $base_taxonomies[ $taxonomy ]['query_var'] : 'explore_'.$taxonomy;

			// get the actual taxonomy name (for base taxonomies, it's different from the tab id, e.g. categories -> job_listing_category)
			$tax = isset( $base_taxonomies[ $taxonomy ] ) ? $base_taxonomies[ $taxonomy ]['tax'] : $taxonomy;
			$field = isset( $base_taxonomies[ $taxonomy ] ) ? $base_taxonomies[ $taxonomy ]['field'] : $taxonomy;

			// get the active term if available
			$active_term_config = false;
			$active_term_id = 0;
			if ( get_query_var( $query_var ) && ( $term = get_term_by( 'slug', sanitize_title( get_query_var( $query_var ) ), $tax ) ) ) {
				$active_term = \MyListing\Src\Term::get( $term );
				$active_term_id = $active_term->get_id();

				// @todo: refactor, prevent duplicate code with explore-terms-endpoint.php
				$background = $active_term->get_image();
				$active_term_config = [
					'term_id' => $active_term->get_id(),
					'name' => $active_term->get_name(),
					'description' => $active_term->get_description(),
					'slug' => $active_term->get_slug(),
					'link' => $active_term->get_link(),
					'parent' => $active_term->get_parent_id(),
					'count' => $active_term->get_count(),
					'single_icon' => $active_term->get_icon( [ 'background' => false, 'color' => true ] ),
					'color' => $active_term->get_color(),
					'icon' => $active_term->get_icon( [ 'background' => false, 'color' => false ] ),
					'background' => is_array( $background ) && ! empty( $background ) ? $background['sizes']['large'] : false,
					'listing_types' => array_filter( array_map( 'absint', (array) get_term_meta( $active_term->get_id(), 'listing_type', true ) ) ),
				];

				// if we're loading a term explore page, then determine the best listing type to load based on the term's taxonomy.
				$this->set_active_type_by_taxonomy( [ 'taxonomy' => $tax, 'tab_id' => $taxonomy, 'field_name' => $field ], $active_term );
			}

			$pg = ! empty( $_GET['pg'] ) ? absint( $_GET['pg'] ) : 1;
			$this->taxonomies[ $taxonomy ] = [
	        	'tax' => $tax,
	        	'field_name' => $field,
	        	'tab_id' => $taxonomy,
	        	'page' => ( $pg - 1 ),
	        	'termsLoading' => false,
	        	'termsPage' => 0,
	        	'activeTermId' => $active_term_id,
	        	'activeTerm' => $active_term_config,
	        	'hasMore' => false,
	        	'terms' => new \stdClass,
			];
		}
	}

	/**
	 * If we're loading a term explore page, then determine the best listing type
	 * to load based on the term's taxonomy.
	 *
	 * @since 2.1
	 */
	public function set_active_type_by_taxonomy( $tax, $term ) {
		$active_term_types = $term->get_listing_types();

		foreach ( (array) $this->types as $type ) {
			$tabs = $type->get_explore_tabs();
			$fields = $type->get_fields();
			// if there's an active term, make sure it only loads a listing type
			// that it's been assigned to, if any.
			if ( ! empty( $active_term_types ) ) {
				if ( ! in_array( $type->get_id(), $active_term_types )  ) {
					continue;
				}

				mlog()->note( 'Found listing type in active term\'s allowed types. Setting as active.' );
				$this->active_listing_type = $type;
				break;
			}

			// check if global listing type is added in explore page
			// if yes set it as the active listing type
			if ( $type->is_global() ) {
				mlog()->note( 'Global listing type found. Setting as active.' );
				$this->active_listing_type = $type;
				break;
			}

			// check if there are listings for this term in any of the listing types
			// if there is, set the first occurrence as the active listing type
			$count_by_type = $term->get_count_by_type();
			if ( is_array( $count_by_type ) && ! empty( $count_by_type ) ) {
				$listing_type_slugs = array_map( function( $type ) { return $type->get_slug(); }, $this->types );
				foreach ( $listing_type_slugs as $slug ) {
					if ( isset( $count_by_type[ $slug ] ) ) {
						$listing_type_obj = get_page_by_path( $slug, OBJECT, 'case27_listing_type' );
						if ( $listing_type_obj ) {
							foreach ( $this->types as $type ) {
								if ( $type->get_id() === $listing_type_obj->ID ) {
									mlog()->note( 'Found listings for term in listing type. Setting as active.' );
									$this->active_listing_type = $type;
									break 2;
								}
							}
						}
					}
				}
			}
			// check if there's a listing type that's using a tab for this taxonomy.
			// if there is, set the first occurrence as the active listing type
			if ( in_array( $tax['tab_id'], array_keys( $tabs ) ) ) {
				mlog()->note( 'Found listing type with a '.$tax['tab_id'].' tab available. Setting as active type.' );
				$this->active_listing_type = $type;
				break;
			}
			// if above check fails, then search for listing types that at least use this taxonomy in their add listing page
			if ( in_array( $tax['field_name'], array_keys( $fields ) ) ) {
				mlog()->note( 'Found listing type that uses the '.$tax['field_name'].' taxonomy field. Setting as active type.' );
				$this->active_listing_type = $type;
				break;
			}

			// if both check fails, the requested term's taxonomy isn't being used anywhere, so it's an invalid request.
		}
	}

	/**
	 * Add rewrite rules for pretty url-s in Explore page.
	 * e.g. site/explore/category/category-name
	 * 		site/explore/regions/region-name
	 * 		site/explore/tags/tag-name
	 */
	public static function add_rewrite_rules() {
		// Stack overflow link: https://wordpress.stackexchange.com/questions/89164/passing-parameters-to-a-custom-page-template-using-clean-urls
		if ( ! ( $explore_page_id = get_option( 'options_general_explore_listings_page', false ) ) ) {
			return;
		}

		if ( ! ( $explore_page = get_post( $explore_page_id ) ) ) {
			return;
		}

		self::$explore_page = $explore_page;

		// Add query vars.
		global $wp;
	    $wp->add_query_var( 'explore_tab' );

    	$bases = \MyListing\Src\Permalinks::get_permalink_structure();

		// default taxonomies
		self::_rewrite_listing_taxonomy( 'category', $bases['category_base'], 'categories' );
		self::_rewrite_listing_taxonomy( 'region', $bases['region_base'], 'regions' );
		self::_rewrite_listing_taxonomy( 'tag', $bases['tag_base'], 'tags' );

		// custom taxonomies
		foreach ( self::$custom_taxonomies as $slug => $label ) {
			self::_rewrite_listing_taxonomy( $slug, $slug );
		}
	}

	private static function _rewrite_listing_taxonomy( $taxonomy, $base, $explore_tab = null ) {
		if ( $explore_tab === null ) {
			$explore_tab = $taxonomy;
		}

		// rewrite tag
    	add_rewrite_tag( '%explore_'.$taxonomy.'%', '([^/]+)' );

    	// rewrite rule
	    add_rewrite_rule(
	    	sprintf( '^%s/([^/]+)?', $base ),
	    	sprintf( 'index.php?page_id=%d&explore_tab=%s&explore_%s=$matches[1]', self::$explore_page->ID, $explore_tab, $taxonomy ),
	    	'top'
	    );
	}

	/**
	 * Since taxonomy archive pages are redirected to the Explore page, filter page
	 * meta data to show term title/description/details instead of Explore page details.
	 *
	 * @since 2.1
	 */
	public static function handle_single_term_page() {
	    $taxonomies = [
	        [ 'taxonomy' => 'job_listing_category', 'query_var' => 'explore_category', 'name_filter' => 'single_cat_title' ],
	        [ 'taxonomy' => 'case27_job_listing_tags', 'query_var' => 'explore_tag', 'name_filter' => 'single_tag_title' ],
	        [ 'taxonomy' => 'region', 'query_var' => 'explore_region' ],
	    ];

	    foreach( mylisting_custom_taxonomies() as $key => $label ) {
	    	$taxonomies[] = [
	    		'taxonomy' => $key,
	    		'query_var' => 'explore_'.$key,
	    	];
	    }

	    foreach ( $taxonomies as $tax ) {
	    	if ( ! get_query_var( $tax['query_var'] ) ) {
	    		continue;
	    	}

	    	$term = \MyListing\Src\Term::get_by_slug( get_query_var( $tax['query_var'] ), $tax['taxonomy'] );
	    	if ( ! $term ) {
	    		continue;
	    	}

	    	/* we're on single listing term page */
	    	$image = $term->get_image();
    		$page_title = apply_filters( isset( $tax['name_filter'] ) ? $tax['name_filter'] : 'single_term_title', $term->get_name() );
            $page_title .= ' ' . apply_filters( 'document_title_separator', '-' ) . ' ';
            $page_title .= get_bloginfo( 'name', 'display' );
			$page_title = capital_P_dangit( esc_html( convert_chars( wptexturize( $page_title ) ) ) );

	    	$cfg = new \stdClass;
	    	$cfg->title = $page_title;
	    	$cfg->link = $term->get_link();
	    	$cfg->image = is_array( $image ) && ! empty( $image ) ? $image['sizes']['medium_large'] : false;
	    	$cfg->is_yoast = defined( 'WPSEO_VERSION' );
	    	$cfg->is_rankmath = defined( 'RANK_MATH_VERSION' );
			$cfg->description = $term->get_description();

			if ( $cfg->is_yoast ) {
				$meta = get_option( 'wpseo_taxonomy_meta' );
				$tax_meta = get_option( 'wpseo_titles' );
				$term_object = get_term( $term->get_id(), $tax['taxonomy'] );
				$tax_slug = $term_object->taxonomy;
				$term_id = $term_object->term_id;
				$replacer = new \WPSEO_Replace_Vars();

				if ( isset( $meta[ $tax_slug ][ $term_id ]['wpseo_title'] ) ) {
					$cfg->title = $replacer->replace(
						$meta[ $tax_slug ][ $term_id ]['wpseo_title'],
						$term_object
					);
	            } else {
					$cfg->title = $replacer->replace(
						\WPSEO_Options::get('title-tax-'.$tax_slug, ''),
						$term_object
					);
	            }

	    		if ( isset( $meta[ $tax_slug ][ $term_id ]['wpseo_desc'] ) ) {
					$cfg->description = $replacer->replace(
						$meta[ $tax_slug ][ $term_id ]['wpseo_desc'],
						$term_object
					);
	            } elseif( empty( $cfg->description ) ) {
					$cfg->description = $replacer->replace(
						\WPSEO_Options::get('metadesc-tax-'.$tax_slug, ''),
						$term_object
					);
	            }

	            add_filter( 'wpseo_robots', function( $robots ) use ( $meta, $tax_slug, $term_id, $tax_meta ) {
	            	if ( isset( $meta[ $tax_slug ][ $term_id ]['wpseo_noindex'] ) ) {
	            		if ( $meta[ $tax_slug ][ $term_id ]['wpseo_noindex'] === 'noindex' || $meta[ $tax_slug ][ $term_id ]['wpseo_noindex'] === '1' ) {
	            			return 'noindex, nofollow';
	            		} else {
	            			return $robots;
	            		}
	            	}

	            	if ( isset( $tax_meta['noindex-tax-' . $tax_slug . ''] ) ) {
	            		if ( $tax_meta['noindex-tax-' . $tax_slug . ''] == 1 ) {
	            			return 'noindex, nofollow';
	            		}
	            	}

	            	return $robots;
	            }, 20 );
			}

			if ( empty( $cfg->title ) ) {
				$cfg->title = $page_title;
			}

			if ( empty( $cfg->description ) ) {
	    		$cfg->description = sprintf(
	    			__( 'Browse listings in %s | %s', 'my-listing' ),
	    			$term->get_name(),
	    			get_bloginfo('description')
	    		);
	    	}

	    	/**
	    	 * Handle redirects for custom landing pages.
	    	 * @since 1.7
	    	 */
	    	$redirect_page = get_term_meta( $term->get_id(), '_landing_page', true );
	    	if ( $redirect_page && is_numeric( $redirect_page ) && ( $redirect_url = get_permalink( absint( $redirect_page ) ) ) ) {
	            wp_redirect( $redirect_url, 301 );
	            exit;
	        }

	    	/**
	    	 * Filter the page title and meta tags.
	    	 * @since 1.6.2
	    	 */
			add_filter( 'pre_get_document_title', function() use ( $cfg ) { return $cfg->title; }, 10e3 );
			add_action( 'wp_head', function() use ( $cfg ) {
				// title meta tag is also output by Yoast, so only add it if Yoast isn't active
				if ( ! $cfg->is_yoast && ! $cfg->is_rankmath ) {
					printf( '<meta property="og:title" content="%s"/>'."\n", esc_attr( $cfg->title ) );
				}

				// description tags are also output by Yoast, so only add if Yoast isn't active
				if ( $cfg->description && ! $cfg->is_yoast && ! $cfg->is_rankmath ) {
					printf( '<meta name="description" content="%s"/>'."\n", esc_attr( $cfg->description ) );
					printf( '<meta property="og:description" content="%s"/>'."\n", esc_attr( $cfg->description ) );
				}

				// output term image meta tag
				if ( $cfg->image && ! $cfg->is_yoast && ! $cfg->is_rankmath ) {
					printf( '<meta property="og:image" content="%s"/>'."\n", esc_attr( $cfg->image ) );
				}
			}, 1 );

			/**
			 * Avoid page being marked as duplicate content by search engines b\w explore page
			 * and single term pages, by adding unique canonical url's for every term.
	    	 * @since 2.1
	    	 */
			remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
			add_filter( 'get_canonical_url', function() use ( $cfg ) { return $cfg->link; } );

			// Yoast
			add_filter( 'wpseo_opengraph_title', function() use ( $cfg ) { return $cfg->title; }, 10e3 );
			add_filter( 'wpseo_title', function() use ( $cfg ) { return $cfg->title; }, 10e3 );
			add_filter( 'wpseo_opengraph_url', function() use ( $cfg ) { return $cfg->link; }, 10e3 );
			add_filter( 'wpseo_metadesc', function() use ( $cfg ) { return $cfg->description; } );
			add_filter( 'wpseo_opengraph_desc', function() use ( $cfg ) { return $cfg->description; } );
			add_filter( 'wpseo_canonical', function() use ( $cfg ) { return $cfg->link; } );
			if ( $cfg->is_yoast && $cfg->image ) {
				add_filter( 'wpseo_opengraph_image', function( $image ) use ( $cfg ) { return $cfg->image; }, 10e5, 1 );
			}

			if ( $cfg->is_rankmath ) {
				$replacer = new \RankMath\Replace_Variables\Replacer();
				$object = get_term( $term->get_id(), $tax['taxonomy'] );
				$title = \RankMath\Term::get_meta( 'title', $object, $object->taxonomy );
				$description = \RankMath\Term::get_meta( 'description', $object, $object->taxonomy );
				$robots = \RankMath\Term::get_meta( 'robots', $object, $object->taxonomy );
				$advanced_robots = \RankMath\Term::get_meta( 'advanced_robots', $object, $object->taxonomy );

				if ( '' !== $title ) {
					$cfg->title = $title;
				} else {
					$cfg->title = \RankMath\Paper\Paper::get_from_options( 'tax_' . $tax['taxonomy'] . '_title', $object );
				}

				if ( '' !== $description ) {
					$cfg->description = $description;
				} else {
					$cfg->description = \RankMath\Paper\Paper::get_from_options( 'tax_' . $tax['taxonomy'] . '_description', $object );
				}

				if ( '' !== $robots ) {
					$cfg->robots = $robots;
				} else {
					$cfg->robots = \RankMath\Helpers\Api::get_settings( 'titles.tax_' . $tax['taxonomy'] . '_robots', $object );
				}

				if ( '' !== $advanced_robots ) {
					$cfg->advanced_robots = $advanced_robots;
				} else {
					$cfg->advanced_robots = \RankMath\Helpers\Api::get_settings( 'titles.tax_' . $tax['taxonomy'] . '_advanced_robots', $object );
				}

				$cfg->slack_enhanced_sharing = \RankMath\Helpers\Api::get_settings( 'titles.tax_' . $tax['taxonomy'] . '_slack_enhanced_sharing', $object );

				if ( $cfg->image ) {
	                add_filter( 'rank_math/opengraph/facebook/image', function( $image ) use ( $cfg ) { return $cfg->image; }, 10e5, 1 );
	            }
	            
	            add_filter( 'rank_math/frontend/title', function( $title ) use ( $cfg ) { return $cfg->title; }, 10e3 );
	            add_filter( 'rank_math/opengraph/url', function() use ( $cfg ) { return $cfg->link; }, 10e3 );
	            add_filter( 'rank_math/frontend/description', function() use ( $cfg ) { return $cfg->description; } );
	            add_filter( 'rank_math/opengraph/desc', function( $desc ) use ( $cfg ) { return $cfg->description; } );
	            add_action('wp_head', function() use ($cfg) {
	            	if (isset($cfg->link)) {
	            		echo '<link rel="canonical" href="' . esc_url($cfg->link) . '" />' . "\n";
	            	}
	            }, 1);

	            if ( $cfg->robots ) {
		            add_filter( 'rank_math/frontend/robots', function( $robots ) use ( $cfg ) {
		            	return \RankMath\Paper\Paper::robots_combine( $cfg->robots );
		            } );
		        }

		        if ( $cfg->advanced_robots ) {
		            add_filter( 'rank_math/frontend/advanced_robots', function( $advanced_robots ) use ( $cfg ) {
		            	return \RankMath\Paper\Paper::advanced_robots_combine( $cfg->advanced_robots );
		            } );
		        }

	            add_filter( 'rank_math/opengraph/slack_enhanced_data', function( $data ) use ( $cfg ) {

	            	if ( ! $cfg->slack_enhanced_sharing ) {
	            		return [];
	            	}

	            	return $data;
	            } );
			}

	    	return;
	    }
	}
}
