<?php

/*
* SPEEDYCACHE
* https://speedycache.com/
* (c) SpeedyCache Team
*/

namespace SpeedyCache;

if(!defined('ABSPATH')){
	die('Hacking Attempt');
}

class CriticalCss extends CommonCss{

	static function generate($urls){
		global $speedycache;

		$time = time() + 50;
		set_time_limit(60);
		
		$api = self::get_endpoint();
		
		if(empty($urls)){
			self::log('speedycache_ccss_logs', 'URL not found');
			return false;
		}

		if(empty($speedycache->license['license']) || empty($speedycache->license['active'])){
			self::log('speedycache_ccss_logs', 'License Not found or inactive, please link your License');
			return false;
		}

		$path = speedycache_cache_path('critical-css/');
		$error = ''; // To hold errors when single Critical CSS is generated
		$attempted_url = []; // Keeping track of URL that have been proccessed to generate CriticalCSS to handle in case of Timeout

		if(!is_dir($path)){
			mkdir($path);
			touch($path . 'index.html');
		}

		$license = $speedycache->license['license'];

		foreach($urls as $url){
			// Handeling php timeout here
			if($time < time()){
				$urls = array_diff($urls, $attempted_url);
				self::schedule($urls);
				return;
			}

			$url = trim($url, '/');

			$attempted_url[] = $url;

			$basename = md5($url);
			$file_name = $path . $basename . '.css';

			$last_request = get_transient('speedycache_critical_css_time');

			// Make the request wait for atleast 10 seconds
			if(!empty($last_request) &&  (time() - $last_request) < 10){
				return;
			}

			set_transient('speedycache_critical_css_time', time(), 90);

			$response = wp_remote_post($api, array(
				'timeout' => 30,
				'body' => array(
					'url' => $url,
					'license' => $license,
				),
				'sslverify' => false,
			));

			if(is_wp_error($response)){
				$error = $response->get_error_message();
				self::log('speedycache_ccss_logs', $response->get_error_message(), $url);
				continue;
			}
			
			$res_code = wp_remote_retrieve_response_code($response);

			if($res_code > 299){
				$body = json_decode(wp_remote_retrieve_body($response), true);
				
				if(!empty($body)){
					$error = sanitize_text_field($body);
					self::log('speedycache_ccss_logs', $error, $url);
					continue;
				}
				$error = __('Unable to extract CriticalCss', 'speedycache');
				self::log('speedycache_ccss_logs', $error, $url);
				continue;
			}

			$body = json_decode(wp_remote_retrieve_body($response), true);
			
			if(empty($body)){
				$error = __('The response recieved is empty.', 'speedycache');
				self::log('speedycache_ccss_logs', __('The response recieved is empty.', 'speedycache'), $url);
				continue;
			}

			if(empty($body['success'])){
				$error = !empty($body['message']) ? wp_strip_all_tags($body['message']) : __('Unable to extract CriticalCss', 'speedycache');
				self::log('speedycache_ccss_logs', !empty($body['message']) ? wp_strip_all_tags($body['message']) : __('Unable to extract CriticalCss', 'speedycache'), $url);
				continue;
			}

			if(empty($body['css']) || strlen($body['css']) < 20){
				$error = __('Was unable to generate Critical CSS', 'speedycache');
				self::log('speedycache_ccss_logs', __('Was unable to generate Critical CSS', 'speedycache'), $url);
				continue;
			}
			
			if(!is_dir($path)){
				mkdir($path);
			}
			
			file_put_contents($file_name, $body['css']);

			self::update_css($url, $body['css']);
			
			self::log('speedycache_ccss_logs', 'success', $url); //Updates the log on success
			
			if(!empty($error)){
				return $error;
			}

			return true;
		}
	}
	
	// Builds up the list to schedule URLs
	static function get_url_list(){
		global $blog_id;
		
		$pages = get_pages(array('child_of' => 0, 'number' => 9));
		
		if(empty($pages)){
			return false;
		}

		$page_to_crawl = [];

		$url = get_home_url(!empty($blog_id) ? $blog_id : null);
		
		if(!empty($url)){
			$page_to_crawl['home'] = $url;
		}

		foreach($pages as $p){
			$page_to_crawl[$p->ID] = get_page_link($p->ID);
		}

		return $page_to_crawl;
	}
	
	// Adds the generated css and asynchronyses the css includes
	static function update_css($url, $css){
		global $speedycache;
		
		if(empty($url)){
			return false;
		}
		
		if(empty($css) && file_exists(speedycache_cache_path('critical-css/') . md5($url) . '.css')){
			$css = file_get_contents(speedycache_cache_path('critical-css/') . md5($url) . '.css');
		}
		
		if(empty($css)){
			return false;
		}

		$css = '<style id="speedycache-generated-criticalcss">'. "\n". wp_strip_all_tags($css) . '</style>';

		$url = parse_url($url);
		$uri = !empty($url['path']) ?  $url['path'] : '';
		$cache_loc = $uri . '/index.html';

		if(empty($cache_loc)){
			return;
		}
		
		if(!empty($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === 'SpeedyCacheTest'){
			$cache_path = speedycache_cache_path('test' . $cache_loc);
		} else {
			$cache_path = speedycache_cache_path('all' . $cache_loc);
		}

		$cache_path = rtrim($cache_path, '/');
		
		// For Desktop
		\SpeedyCache\CriticalCss::update_cached($cache_path, $css);

		if(!empty($speedycache->options['mobile_theme']) && $_SERVER['HTTP_USER_AGENT'] !== 'SpeedyCacheTest'){
			$cache_mobile = speedycache_cache_path('mobile-cache' . $cache_loc);
			
			// For Mobile Cache
			if(file_exists($cache_mobile)){
				\SpeedyCache\CriticalCss::update_cached($cache_mobile, $css);
			}	
		}

	}
	
	// Updates the content of the cached file
	static function update_content($content, $css){
		
		if(strpos($content, 'speedycache-generated-criticalcss') !== FALSE){
			$content = preg_replace('/<style id="speedycache-generated-criticalcss">(.*)<\/style>/sU', $css, $content);
		} else{
			$content = preg_replace('#</title>#iU', "</title>\n". $css, $content);
		}

		$content = \SpeedyCache\CriticalCss::make_css_defer($content);
		
		return $content;
	}
	
	/**
	 * This extracts the stylesheet links and convertes them to preload to make them async,
	 * and add the stylesheet links in noscript if js is disabled in some browsers
	 */
	static function make_css_defer($content){
		
		$css_links = '/(?=<link[^>]*\s(rel\s*=\s*[\'"]stylesheet["\']))<link[^>]*\shref\s*=\s*[\'"]([^\'"]+)[\'"](.*)>/iU';
		
		preg_match_all($css_links, $content, $matches, PREG_SET_ORDER, 0);

		if(empty($matches)){
			return $content;
		}
		
		$noscript_wrap = '<noscript>';

		foreach($matches as $tag){
			$preload = str_replace('stylesheet', 'preload', $tag[1]);
			$onload = preg_replace('~' . preg_quote($tag[3], '~') . '~iU', ' as="style" onload="" ' . $tag[3] . '>', $tag[3]);

			$new_tag = str_replace($tag[3] . '>', $onload, $tag[0]);
			$new_tag = str_replace($tag[1], $preload, $new_tag);
			$new_tag = str_replace('onload=""', 'onload="this.onload=null;this.rel=\'stylesheet\'"', $new_tag);
			$new_tag = preg_replace('/(id\s*=\s*[\"\'](?:[^\"\']*)*[\"\'])/i', '', $new_tag);

			$content = str_replace($tag[0], $new_tag, $content);
			
			$noscript_wrap .= $tag[0];
		}
		
		$noscript_wrap .= '</noscript>';
		
		$content = str_replace($noscript_wrap, '', $content);
		
		return str_replace('</body>', $noscript_wrap . '</body>', $content);

	}
	
	static function status_modal(){
		$html = '<!--SpeedyCache Critical CSS Logs Modal Starts Here-->
	<div modal-id="speedycache_critical_css" class="speedycache-modal">
		<div class="speedycache-modal-wrap">
			<div class="speedycache-modal-header">
				<div>'.esc_html__('Critical Cache Logs', 'speedycache').'</div>
				<div title="Close Modal" class="speedycache-close-modal">
					<span class="dashicons dashicons-no"></span>
				</div>
			</div>
			<div class="speedycache-modal-content" style="min-height:50vh;">
				<div class="speedycache-critical-css-status">';
			
			$generate_ccss = get_option('speedycache_ccss_logs', []);
			
			$scheduled = self::get_schedule(array('speedycache_generate_ccss'));
		
			if(!empty($scheduled)){
				$time = 'now';
				if(!empty($scheduled[0]['time']) && ($scheduled[0]['time'] - time()) > 0){
					$time = 'in ' . ($scheduled[0]['time'] - time()) . 's';
				}

				$html .= '<p style="color:rgb(1, 67, 97); background-color: rgb(229, 246, 253); padding: 10px; border-radius: 6px; font-family: monospace;">'. esc_html__('A process has been scheduled and will be executed', 'speedycache'). ' <strong>' . esc_html($time).'</strong></p>';
			}

			if(!is_array($generate_ccss) || count($generate_ccss) < 1){
				$html .= '<span>'.esc_html__('No Logs found for CriticalCss', 'speedycache').'</span>';
			} else {
				$html .='<table style="margin:auto; width: 100%;">
					<thead>
						<tr>
							<th class="speedycache-table-hitem" scope="col">'.esc_html__('Time', 'speedycache').'</th>
							<th class="speedycache-table-hitem" scope="col">'. esc_html__('URLs', 'speedycache').'</th>
							<th class="speedycache-table-hitem" scope="col">'. esc_html__('Status', 'speedycache').'</th>
						</tr>
					</thead>
					<tbody>';
					
					$generate_ccss = array_reverse($generate_ccss);
					
					foreach($generate_ccss as $url => $status){
						$parsed_url = wp_parse_url($url);
						$path = !empty($parsed_url['path']) ? $parsed_url['path'] : '/';
						
						if($status['message'] == 'success'){
							$status_html = '<span class="dashicons dashicons-yes-alt" style="color:	#198754;" title="Success"></span>';
						} else {
							$status_html = '<div class="speedycache-tt"><span class="dashicons dashicons-info" style="color:#DC3545;"></span><span class="speedycache-tt-text">'.esc_html($status['message']).'</span></div>';
						}
						
						$html .= '<tr><td class="speedycache-table-item">'.esc_html($status['time']).'</td>
						<td class="speedycache-table-item">'.esc_html($path).'</td>
						<td class="speedycache-table-item" style="text-align:right;">'.wp_kses_post($status_html).'</td></tr>';
					}

				$html .= '</tbody>
				</table>';
			}
			
			$html .= '</div>
		</div>
	</div>
</div>';

		return $html;
	}

}
