<?php

class WPML_404_Guess extends WPML_Slug_Resolution {

	private $query_filter;

	public function __construct( &$wpdb, &$sitepress, &$query_filter ) {
		parent::__construct( $wpdb, $sitepress );
		$this->query_filter = &$query_filter;
	}

	public function guess_cpt_by_name( $name, $query ) {
		$type  = $query->get( 'post_type' );
		$ret   = array( $name, $type, false );
		$types = (bool) $type === false
			? $this->sitepress->get_wp_api()->get_post_types( array( 'public' => true ) )
			: (array) $type;
		if ( (bool) $types === true ) {

			$date_snippet = $this->by_date_snippet( $query );
			$page_first = (bool) $query->get( 'pagename' );

			$cache     = new WPML_WP_Cache( 'WPML_404_Guess' );

			$can_read_private = current_user_can( 'read_private_posts' ) ? '1' : '0';
			$cache_key        = 'guess_cpt' . $name . wp_json_encode( $types ) . $page_first . $date_snippet . $can_read_private;
			$found            = false;
			$ret       = $cache->get( $cache_key, $found );

			if ( ! $found ) {
				$ret = $this->find_post_type( $name, $type, $types, $date_snippet, $page_first );
				$cache->set( $cache_key, $ret );
			}
		}

		return $ret;
	}

	private function find_post_type( $name, $type, $types, $date_snippet, $page_first ) {
		$ret    = array( $name, $type, false );
		$where  = $this->wpdb->prepare( 'post_name = %s ', $name );
		$where .= " AND post_type IN ('" . implode( "', '", $types ) . "')";
		$where .= $date_snippet;

		$private_status_clause = current_user_can( 'read_private_posts' )
			? " OR post_status = 'private' "
			: '';

		$res    = $this->wpdb->get_row(
			"
										 SELECT post_type, post_name
										 FROM {$this->wpdb->posts} p
										 LEFT JOIN {$this->wpdb->prefix}icl_translations wpml_translations
											ON wpml_translations.element_id = p.ID
											    AND CONCAT('post_', p.post_type) = wpml_translations.element_type
										        AND " . $this->query_filter->in_translated_types_snippet( false, 'p' ) . "
										 WHERE $where
										    AND ( post_status = 'publish' {$private_status_clause}
										        OR ( post_type = 'attachment'
										             AND post_status = 'inherit' ) )
										    " . $this->order_by_type_and_language_snippet( (bool) $date_snippet, $page_first ) . '
									     LIMIT 1'
		);
		if ( (bool) $res === true ) {
			$ret = array( $res->post_name, $res->post_type, true );
		}

		return $ret;
	}


	private function by_date_snippet( $query ) {
		$snippet = '';
		foreach ( array(
			'year'     => 'YEAR',
			'monthnum' => 'MONTH',
			'day'      => 'DAY',
		) as $index => $time_unit ) {
			if ( (bool) ( $value = $query->get( $index ) ) === true ) {
				$snippet .= $this->wpdb->prepare( " AND {$time_unit}(post_date) = %d ", $value );
			}
		}

		return $snippet;
	}

	private function order_by_type_and_language_snippet( $has_date, $page_first ) {
		$lang_order   = $this->get_ordered_langs();
		$current_lang = array_shift( $lang_order );
		$best_score   = count( $lang_order ) + 2;
		$order_by     = sprintf( "ORDER BY post_type = 'page' %s", $page_first ? 'DESC' : 'ASC' );
		if ( $best_score > 2 ) {
			$order_by .= $this->wpdb->prepare(
				', CASE wpml_translations.language_code WHEN %s THEN %d ',
				$current_lang,
				$best_score
			);
			$score     = $best_score - 2;
			foreach ( $lang_order as $lang_code ) {
				$order_by .= $this->wpdb->prepare( ' WHEN %s THEN %d ', $lang_code, $score );
				$score    -= 1;
			}
			$order_by .= ' ELSE 0 END DESC ';
			if ( $has_date ) {
				$order_by .= ", CASE p.post_type WHEN 'post' THEN 0 ELSE 1 END ";
			}
			$order_by .= ' , ' . $this->order_by_post_type_snippet();
		}

		return $order_by;
	}

	private function order_by_post_type_snippet() {
		$post_types = array(
			'page' => 2,
			'post' => 1,
		);
		$order_by   = ' CASE p.post_type ';
		foreach ( $post_types as $type => $score ) {
			$order_by .= $this->wpdb->prepare( ' WHEN %s THEN %d ', $type, $score );
		}
		$order_by .= ' ELSE 0 END DESC ';

		return $order_by;
	}
}
