<?php

class Smartcrawl_Yoast_Importer extends Smartcrawl_Importer {
	const IMPORT_IN_PROGRESS_FLAG = 'wds-yoast-import-in-progress';
	const NETWORK_IMPORT_SITES_PROCESSED_COUNT = 'wds-yoast-network-sites-processed';

	private $custom_handlers = array(
		'wpseo-premium-redirects-base'             => 'handle_redirects',
		'_yoast_wpseo_meta-robots-noindex'         => 'handle_robots_noindex_value',
		'wpseo_titles/noindex-POSTTYPE'            => 'handle_post_type_noindex',
		'wpseo_titles/noindex-tax-TAXONOMY'        => 'handle_taxonomy_noindex',
		'wpseo_titles/title-ptarchive-POSTTYPE'    => 'handle_pt_archive_title',
		'wpseo_titles/metadesc-ptarchive-POSTTYPE' => 'handle_pt_archive_description',
	);

	public function data_exists() {
		// Go ahead with the import if ...
		return $this->version_supported()               // ... current yoast version is explicitly marked as supported
		       || $this->mapped_options_available();    // ... or we have all the options we need in the right places
	}

	private function version_supported() {
		$options = get_option( 'wpseo' );
		$version = smartcrawl_get_array_value( $options, 'version' );

		if ( ! $version ) {
			return false;
		}

		return apply_filters(
			'wds-import-yoast-data-exists',
			strpos( $version, '15.' ) === 0
		);
	}

	private function mapped_options_available() {
		$mappings = $this->expand_mappings( $this->load_option_mappings() );
		$source_options = $this->get_yoast_options();
		$difference = array_diff_key( $mappings, $source_options );

		return empty( $difference );
	}

	public function import_options() {
		$mappings = $this->expand_mappings( $this->load_option_mappings() );
		$source_options = $this->get_yoast_options();
		$target_options = array();
		foreach ( $source_options as $source_key => $source_value ) {
			$target_key = smartcrawl_get_array_value( $mappings, $source_key );

			if ( ! $target_key ) {
				$target_options = $this->try_custom_handlers( $source_key, $source_value, $target_options );
				continue;
			}

			$processed_target_key = $this->pre_process_key( $target_key );
			$processed_target_value = $this->pre_process_value( $target_key, $source_value );

			if ( ! $processed_target_key ) {
				continue;
			}

			smartcrawl_put_array_value(
				$processed_target_value,
				$target_options,
				$processed_target_key
			);
		}

		$target_options = $this->activate_modules( $target_options );
		$target_options = $this->activate_social_options_for_types( $target_options );

		$this->save_options( $target_options );
	}

	private function load_option_mappings() {
		return $this->load_mapping_file( 'yoast-mappings.php' );
	}

	protected function expand_mappings( $mappings ) {
		$mappings = parent::expand_mappings( $mappings );
		$mappings = $this->remove_unnecessary_ptarchive_mappings( $mappings );

		return $mappings;
	}

	/**
	 * ptarchive settings are for custom post types with archives only so let's remove mappings for:
	 * - builtin post types
	 * - post types with no archives
	 *
	 * @param $mappings
	 *
	 * @return array
	 */
	private function remove_unnecessary_ptarchive_mappings( $mappings ) {
		foreach (
			array_merge(
				get_post_types( array( 'public' => true, '_builtin' => true ) ),
				get_post_types( array( 'has_archive' => false ) )
			) as $post_type
		) {
			$ptarchive_title_key = "wpseo_titles/title-ptarchive-$post_type";
			$ptarchive_desc_key = "wpseo_titles/metadesc-ptarchive-$post_type";

			if ( isset( $mappings[ $ptarchive_title_key ] ) ) {
				unset( $mappings[ $ptarchive_title_key ] );
			}
			if ( isset( $mappings[ $ptarchive_desc_key ] ) ) {
				unset( $mappings[ $ptarchive_desc_key ] );
			}
		}

		return $mappings;
	}

	private function get_yoast_options() {
		$all_options = array();
		$keys = array( 'wpseo', 'wpseo_titles', 'wpseo_social' );

		foreach ( $keys as $key ) {
			$options = get_option( $key );
			if ( is_array( $options ) && count( $options ) ) {
				foreach ( $options as $option_key => $option_value ) {
					$all_options[ $key . '/' . $option_key ] = $option_value;
				}
			}
		}

		return $this->add_redirects( $all_options );
	}

	/**
	 * @param $all_options
	 *
	 * @return array
	 */
	private function add_redirects( $all_options ) {
		$option_id = 'wpseo-premium-redirects-base';
		$redirects = get_option( $option_id );
		if ( $redirects ) {
			$all_options = array_merge(
				$all_options,
				array( $option_id => $redirects )
			);

			return $all_options;
		}

		return $all_options;
	}

	private function activate_modules( $target_options ) {
		smartcrawl_put_array_value( true, $target_options, array( 'wds_settings_options', 'social' ) );
		smartcrawl_put_array_value( true, $target_options, array( 'wds_settings_options', 'onpage' ) );

		return $target_options;
	}

	private function activate_social_options_for_types( $target_options ) {
		$og_enabled = smartcrawl_get_array_value( $target_options, array( 'wds_social_options', 'og-enable' ) );
		$twitter_enabled = smartcrawl_get_array_value( $target_options, array(
			'wds_social_options',
			'twitter-card-enable',
		) );

		if ( $og_enabled ) {
			foreach ( $this->get_all_supported_types() as $type ) {
				smartcrawl_put_array_value( true, $target_options, array(
					'wds_onpage_options',
					sprintf( 'og-active-%s', $type ),
				) );
			}
		}

		if ( $twitter_enabled ) {
			foreach ( $this->get_all_supported_types() as $type ) {
				smartcrawl_put_array_value( true, $target_options, array(
					'wds_onpage_options',
					sprintf( 'twitter-active-%s', $type ),
				) );
			}
		}

		return $target_options;
	}

	private function get_all_supported_types() {
		return array_merge(
			array(
				'home',
				'search',
				'404',
				'bp_groups',
				'bp_profile',
				'author',
				'date',
			),
			$this->get_post_types(),
			smartcrawl_get_archive_post_types(),
			$this->get_taxonomies()
		);
	}

	public function wrap_google_meta_in_markup( $target_key, $source_value ) {
		if ( strpos( trim( $source_value ), '<meta' ) === 0 ) {
			return $source_value;
		}

		if ( empty( $source_value ) ) {
			return '';
		}

		return sprintf( '<meta name="google-site-verification" content="%s" />', $source_value );
	}

	public function wrap_bing_meta_in_markup( $target_key, $source_value ) {
		if ( strpos( trim( $source_value ), '<meta' ) === 0 ) {
			return $source_value;
		}

		if ( empty( $source_value ) ) {
			return '';
		}

		return sprintf( '<meta name="msvalidate.01" content="%s" />', $source_value );
	}

	public function process_separator_value( $target_key, $source_value ) {
		$mapping = array(
			'sc-dash'   => 'dash',
			'sc-ndash'  => 'dash-l',
			'sc-mdash'  => 'dash-l',
			'sc-middot' => 'dot',
			'sc-bull'   => 'dot-l',
			'sc-star'   => 'pipe',
			'sc-smstar' => 'pipe',
			'sc-pipe'   => 'pipe',
			'sc-tilde'  => 'tilde',
			'sc-laquo'  => 'caret-left',
			'sc-raquo'  => 'caret-right',
			'sc-lt'     => 'less-than',
			'sc-gt'     => 'greater-than',
		);
		$mapped_value = smartcrawl_get_array_value( $mapping, $source_value );

		if ( ! $mapped_value ) {
			return $source_value;
		}

		return $mapped_value;
	}

	public function handle_taxonomy_noindex( $source_key, $source_value, $target_options ) {
		$current_taxonomy = null;
		foreach ( $this->get_taxonomies() as $taxonomy ) {
			if ( str_replace( 'TAXONOMY', $taxonomy, 'wpseo_titles/noindex-tax-TAXONOMY' ) === $source_key ) {
				$current_taxonomy = $taxonomy;
			}
		}

		if ( ! $current_taxonomy ) {
			return $target_options;
		}

		$noindex_key = array( 'wds_onpage_options', sprintf( 'meta_robots-noindex-%s', $current_taxonomy ) );
		$sitemap_exclusion_key = array(
			'wds_sitemap_options',
			sprintf( 'taxonomies-%s-not_in_sitemap', $current_taxonomy ),
		);

		smartcrawl_put_array_value( $source_value, $target_options, $noindex_key );
		smartcrawl_put_array_value( $source_value, $target_options, $sitemap_exclusion_key );

		return $target_options;
	}

	public function handle_post_type_noindex( $source_key, $source_value, $target_options ) {
		$current_post_type = null;
		foreach ( $this->get_post_types() as $post_type ) {
			if ( str_replace( 'POSTTYPE', $post_type, 'wpseo_titles/noindex-POSTTYPE' ) === $source_key ) {
				$current_post_type = $post_type;
			}
		}

		if ( ! $current_post_type ) {
			return $target_options;
		}

		$noindex_key = array( 'wds_onpage_options', sprintf( 'meta_robots-noindex-%s', $current_post_type ) );
		$sitemap_exclusion_key = array(
			'wds_sitemap_options',
			sprintf( 'post_types-%s-not_in_sitemap', $current_post_type ),
		);

		smartcrawl_put_array_value( $source_value, $target_options, $noindex_key );
		smartcrawl_put_array_value( $source_value, $target_options, $sitemap_exclusion_key );

		return $target_options;
	}

	public function handle_redirects( $source_key, $redirects, $target_options ) {
		if ( empty( $redirects ) ) {
			return $target_options;
		}

		$wds_redirects = array();
		$wds_redirect_types = array();
		foreach ( $redirects as $redirect ) {
			$format = smartcrawl_get_array_value( $redirect, 'format' );
			$origin = smartcrawl_get_array_value( $redirect, 'origin' );
			$url = smartcrawl_get_array_value( $redirect, 'url' );
			$type = smartcrawl_get_array_value( $redirect, 'type' );

			// We are not supporting anything other than plain redirects at the moment
			if ( 'plain' !== $format || ! $origin || ! $url || ! $type ) {
				continue;
			}

			$wds_redirects[ home_url( $origin ) ] = home_url( $url );
			$wds_redirect_types[ home_url( $origin ) ] = $type;
		}
		smartcrawl_put_array_value( $wds_redirects, $target_options, 'wds-redirections' );
		smartcrawl_put_array_value( $wds_redirect_types, $target_options, 'wds-redirections-types' );

		return $target_options;
	}

	public function handle_robots_noindex_value( $source_key, $source_value, $post_meta ) {
		if ( intval( $source_value ) === 1 ) {
			smartcrawl_put_array_value( true, $post_meta, '_wds_meta-robots-noindex' );
		} else if ( intval( $source_value ) === 2 ) {
			smartcrawl_put_array_value( true, $post_meta, '_wds_meta-robots-index' );
		}

		return $post_meta;
	}

	public function process_schema_type_value( $target_key, $source_value ) {
		return 'company' === $source_value ? 'Organization' : 'Person';
	}

	public function import_taxonomy_meta() {
		$mappings = $this->load_taxonomy_meta_mappings();
		$taxonomy_meta_option = get_option( 'wpseo_taxonomy_meta', array() );
		$wds_meta = array();

		foreach ( $taxonomy_meta_option as $taxonomy => $taxonomy_meta ) {
			foreach ( $taxonomy_meta as $term_id => $meta_values ) {
				$wds_taxonomy_meta = array();

				foreach ( $meta_values as $meta_key => $meta_value ) {
					$target_key = smartcrawl_get_array_value( $mappings, $meta_key );

					if ( ! $target_key || ! $meta_value ) {
						continue;
					}

					$processed_target_key = $this->pre_process_key( $target_key );
					$processed_target_value = $this->pre_process_value( $target_key, $meta_value );

					if ( ! $processed_target_key || ! $processed_target_value ) {
						continue;
					}

					smartcrawl_put_array_value(
						$processed_target_value,
						$wds_taxonomy_meta,
						$processed_target_key
					);
				}

				$wds_meta[ $taxonomy ][ $term_id ] = $wds_taxonomy_meta;
			}
		}
		update_option( 'wds_taxonomy_meta', $wds_meta );
	}

	public function load_taxonomy_meta_mappings() {
		return $this->load_mapping_file( 'yoast-taxonomy-meta-mappings.php' );
	}

	public function import_post_meta() {
		$mappings = $this->load_post_meta_mappings();
		$batch_size = apply_filters( 'wds_post_meta_import_batch_size', 300 );
		$all_posts = $this->get_posts_with_yoast_metas();
		$batch_posts = array_slice( $all_posts, 0, $batch_size );

		foreach ( $batch_posts as $post_id ) {
			$wds_meta = array();

			foreach ( $mappings as $source_key => $target_key ) {
				$source_value = get_post_meta( $post_id, $source_key, true );

				if ( ! $target_key ) {
					$wds_meta = $this->try_custom_handlers( $source_key, $source_value, $wds_meta );
					continue;
				}

				$processed_target_key = $this->pre_process_key( $target_key );
				$processed_target_value = $this->pre_process_value( $target_key, $source_value, $post_id );

				if ( ! $processed_target_key ) {
					continue;
				}

				smartcrawl_put_array_value( $processed_target_value, $wds_meta, $processed_target_key );
			}

			$this->add_post_meta( $post_id, $wds_meta );
		}

		$this->update_status( array(
			'remaining_posts' => count( $this->get_posts_with_yoast_metas() ),
			'completed_posts' => count( $this->get_posts_with_target_metas() ),
		) );

		return count( $all_posts ) === count( $batch_posts );
	}

	private function load_post_meta_mappings() {
		return $this->load_mapping_file( 'yoast-post-meta-mappings.php' );
	}

	/**
	 * @return int[]
	 */
	private function get_posts_with_yoast_metas() {
		return $this->get_posts_with_source_metas( '_yoast_' );
	}

	private function add_post_meta( $post_id, $meta ) {
		foreach ( $meta as $meta_key => $meta_value ) {
			update_post_meta( $post_id, $meta_key, $meta_value );
		}
	}

	public function handle_pt_archive_title( $source_key, $source_value, $target_options ) {
		return $this->add_pt_archive_setting( $source_value, $target_options, 'title-%s' );
	}

	/**
	 * @param $source_value
	 * @param $target_options
	 * @param $setting_key
	 *
	 * @return mixed
	 */
	private function add_pt_archive_setting( $source_value, $target_options, $setting_key ) {
		foreach ( smartcrawl_get_archive_post_types() as $archive_post_type ) {
			smartcrawl_put_array_value( $source_value, $target_options, array(
				'wds_onpage_options',
				sprintf( $setting_key, $archive_post_type ),
			) );
		}

		return $target_options;
	}

	public function handle_pt_archive_description( $source_key, $source_value, $target_options ) {
		return $this->add_pt_archive_setting( $source_value, $target_options, 'metadesc-%s' );
	}

	protected function get_pre_processors() {
		return array(
			'wds_onpage_options/preset-separator'          => 'process_separator_value',
			'wds_social_options/schema_type'               => 'process_schema_type_value',
			'wds_sitemap_options/verification-google-meta' => 'wrap_google_meta_in_markup',
			'wds_sitemap_options/verification-bing-meta'   => 'wrap_bing_meta_in_markup',
		);
	}

	protected function get_next_network_site_option_id() {
		return self::NETWORK_IMPORT_SITES_PROCESSED_COUNT;
	}

	protected function get_import_in_progress_option_id() {
		return self::IMPORT_IN_PROGRESS_FLAG;
	}

	protected function get_custom_handlers() {
		return $this->custom_handlers;
	}

	protected function get_source_plugins() {
		return array(
			'wordpress-seo/wp-seo.php',
			'wordpress-seo-premium/wp-seo-premium.php',
		);
	}
}