<?php
/**
 * @copyright (C) 2016 - 2025 Holger Brandt IT Solutions
 * @license GNU/GPL, see license.txt
 * WP Mailster is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License 2
 * as published by the Free Software Foundation.
 *
 * WP Mailster is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WP Mailster; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 * or see http://www.gnu.org/licenses/.
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit; // Not the droids
}


/**
 * Mailster Cronjob Controller
 */
class MailsterControllerCron {		
	var $execEnd;
	var $startTime;
	var $maxExecTime;
	var $minDuration;
    var $minCheckTime;
    var $minSendTime;
	var $debug;
    var $log;
	
	private function initCronjob(){
		$startTime = time();
		$this->maxExecTime = get_option('wpmst_cfg_maxexectime', 12);
		$this->minDuration = get_option('wpmst_cfg_minduration', 2);
        $this->minCheckTime = get_option('wpmst_cfg_minchecktime', 240);
        $this->minSendTime = get_option('wpmst_cfg_minsendtime', 60);
		$this->startTime = $startTime;
		$this->execEnd = $this->startTime + $this->maxExecTime;
		if( isset( $_GET[ 'debug' ] ) ) {
			$this->debug = sanitize_text_field($_GET['debug']);
		}
		$this->log = MstFactory::getLogger();
		
		$mstV = MstFactory::getV()->getProductName(true);
		$this->log($mstV . ' running on WordPress ' . get_bloginfo('version') );
		$this->log('Time left for cron to run: ' . $this->timeLeft()
						.' (execEnd: '  . $this->execEnd . ', now: ' . time() 
						.', maxExecTime: '.$this->maxExecTime
						.', minDuration: '.$this->minDuration.')'
						.', PHP max. exec: ' . ini_get('max_execution_time')
					, MstConsts::LOGENTRY_PLUGIN);	
	}
	
	private function tearDownCronjob(){
		$this->log('Job successfully finished');
        exit();
	}
	
	private function tearDownRetrieveSendMaintenanceCronjob(){
		$this->tearDownRetrieveCronjob();
		$this->tearDownSendCronjob();
		$this->tearDownMaintenanceCronjob();
	}
	
	private function tearDownRetrieveCronjob(){
		update_option( 'wpmst_cfg_last_exec_retrieve', time() );
	}
	
	private function tearDownSendCronjob(){
		update_option( 'wpmst_cfg_last_exec_sending', time() );
	}
	
	private function tearDownMaintenanceCronjob(){
		update_option( 'wpmst_cfg_last_exec_maintenance', time() );
	}
	
	/**
	 * Cron job that fetches mails of all mailing lists
	 */
	public function fetchall(){
		$this->initCronjob();
		$this->log('Fetch mails from all mailing lists called');
		
    	global $mailster_trigger_source;
    	$mailster_trigger_source = MstConsts::TRIGGER_SOURCE_CRONJOB_TASK_FETCH_ALL_LISTS;

		$retrieverObj = MstFactory::getMailRetriever();
		$this->checkPermission();
        list($nrRetrievedMessages, $nrLists) = $retrieverObj->retrieveMailsOfActiveMailingLists($this->minDuration, $this->execEnd);
        $this->log(($nrRetrievedMessages > 0 ? ('Retrieved '.$nrRetrievedMessages.' mails (from '.$nrLists.' lists)') : ('No mails to retrieve (from '.$nrLists.' lists)')));
		
    	$this->tearDownRetrieveCronjob();
    	$this->tearDownCronjob();
	}
	
	/**
	 * Cron job that fetches mails of a single mailing list
	 */
	public function fetch(){
		$this->initCronjob();
		$listId = intval($_GET['id']);
		$this->log('Fetch mails called for list: ' . $listId);
		
    	global $mailster_trigger_source;
    	$mailster_trigger_source = MstConsts::TRIGGER_SOURCE_CRONJOB_TASK_FETCH_LIST;

		$retrieverObj = MstFactory::getMailRetriever();
		$this->checkPermission();
		$listUtils	= MstFactory::getMailingListUtils();
		$mList = $listUtils->getMailingList($listId);
        $nrRetrievedMessages = 0;
        if($mList){
            $nrRetrievedMessages = $retrieverObj->retrieveMailsOfMailingList($mList, $this->minDuration, $this->execEnd);
        }
        $this->log(($nrRetrievedMessages > 0 ? 'Retrieved '.$nrRetrievedMessages.' mails' : 'No mails to retrieve'));
		
    	$this->tearDownRetrieveCronjob();
    	$this->tearDownCronjob();
	}
	
	/**
	 * Cron job that sends mails of all mailing lists
	 */
	public function send(){
		$this->initCronjob();
		$this->log('Send mails called');
		
    	global $mailster_trigger_source;
    	$mailster_trigger_source = MstConsts::TRIGGER_SOURCE_CRONJOB_TASK_SEND;

		$senderObj 	= MstFactory::getMailSender();
		$this->checkPermission();
		$nrEmailsSent = $senderObj->sendMails($this->minDuration, $this->execEnd);
        $this->log(($nrEmailsSent > 0 ? 'Sent '.$nrEmailsSent.' mails' : 'No mails sent'));
		
    	$this->tearDownSendCronjob();
    	$this->tearDownCronjob();
	}
	
	/**
	 * Cron job that performs system maintenance tasks
	 */
	public function cleanup(){
		$this->initCronjob();
		$this->log('Cleanup called');
		
    	global $mailster_trigger_source;
    	$mailster_trigger_source = MstConsts::TRIGGER_SOURCE_CRONJOB_TASK_MAINTENANCE;

  		$mstApp = MstFactory::getApplication();
		$this->checkPermission();
		$mstApp->performMaintenance($this->minDuration, $this->execEnd);
		
    	$this->tearDownMaintenanceCronjob();
    	$this->tearDownCronjob();
	}

	/**
	 * Cron job that fetches and sends mails of all mailing lists
	 */
	public function all(){
		$this->initCronjob();
		
		$this->log('All (fetch/retrieve and send mails) tasks called');
		
    	global $mailster_trigger_source;
    	$mailster_trigger_source = MstConsts::TRIGGER_SOURCE_CRONJOB_TASK_ALL;

  		$mstApp = MstFactory::getApplication();
		$retrieverObj = MstFactory::getMailRetriever();
		$senderObj 	= MstFactory::getMailSender();
		$this->checkPermission();
		$this->log('All (retrieve/send/cleanup) task called -> retrieve (1/3)');
        list($nrRetrievedMessages, $nrLists) = $retrieverObj->retrieveMailsOfActiveMailingLists($this->minDuration, $this->execEnd, $this->minCheckTime);
        $this->log(($nrRetrievedMessages > 0 ? ('Retrieved '.$nrRetrievedMessages.' mails (from '.$nrLists.' lists)') : ('No mails to retrieve (from '.$nrLists.' lists)')));
        $this->tearDownRetrieveCronjob();

        $this->log('All (retrieve/send/cleanup) task called -> send (2/3)');
        if($this->isTimeLeft()){
            $nrEmailsSent = $senderObj->sendMails($this->minDuration, $this->execEnd);
            $this->log(($nrEmailsSent > 0 ? 'Sent '.$nrEmailsSent.' mails' : 'No mails sent'));
            $this->tearDownSendCronjob();
        }else{
            $this->log('Timeout before sending, time left: '. $this->timeLeft()
                .' (execEnd: '  . $this->execEnd . ', now: ' . time()
                .', maxExecTime: '.$this->maxExecTime
                .', minDuration: '.$this->minDuration.')'
                .', PHP max. exec: ' . ini_get('max_execution_time'));
        }
		
		$this->log('All (retrieve/send/cleanup) task called -> cleanup (3/3)');
        if($this->isTimeLeft()){
            $mstApp->performMaintenance($this->minDuration, $this->execEnd);
            $this->tearDownMaintenanceCronjob();
        }else{
            $this->log('Timeout before maintenance, time left: '. $this->timeLeft()
                .' (execEnd: '  . $this->execEnd . ', now: ' . time()
                .', maxExecTime: '.$this->maxExecTime
                .', minDuration: '.$this->minDuration.')'
                .', PHP max. exec: ' . ini_get('max_execution_time'));
        }


    	$this->tearDownCronjob();
	}
	
	private function checkPermission(){
		$key = sanitize_text_field($_GET['key']);
		$validKey = get_option('wpmst_cfg_cron_job_key');
		$validKey = trim($validKey);
		if( ($key !== $validKey) || (empty($validKey)) ){
			$this->log('Cronjob key incorrect');
			die('403 '.__("Error: Invalid cronjob key", 'wp-mailster') );
		}else{
			$this->log('Cronjob key OK');
		}
	}
	
	private function isTimeLeft(){
		$timeLeft = $this->timeLeft();
		return ($timeLeft > $this->minDuration);
	}
	
	private function timeLeft(){	
		$tNow = time();		
		$t1 =  $this->execEnd-$tNow;
		if($t1 < -30000){ // is time incorrectly considered negativ?				
			$t1 = $this->minDuration+3; // add 3 seconds to have more than min duration
		}
		return $t1;
	}
	
	private function log($msg){
		if($this->debug == true){
			echo '<p>' . $msg . '</p>';
		}
		$this->log->debug('->->-> Cron: ' . $msg);
	}
	
}