<?php
/**
 * Main Link class.
 *
 * @package dvic\classes
 */

/**
 * Main Link class.
 */
final class Link {
	/**
	 * The URL of the link.
	 *
	 * @var string $url
	 */
	private $url;

	/**
	 * The URL of the link.
	 *
	 * @var $video_id
	 */
	private $video_id = null;

	/**
	 * The tube where the link comes from.
	 *
	 * @var $tube_id
	 */
	private $tube_id = null;

	/**
	 * The config of the tube.
	 *
	 * @var $tube_config
	 */
	private $tube_config = null;

	/**
	 * Class constructor.
	 *
	 * @param string $url The url of the link.
	 */
	public function __construct( $url ) {
		$this->url = $this->fix_url_scheme( $url );
		$this->extract_data();
	}

	/**
	 * Get link url.
	 *
	 * @return string The url.
	 */
	public function get_url() {
		return $this->url;
	}

	/**
	 * Fix the url scheme (add https when link starts with '//...').
	 *
	 * @param string $url The url to fix.
	 *
	 * @return string The url fixed if needed.
	 */
	private function fix_url_scheme( $url ) {
		$parsed_url = wp_parse_url( $url );
		if ( ! isset( $parsed_url['scheme'] ) ) {
			$url = 'https:' . $url;
		}
		return $url;
	}

	/**
	 * Extract video_id and tube from the link.
	 *
	 * @return bool True if some regex pattern matched, false if not.
	 */
	private function extract_data() {
		$tubes_config = $this->get_tubes_config();
		foreach ( $tubes_config as $tube_id => $tube_configs ) {
			foreach ( $tube_configs as $tube_config ) {
				preg_match( $tube_config['regex'], $this->url, $match );
				if ( ! empty( $match ) ) {
					$this->tube_id     = $tube_id;
					$this->tube_config = $tube_config;
					$this->video_id    = $match[1];
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Returns the video_id detected in the link.
	 *
	 * @return mixed The video_id from the link if some has been found, null if not.
	 */
	public function get_video_id() {
		return $this->video_id;
	}

	/**
	 * Returns the tube detected in the link.
	 *
	 * @return mixed The tube from the link if some has been found, null if not.
	 */
	public function get_tube_id() {
		return $this->tube_id;
	}

	/**
	 * Detect if the link has a video extention.
	 *
	 * @return bool True if the link has a video extention, false if not.
	 */
	public function has_video_extention() {
		$videos_extentions = array( 'avi', 'hls', 'mov', 'mp4', 'ogg', 'webm' );
		return in_array( $this->get_extention(), $videos_extentions, true );
	}

	/**
	 * Detect if the link has an image extention.
	 *
	 * @return bool True if the link has an image extention, false if not.
	 */
	public function has_image_extention() {
		$videos_extentions = array( 'jpg', 'png', 'gif', 'webp', 'tiff', 'bmp', 'svg', 'ai', 'eps', 'pdf' );
		return in_array( $this->get_extention(), $videos_extentions, true );
	}

	/**
	 * Returns the link extention.
	 */
	public function get_extention() {
		$parse_url = wp_parse_url( $this->url );
		$url_path  = explode( '.', $parse_url['path'] );
		$extention = end( $url_path );
		return strtolower( $extention );
	}

	/**
	 * Detect if the link is broken or not.
	 *
	 * @return bool True if the link is broken, false if not.
	 */
	public function is_broken() {
		// Regular link.
		if ( null === $this->get_tube_id() ) {
			return $this->check_by_http_status_code();
		}
		// Tube link.
		switch ( $this->tube_config['method'] ) {
			case 'api':
				return $this->check_by_api_response();
			case 'find_string':
				return $this->check_by_string_in_content();
			case 'http_status':
				return $this->check_by_http_status_code();
			case 'dead_site':
				return true;
			default:
				return false;
		}
	}

	/**
	 * Check if current link is broken or not depending on its http call status.
	 * - Uses wp_remote_head() for videos and images urls to speed up process.
	 * - Uses wp_remote_get() for all other urls types to prevent false 200 header response code.
	 *
	 * @return bool True if it is broken, false if not.
	 */
	public function check_by_http_status_code() {
		$remote_args = isset( $this->tube_config['remote_args'] ) ? $this->tube_config['remote_args'] : array();
		$response    = wp_remote_head( $this->url, $this->get_remote_args( $remote_args ) );
		if ( is_wp_error( $response ) ) {
			return true;
		}
		// Test for xvideos for inactive videos, which are different from broken videos.
		if ( isset( $response['headers'], $response['headers']['location'] ) && in_array( $response['headers']['location'], array( '/video-inactive' ), true ) ) {
			return true;
		}
		return ! in_array( wp_remote_retrieve_response_code( $response ), array( 200, 204, 301, 302 ), true );
	}

	/**
	 * Check if current link is broken or not depending on an api call response.
	 * Transforms array from api call to iterator to look for the active code node.
	 *
	 * @return bool True if it is broken, false if not.
	 */
	public function check_by_api_response() {
		$remote_args = isset( $this->tube_config['remote_args'] ) ? $this->tube_config['remote_args'] : array();
		$response    = wp_remote_get( $this->tube_config['api_endpoint'] . $this->video_id, $this->get_remote_args( $remote_args ) );
		if ( 200 !== $response['response']['code'] ) {
			return true;
		}
		$api_response = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( ! is_array( $api_response ) ) {
			return true;
		}
		$api_response_iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator( $api_response ) );
		$active_code_node      = end( $this->tube_config['api_response_structure'] );
		$active_code_value     = $this->tube_config['api_active_code'];
		foreach ( $api_response_iterator as $it_node => $it_value ) {
			if ( $it_node === $active_code_node && $it_value === $active_code_value ) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Check if current link is broken or not depending on a string to find in the content of the page it links.
	 *
	 * @return bool True if it is broken, false if not.
	 */
	public function check_by_string_in_content() {
		$remote_args = isset( $this->tube_config['remote_args'] ) ? $this->tube_config['remote_args'] : array();
		$response    = wp_remote_get( $this->tube_config['string_endpoint'], $this->get_remote_args( $remote_args ) );
		$html        = wp_remote_retrieve_body( $response );
		if ( empty( $html ) ) {
			return true;
		}
		$document             = new DiDom\Document( $html );
		$count_string_to_find = $document->count( $this->tube_config['string_to_find'] );
		if ( $count_string_to_find === $this->tube_config['string_active_count'] ) {
			return false;
		}
		return true;
	}

	/**
	 * Get remote args to call wp_remote_get().
	 *
	 * @param array $remote_args The remote args to merge with default ones.
	 *
	 * @return array Request arguments to call wp_remote_get().
	 */
	private function get_remote_args( $remote_args = array() ) {
		$user_agent   = \Campo\UserAgent::random(
			array(
				'device_type' => 'Desktop',
				'agent_name'  => array(
					'Baiduspider',
					'Bunjalloo',
					'Googlebot-Image',
					'Googlebot',
					'Teoma',
					'PlayStation 3',
					'PlayStation Portable',
					'Firefox',
					'Bingbot',
					'IE Mobile',
					'Yahoo! Slurp China',
					'Yahoo! Slurp',
					'YandexBot',
					'YandexImages',
					'Safari',
					'Seamonkey',
					'Midori',
					'NetFront',
					'Pale Moon',
					'Konqueror',
					'Iceweasel',
					'konqueror',
				),
			)
		);
		$default_args = array(
			'timeout'    => 10,
			'sslverify'  => false,
			'user-agent' => $user_agent,
		);
		$merged_args  = array_merge(
			$default_args,
			array_intersect_key( $remote_args, $default_args )
		);
		return $merged_args;
	}

	/**
	 * Get Tubes configs.
	 *
	 * @return array An array with all tubes configs.
	 */
	public function get_tubes_config() {
		return array(
			'eporner'   => array(
				'embed' => array(
					'regex'               => '/^.+\.eporner\.com\/embed\/(.+)\/?$/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => 'h1',
					'string_active_count' => 1,
				),
				'site'  => array(
					'regex'               => '/^.+\.eporner\.com\/.*\/(.+)\/.*\/?$/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => '#deletedfile',
					'string_active_count' => 0,
				),
			),
			'porn.com'  => array(
				'embed' => array(
					'regex'  => '/^.+\.porn\.com\/videos\/embed\/(.+)\/?$/mU',
					'method' => 'dead_site',
				),
			),
			'pornhub'   => array(
				'embed' => array(
					'regex'                  => '/^.+\.pornhub\.com\/embed\/(.+)\/?$/mU',
					'method'                 => 'api',
					'api_endpoint'           => 'https://www.pornhub.com/webmasters/is_video_active?id=',
					'api_response_structure' => array( 'active', 'is_active' ),
					'api_active_code'        => '1',
				),
				'site'  => array(
					'regex'                  => '/^.+\.pornhub\.com\/view_video.php\?viewkey=(ph.+)\/?$/mU',
					'method'                 => 'api',
					'api_endpoint'           => 'https://www.pornhub.com/webmasters/is_video_active?id=',
					'api_response_structure' => array( 'active', 'is_active' ),
					'api_active_code'        => '1',
				),
			),
			'redtube'   => array(
				'embed' => array(
					'regex'                  => '/^.+(?<=embed\.)redtube\.com\/\?id=([0-9]++)/mU',
					'method'                 => 'api',
					'api_endpoint'           => 'https://api.redtube.com/?data=redtube.Videos.isVideoActive&video_id=',
					'api_response_structure' => array( 'active', 'is_active' ),
					'api_active_code'        => true,
				),
				'site'  => array(
					'regex'  => '/^.+(?<!embed\.)redtube\.com\/([0-9]++)/mU',
					'method' => 'http_status',
				),
			),
			'spankwire' => array(
				'embed' => array(
					'regex'  => '/^.+\.spankwire\.com\/EmbedPlayer\.aspx\?ArticleId=([0-9]+)\/?$/mU',
					'method' => 'dead_site',
				),
			),
			'tube8'     => array(
				'site'  => array(
					'regex'               => '/^.+\.tube8\.com\/(?!embed).+\/([0-9]+)\/?$/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => 'div[id=videoWatchPage]',
					'string_active_count' => 1,
				),
				'embed' => array(
					'regex'               => '/^.+\.tube8\.com\/embed\/.+\/([0-9]+)\/?$/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => 'body[data-esp-node=embed_page]',
					'string_active_count' => 1,
				),
			),
			'pornone'   => array(
				'image' => array(
					'regex'               => '/^.+th-.+\.pornone\.com\/t\/\d+\/([0-9]++)\/.+\.jpg/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->prepare_pornone_endpoint_for_image(),
					'string_to_find'      => '#playerholder',
					'string_active_count' => 1,
				),
				'embed' => array(
					'regex'               => '/^.+pornone\.com\/embed\/([0-9]++)/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => '#playerholder',
					'string_active_count' => 1,
				),
				'site'  => array(
					'regex'               => '/^.+pornone\.com\/.+\/.+\/([0-9]++)/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => '#pornone-video-player',
					'string_active_count' => 1,
				),
			),
			'xhamster'  => array(
				'site'  => array(
					'regex'               => '/^.+xhamster\.com\/videos\/.+-([0-9a-zA-Z]++)\/?$/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => 'h1',
					'string_active_count' => 1,
					'remote_args'         => array(
						'redirection' => 0,
						'user-agent'  => '',
					),
				),
				'embed' => array(
					'regex'               => '/^.+xhamster\.com\/embed\/([0-9a-zA-Z]++)\/?$/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => 'div[id=player]',
					'string_active_count' => 1,
					'remote_args'         => array(
						'redirection' => 0,
						'user-agent'  => '',
					),
				),
			),
			'xvideos'   => array(
				'embed' => array(
					'regex'               => '/^.+xvideos\.com\/embedframe\/([0-9]++)/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => 'h3',
					'string_active_count' => 0,
				),
				'site'  => array(
					'regex'               => '/^.+xvideos\.com\/video([0-9]++)/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->prepare_xvideos_endpoint_for_site(),
					'string_to_find'      => 'h3',
					'string_active_count' => 0,
				),
			),
			'youporn'   => array(
				'embed' => array(
					'regex'       => '/^.+youporn\.com\/embed\/([0-9]++)/mU',
					'method'      => 'http_status',
					'remote_args' => array(
						'redirection' => 0,
					),
				),
				'site'  => array(
					'regex'       => '/^.+youporn\.com\/watch\/([0-9]++)/mU',
					'method'      => 'http_status',
					'remote_args' => array(
						'redirection' => 0,
					),
				),
			),
			'openload'  => array(
				'embed' => array(
					'regex'               => '/openload/mU',
					'method'              => 'find_string',
					'string_endpoint'     => $this->url,
					'string_to_find'      => 'div[class=content-blocked]',
					'string_active_count' => 0,
				),
			),
			'youtube'   => array(
				'embed' => array(
					'regex'                  => '/^.+youtu\.?b.+\/(?:embed\/)([0-9a-zA-Z_.-]++)/mU',
					'method'                 => 'api',
					'api_endpoint'           => 'https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=' . $this->get_video_id(),
					'api_response_structure' => array( 'type' ),
					'api_active_code'        => 'video',
				),
				'site'  => array(
					'regex'                  => '/^.+youtu\.?b.+\/(?:watch\?v=)([0-9a-zA-Z_.-]++)/mU',
					'method'                 => 'api',
					'api_endpoint'           => 'https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=' . $this->get_video_id(),
					'api_response_structure' => array( 'type' ),
					'api_active_code'        => 'video',
				),
			),
		);
	}

	/**
	 * Prepare the endpoint for pornone image check.
	 * Because Pornone broken image response is 200 with no html.
	 *
	 * @return string The Porneone embed page created from the current image url or empty string if no match.
	 */
	private function prepare_pornone_endpoint_for_image() {
		$regex = '/^.+th-.+\.pornone\.com\/t\/\d+\/([0-9]++)\/.+\.jpg/mU';
		preg_match( $regex, $this->url, $match );
		if ( empty( $match ) ) {
			return '';
		}
		$video_id = $match[1];
		return 'https://pornone.com/embed/' . $video_id;
	}

	/**
	 * Prepare the endpoint for xvideos site check.
	 * Because xvideos broken videos from site response is 301 to 404.
	 *
	 * @return string The Porneone embed page created from the current image url or empty string if no match.
	 */
	private function prepare_xvideos_endpoint_for_site() {
		$regex = '/^.+xvideos\.com\/video([0-9]++)/mU';
		preg_match( $regex, $this->url, $match );
		if ( empty( $match ) ) {
			return '';
		}
		$video_id = $match[1];
		return 'https://xvideos.com/embedframe/' . $video_id;
	}
}
