<?php
namespace JExtstore\Component\JRealtimeAnalytics\Administrator\Model;
/** 
 * @package JREALTIMEANALYTICS::AJAXSERVER::components::com_jrealtimeanalytics 
 * @subpackage models
 * @author Joomla! Extensions Store
 * @copyright (C)2014 Joomla! Extensions Store
 * @license GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html  
 */
defined('_JEXEC') or die('Restricted access');
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Installer\Installer;
use Joomla\CMS\Installer\InstallerHelper;
use JExtstore\Component\JRealtimeAnalytics\Administrator\Model\ServerstatsModel;
use JExtstore\Component\JRealtimeAnalytics\Administrator\Framework\Http;
use JExtstore\Component\JRealtimeAnalytics\Administrator\Framework\Http\Transport\Socket;
use JExtstore\Component\JRealtimeAnalytics\Administrator\Framework\Http\Transport\Curl;
use JExtstore\Component\JRealtimeAnalytics\Administrator\Framework\Helpers\Language;
use JExtstore\Component\JRealtimeAnalytics\Administrator\Framework\Graph\Generators\Charts as GraphGeneratorsCharts;
use JExtstore\Component\JRealtimeAnalytics\Administrator\Framework\Model as JRealtimeModel;
use JExtstore\Component\JRealtimeAnalytics\Administrator\Framework\Exception as JRealtimeException;

/**
 * Ajax Server model responsibilities
 *
 * @package JREALTIMEANALYTICS::AJAXSERVER::components::com_jrealtimeanalytics  
 * @subpackage models
 * @since 2.0
 */
interface IAjaxserverModel {
	public function loadAjaxEntity($id, $param, $DIModels) ;
}

/** 
 * Classe che gestisce il recupero dei dati per il POST HTTP
 * @package JREALTIMEANALYTICS::AJAXSERVER::components::com_jrealtimeanalytics  
 * @subpackage models
 * @since 1.0
 */
class AjaxserverModel extends JRealtimeModel implements IAjaxserverModel {
	/**
	 * Get license informations about this user subscription license email code
	 * Use the RESTFul interface API on the remote License resource
	 *
	 * @static
	 * @access private
	 * @param Object[] $additionalModels Array for additional injected models type hinted by interface
	 * @return Object
	 */
	private function getLicenseStatus($additionalModels = null) {
		// Get email license code
		$code = ComponentHelper::getParams('com_jrealtimeanalytics')->get('registration_email', null);
	
		// Instantiate HTTP client
		$HTTPClient = new Http();
	
		/*
		 * Status domain code
		 * Remote API Call
		 */
		$headers = array('Accept'=>'application/json', 'User-agent' => 'JRealtime Analytics updater');
		if($code) {
			try {
				$prodCode = 'jrealtime';
				$cdFuncUsed = 'str_' . 'ro' . 't' . '13';
				$HTTPResponse = $HTTPClient->get($cdFuncUsed('uggc' . '://' . 'fgberwrkgrafvbaf' . '.bet') . "/option,com_easycommerce/action,licenseCode/email,$code/productcode,$prodCode", $headers);
			} catch (\Exception $e) {
				$HTTPResponse = new \stdClass();
				$HTTPResponse->body = '{"success":false,"reason":"connection_error","details":"' . $e->getMessage() . '"}';
			}
		} else {
			$HTTPResponse = new \stdClass();
			$HTTPResponse->body = '{"success":false,"reason":"nocode_inserted"}';
		}
			
		// Deserializing della response
		try {
			$objectHTTPResponse = json_decode($HTTPResponse->body);
			if(!is_object($objectHTTPResponse)) {
				throw new \Exception('decoding_error');
			}
		} catch (\Exception $e) {
			$HTTPResponse = new \stdClass();
			$HTTPResponse->body = '{"success":false,"reason":"' . $e->getMessage() . '"}';
			$objectHTTPResponse = json_decode($HTTPResponse->body);
		}
	
		return $objectHTTPResponse;
	}
	
	/**
	 * Perform the asyncronous update of the component
	 * 1- Dowload the remote update package file
	 * 2- Use the Joomla installer to install it
	 * 3- Return status to the js app
	 *
	 * @static
	 * @access private
	 * @param Object[] $additionalModels Array for additional injected models type hinted by interface
	 * @return Object
	 */
	private function downloadComponentUpdate($additionalModels = null) {
		// Response JSON object
		$response = new \stdClass ();
		$cdFuncUsed = 'str_' . 'ro' . 't' . '13';
		$ep = $cdFuncUsed('uggc' . '://' . 'fgberwrkgrafvbaf' . '.bet' . '/XZY1402ZQOFnifs3243564832knfnxj35td1rtt1404q.ugzy');
		$file_path = $this->app->getConfig()->get('tmp_path', '/tmp') . '/KML1402MDBSavsf3243564832xasakw35gq1egg1404d.zip';
	
		try {
			// Ensure CURL support
			if (! function_exists ( 'curl_init' )) {
				throw new JRealtimeException ( Text::_ ( 'COM_JREALTIME_CURL_NOT_SUPPORTED' ), 'error' );
			}
	
			// Firstly test if the server is up and HTTP 200 OK
			$ch = curl_init($ep);
			curl_setopt( $ch, CURLOPT_NOBODY, true );
			curl_setopt( $ch, CURLOPT_HEADER, false );
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, false );
			curl_setopt( $ch, CURLOPT_MAXREDIRS, 3 );
			curl_exec( $ch );
	
			$headerInfo = curl_getinfo( $ch );
			if($headerInfo['http_code'] != 200 || !$headerInfo['download_content_length']) {
				throw new JRealtimeException ( Text::_ ( 'COM_JREALTIME_ERROR_DOWNLOADING_REMOTE_FILE' ), 'error' );
			}
	
			// 1- Download the remote update package file and put in local file
			$fp = fopen ($file_path, 'w+');
			$ch = curl_init();
			curl_setopt( $ch, CURLOPT_URL, $ep );
			// curl_setopt( $ch, CURLOPT_BINARYTRANSFER, true );
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, false );
			curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
			curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 60 );
			curl_setopt( $ch, CURLOPT_FILE, $fp );
			curl_exec( $ch );
			fclose( $fp );
	
			if (!filesize($file_path)) {
				throw new JRealtimeException ( Text::_ ( 'COM_JREALTIME_ERROR_WRITING_LOCAL_FILE' ), 'error' );
			}
	
			// All went well
			$response->result = true;
		} catch ( JRealtimeException $e ) {
			$response->result = false;
			$response->exception_message = $e->getMessage ();
			$response->errorlevel = $e->getExceptionLevel();
			return $response;
		} catch ( \Exception $e ) {
			$jrealtimeException = new JRealtimeException ( Text::sprintf ( 'COM_JREALTIME_ERROR_UPDATING_COMPONENT', $e->getMessage () ), 'error' );
			$response->result = false;
			$response->exception_message = $jrealtimeException->getMessage ();
			$response->errorlevel = $jrealtimeException->getExceptionLevel();
			return $response;
		}
	
		return $response;
	}
	
	/**
	 * Query the Google PageSpeed API to retrieve stats and Core Web Vitals
	 *
	 * @static
	 * @access private
	 * @param string $linkUrl
	 * @param Object[] $additionalModels Array for additional injected models type hinted by interface
	 * @return Object
	 */
	private function getPageSpeedScore($linkUrl, $additionalModels = null) {
		// Response JSON object
		$response = new \stdClass ();
		$response->result = false;
		$cParams = $this->getComponentParams();
		
		// Check if the Google Pagespeed score must be retrieved as well
		if($cParams->get('pagespeed_insights_analysis', 1)) {
			// Ensure that at least a language is available for the backend and locale sent to Google otherwise the API fails
			$language = Language::getCurrentSefLanguage();
			$locale = $language ? $language : 'en';
			
			// Build the purified domain to scrape using the host only
			$hostDomain = rawurlencode ( $linkUrl );
			$customApiKey = trim($cParams->get ( 'pagespeed_api_key', ''));
			$apiKey = $customApiKey ? $customApiKey : 'AIzaSyB1ZfIsasdwAUev0WTanLioTUNmBsuipE4';
			$strategy = $cParams->get('pagespeed_analysis_strategy', 'desktop');
			$url = "https://content.googleapis.com/pagespeedonline/v5/runPagespeed?url=$hostDomain&key=$apiKey&strategy=$strategy&category=performance&locale=" . $locale;
			
			try {
				// Fetch remote data to scrape
				$httpTransport = $cParams->get('pagespeed_http_transport', 'curl') == 'socket' ? new Socket () : new Curl ();
				$connectionAdapter = new Http ( $httpTransport, $cParams );
				$httpResponse = $connectionAdapter->get ( $url );
				
				// Check if HTTP status code is 200 OK
				if ($httpResponse->code == 200 && $httpResponse->body) {
					$response->result = true;
					
					$decodedApiResponse = json_decode($httpResponse->body, true);
					if(is_array($decodedApiResponse)) {
						// Calculate the score category, range, colors for labels and sliders
						$response->pagespeed = isset($decodedApiResponse['lighthouseResult']['categories']['performance']) ? (int)($decodedApiResponse['lighthouseResult']['categories']['performance']['score'] * 100) : -1;
						
						// Largest Contentful Paint, loadingExperience if available, fallback to live metrics
						$response->pagespeed_lcp_score = isset($decodedApiResponse['loadingExperience']['metrics']['LARGEST_CONTENTFUL_PAINT_MS']) ? number_format($decodedApiResponse['loadingExperience']['metrics']['LARGEST_CONTENTFUL_PAINT_MS']['percentile'] / 1000, 1) : number_format($decodedApiResponse['lighthouseResult']['audits']['largest-contentful-paint']['numericValue'] / 1000, 1);
						if(isset($decodedApiResponse['loadingExperience']['metrics']['LARGEST_CONTENTFUL_PAINT_MS'])) {
							$response->pagespeed_lcp_score_vote = $decodedApiResponse['loadingExperience']['metrics']['LARGEST_CONTENTFUL_PAINT_MS']['category'];
						} else {
							if($response->pagespeed_lcp_score <= 2.5) {
								$response->pagespeed_lcp_score_vote = 'FAST';
							} elseif($response->pagespeed_lcp_score > 2.5 && $response->pagespeed_lcp_score <= 4.0) {
								$response->pagespeed_lcp_score_vote = 'AVERAGE';
							} elseif($response->pagespeed_lcp_score > 4.0) {
								$response->pagespeed_lcp_score_vote = 'LOW';
							}
						}
						
						// Load only if the loadingExperience is available as FID is only available as a real-use field metric
						if(isset($decodedApiResponse['loadingExperience']['metrics']['INTERACTION_TO_NEXT_PAINT'])) {
							$response->pagespeed_fid_score = $decodedApiResponse['loadingExperience']['metrics']['INTERACTION_TO_NEXT_PAINT']['percentile'];
							$response->pagespeed_fid_score_vote = $decodedApiResponse['loadingExperience']['metrics']['INTERACTION_TO_NEXT_PAINT']['category'];
						}
						
						// Cumulative Layout Shift, loadingExperience if available, fallback to live metrics
						$response->pagespeed_cls_score = isset($decodedApiResponse['loadingExperience']['metrics']['CUMULATIVE_LAYOUT_SHIFT_SCORE']) ? $decodedApiResponse['loadingExperience']['metrics']['CUMULATIVE_LAYOUT_SHIFT_SCORE']['percentile'] / 100 : number_format($decodedApiResponse['lighthouseResult']['audits']['cumulative-layout-shift']['numericValue'], 3);
						if(isset($decodedApiResponse['loadingExperience']['metrics']['CUMULATIVE_LAYOUT_SHIFT_SCORE']['category'])) {
							$response->pagespeed_cls_score_vote = $decodedApiResponse['loadingExperience']['metrics']['CUMULATIVE_LAYOUT_SHIFT_SCORE']['category'];
						} else {
							if($response->pagespeed_cls_score <= 0.1) {
								$response->pagespeed_cls_score_vote = 'FAST';
							} elseif($response->pagespeed_cls_score > 0.1 && $response->pagespeed_cls_score <= 0.25) {
								$response->pagespeed_cls_score_vote = 'AVERAGE';
							} elseif($response->pagespeed_cls_score > 0.25) {
								$response->pagespeed_cls_score_vote = 'LOW';
							}
						}
					}
				}
			} catch ( \Exception $e ) {
				// Go on with returning an empty object
			}
		}
		
		return $response;
	}
	
	/**
	 * Perform the asyncronous update of the component
	 * 1- Dowload the remote update package file
	 * 2- Use the Joomla installer to install it
	 * 3- Return status to the js app
	 *
	 * @static
	 * @access private
	 * @param Object[] $additionalModels Array for additional injected models type hinted by interface
	 * @return Object
	 */
	private function installComponentUpdate($additionalModels = null) {
		// Response JSON object
		$response = new \stdClass ();
		$file_path = $this->app->getConfig()->get('tmp_path', '/tmp') . '/KML1402MDBSavsf3243564832xasakw35gq1egg1404d.zip';
	
		try {
			// Unpack the downloaded package file.
			$package = InstallerHelper::unpack($file_path, true);
			if(!$package) {
				throw new JRealtimeException ( Text::_ ( 'COM_JREALTIME_ERROR_EXTRACTING_UPDATES' ), 'error' );
			}
	
			// 2- Use the Joomla installer to install it
			// New plugin installer
			$database = Factory::getContainer()->get('DatabaseDriver');
			$updateInstaller = new Installer ();
			$updateInstaller->setDatabase($database);
			if (! $updateInstaller->install ( $package['extractdir'] )) {
				throw new JRealtimeException ( Text::_ ( 'COM_JREALTIME_ERROR_INSTALLING_UPDATES' ), 'error' );
			}
	
			// Delete dirty files and folder
			unlink($file_path);
			$it = new \RecursiveDirectoryIterator($package['extractdir'], \RecursiveDirectoryIterator::SKIP_DOTS);
			$files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
			foreach($files as $file) {
				if ($file->isDir()){
					@rmdir($file->getRealPath());
				} else {
					unlink($file->getRealPath());
				}
			}
			// Delete the now empty folder
			@rmdir($package['extractdir']);
	
			// All went well
			$response->result = true;
		} catch ( JRealtimeException $e ) {
			$response->result = false;
			$response->exception_message = $e->getMessage ();
			$response->errorlevel = $e->getExceptionLevel();
			return $response;
		} catch ( \Exception $e ) {
			$jrealtimeException = new JRealtimeException ( Text::sprintf ( 'COM_JREALTIME_ERROR_UPDATING_COMPONENT', $e->getMessage () ), 'error' );
			$response->result = false;
			$response->exception_message = $jrealtimeException->getMessage ();
			$response->errorlevel = $jrealtimeException->getExceptionLevel();
			return $response;
		}
	
		return $response;
	}
	
	/**
	 * Generate native Serverstats AI analysis through AJAX endpoint.
	 *
	 * @access private
	 * @param mixed $param
	 * @param Object[] $additionalModels
	 * @return \stdClass
	 */
	private function getServerstatsAIAnalysis($param = null, $additionalModels = null) {
		$response = new \stdClass();
		$response->result = false;
		$response->analysis = '';
		$response->message = '';
		
		try {
			$cParams = $this->getComponentParams();
			if(!(int)$cParams->get('ai_analysis_enabled', 0)) {
				throw new \RuntimeException(Text::_('COM_JREALTIME_AI_ANALYSIS_DISABLED'));
			}
			
			$provider = trim((string)$cParams->get('ai_provider', 'gemini'));
			if($provider === 'openai' && !trim((string)$cParams->get('ai_openai_api_key', ''))) {
				throw new \RuntimeException(Text::_('COM_JREALTIME_AI_OPENAI_MISSING_APIKEY'));
			}
			if($provider !== 'openai' && !trim((string)$cParams->get('ai_gemini_api_key', ''))) {
				throw new \RuntimeException(Text::_('COM_JREALTIME_AI_GEMINI_MISSING_APIKEY'));
			}
			
			$fromPeriod = isset($param->fromperiod) ? (string)$param->fromperiod : date('Y-m-d');
			$toPeriod = isset($param->toperiod) ? (string)$param->toperiod : date('Y-m-d');
			$graphTheme = isset($param->graphtheme) ? (string)$param->graphtheme : 'Universal';
			$statsReport = isset($param->statsreport) ? (string)$param->statsreport : 'full';
			$periodLabel = trim($fromPeriod . ' - ' . $toPeriod);
			
			$serverstatsModel = new ServerstatsModel();
			$serverstatsModel->setState('fromPeriodOriginal', $fromPeriod);
			$serverstatsModel->setState('toPeriodOriginal', $toPeriod);
			$serverstatsModel->setState('fromPeriod', $fromPeriod);
			$serverstatsModel->setState('toPeriod', $toPeriod);
			$serverstatsModel->setState('graphTheme', $graphTheme);
			$serverstatsModel->setState('statsReport', $statsReport);
			$serverstatsModel->setState('task', 'display');
			$graphGenerator = new GraphGeneratorsCharts($graphTheme);
			$serverstatsModel->setGraphRenderer($graphGenerator);
			
			$statsData = $serverstatsModel->getData();
			if(empty($statsData)) {
				throw new \RuntimeException(Text::_('COM_JREALTIME_AI_NO_CONTENT'));
			}
			
			$nativeReportContent = $serverstatsModel->buildAINativeReportContent($statsData);
			if(!$nativeReportContent) {
				throw new \RuntimeException(Text::_('COM_JREALTIME_AI_NO_CONTENT'));
			}
			
			$analysis = $serverstatsModel->generateAIAnalysis($nativeReportContent, $periodLabel, 'manual');
			if(!$analysis) {
				throw new \RuntimeException(Text::_('COM_JREALTIME_AI_NO_CONTENT'));
			}
			
			$response->result = true;
			$response->analysis = $analysis;
		} catch (\Exception $e) {
			$response->message = $e->getMessage();
		}
		
		return $response;
	}
	
	/**
	 * Mimic an entities list, as ajax calls arrive are redirected to loadEntity public responsibility to get handled
	 * by specific subtask. Responses are returned to controller and encoded from view over HTTP to JS client
	 * 
	 * @access public 
	 * @param string $id Rappresenta l'op da eseguire tra le private properties
	 * @param mixed $param Parametri da passare al private handler
	 * @param Object[]& $DIModels
	 * @return Object& $utenteSelezionato
	 */
	public function loadAjaxEntity($id, $param , $DIModels) {
		//Delega la private functions delegata dalla richiesta sulla entity
		$response = $this->$id($param, $DIModels);

		return $response;
	}
}