<?php

if( !class_exists( 'PS_AEDL_Batch' ) ):

class PS_AEDL_Batch {

    private static $instance = null;
    private $enabled_batch = false;
    private $emails_per_batch = false;
    private $batch_interval = false;
    private $auto_retry = false;
    private $times_to_retry = false;
    private $has_transient = false;
    private $id = '';
    private $quota_transient = false;
    private $quota_status = 0;
    private $quota = 0;
    private $quota_enabled = false;
	private $enable_advance_delivery = 0;

    const TRANSIENT= 'ps_aedl_batch';
    const OPTION_BATCH_PROCESS = 'ps_aedl_batch';
    const CRON_HOOK = 'post_smtp_batch_cron_hook';

    /**
     * Instance of the class
     * 
     * @since 1.0.0
     * @version 1.0.0
     * @var object
     */
    public static function get_instance() {

        if( null == self::$instance ) {

            self::$instance = new self;

        }

        return self::$instance;

    }

    /**
     * constructor of the class
     * 
     * @since 1.0.0
     * @version 1.0.0
     */
    public function __construct() {
        $settings = get_option( PS_AEDL_Settings::OPTION );
        $this->emails_per_batch = psaedl_extract_value_from_option( $settings, 'emails_per_batch' );
        $this->batch_interval = psaedl_extract_value_from_option( $settings, 'batch_duration' );
        $this->enabled_batch = ( !empty( $this->emails_per_batch ) && !empty( $this->batch_interval ) ) ? true : false;
		$this->enable_advance_delivery = psaedl_extract_value_from_option( $settings, 'enable_advance_delivery' );
        $this->auto_retry = psaedl_extract_value_from_option( $settings, 'enable_auto_retry' );
        $this->times_to_retry = psaedl_extract_value_from_option( $settings, 'times_to_retry' );
        $this->quota = psaedl_extract_value_from_option( $settings, 'quota_value' );
        $this->quota_enabled = psaedl_extract_value_from_option( $settings, 'enable_quota' );
        $this->quota_status = get_option( PS_AEDL_Quota::QUOTA_STATUS );
        $this->quota_transient = get_transient( PS_AEDL_Quota::TRANSIENT );
        add_action( 'init', array( $this, 'schedule_cron_event' ) );
		add_action( 'post_smtp_batch_cron_hook', array( $this, 'process_batch_emails' ) );
        add_filter( 'cron_schedules', array( $this,  'post_smtp_add_custom_cron_schedule' ) );
        //Lets render into the table :)

        if( $this->enabled_batch ) {
            add_action( 'post_smtp_before_logs_table', array( $this, 'render_next_batch' ) );
        }

    }

    /**
     * Converts a given time in seconds to a human-readable format.
     *
     * @since 1.3.0
     * @version 1.3.0
     * 
     * @param int $seconds The time in seconds to be converted.
     * @return string A readable string representation of the time (e.g., "2 hours and 30 minutes").
     */
    public function convert_seconds_to_readable_time( $seconds ) {
        if ( $seconds < 60 ) {
            return sprintf( __( '%d seconds', 'post-smtp' ), $seconds );
        } elseif ( $seconds < 3600 ) {
            $minutes = floor( $seconds / 60 );
            return sprintf( __( '%d minutes', 'post-smtp' ), $minutes );
        } else {
            $hours = floor( $seconds / 3600 );
            $minutes = floor( ( $seconds % 3600 ) / 60 );
            if ( $minutes > 0 ) {
                return sprintf( __( '%d hours and %d minutes', 'post-smtp' ), $hours, $minutes );
            }
            return sprintf( __( '%d hours', 'post-smtp' ), $hours );
        }
    }

    /**
     * Schedules a custom cron event for batch email processing.
     *
     * This function checks if the cron event has already been scheduled.
     * If not, it schedules the event to run at intervals defined by 'batch_interval'.
     *
     * @since 1.3.0
     * @version 1.3.0
     * 
     */
    public function schedule_cron_event() {
		if($this->enable_advance_delivery == 1){
			if ( !wp_next_scheduled( self::CRON_HOOK ) ) {
				wp_schedule_event( time(), 'batch_interval', self::CRON_HOOK );
			}
		}else{
			 // Get the next scheduled timestamp for the custom cron event.
			$timestamp = wp_next_scheduled( 'post_smtp_batch_cron_hook' );

			// If the event is scheduled, unschedule it.
			if ( $timestamp ) {
				wp_unschedule_event( $timestamp, 'post_smtp_batch_cron_hook' );
			}
		}
    }

    /**
     * Processes a batch of emails through the custom cron job.
     *
     * This function enables batch processing if the 'enabled_batch' property is true.
     * It adds necessary filters, logs the action, and calls the email sending logic.
      *
     * @since 1.3.0
     * @version 1.3.0
     *
     */
    public function process_batch_emails() {
        if ( $this->enabled_batch ) {
            // Enable email sending for the batch process.
            add_filter( 'post_smtp_send_email', '__return_true', 13 );
            add_filter( 'post_smtp_do_send_email', '__return_true', 13 );

            // Trigger email sending for queued logs.
            $this->send_emails();
        }
    }

    /**
     * Adds a custom cron schedule based on the batch interval.
     *
     * This function registers a new cron schedule with an interval defined
     * by the 'batch_interval' property. The interval is displayed in a human-readable format.
     *
     * @since 1.3.0
     * @version 1.3.0
     *
     * @param array $schedules Existing WP-Cron schedules.
     * @return array Updated schedules including the custom 'batch_interval'.
     */
    public function post_smtp_add_custom_cron_schedule( $schedules ) {
        if ( $this->batch_interval ) {
            // Convert the batch interval to a readable format for display.
            $readable_time = $this->convert_seconds_to_readable_time( $this->batch_interval );
            $schedules['batch_interval'] = array(
                'interval' => $this->batch_interval,
                'display'  => sprintf( __( 'Every %s', 'post-smtp' ), $readable_time ),
            );
        }

        return $schedules;
    }


    /**
     * Send emails in batch
     * 
     * @since 1.0.0
     * @version 1.0.0
     */
   public function send_emails() {
       $queued_logs = $this->get_queued_logs();
        foreach( $queued_logs as $log ) {

            //Quota limit reached :(
            if(
                $this->quota_transient
                &&
                $this->quota_enabled 
                &&
                $this->quota_status >= $this->quota
            ) {

                break;

            }

           $this->id = $log->id;
  		   $track_code = postman_get_log_meta( $this->id, 'track_code' );
           add_filter( 'post_smtp_update_email_log_id', array( $this, 'update_log' ), 99 );
           add_filter( 'post_smtp_log_status', array( $this, 'set_log_status' ), 99, 2 );
           $log->original_headers = maybe_unserialize( $log->original_headers );
			
           /**
             * Filter the email log before sending
             * 
             * @param object $log
             * 
             * @since 1.2.2
             */
			
			// 🔗 Merge track code into the message
			if ( ! empty( $track_code ) && strpos( $log->original_message, '</body>' ) !== false ) {
				$log->original_message = str_replace(
					'</body>',
					'<!-- HTML Ignored -->' . $track_code . '</body>',
					$log->original_message
				);
			}
            $attachments = array();
			$attachment_id = postman_get_log_meta( $log->id, 'attachments' );

    		if ( $attachment_id ) {
	    		$upload_dir = WP_CONTENT_DIR . '/uploads/post-smtp/' . $attachment_id;

				if ( file_exists( $upload_dir ) && is_dir( $upload_dir ) ) {
					$files = scandir( $upload_dir );
					foreach ( $files as $file ) {
						if ( $file !== '.' && $file !== '..' ) {
							$attachments[] = $upload_dir . '/' . $file;
						}
					}
				}
			}
	        
            $GLOBALS['post_smtp_suppress_logging'] = true;
            $has_sent = wp_mail( $log->original_to, $log->original_subject, $log->original_message, $log->original_headers, $attachments );
			unset( $GLOBALS['post_smtp_suppress_logging'] );
            
            if( !$has_sent && $this->auto_retry ) {

                if( !function_exists( 'postman_get_log_meta' ) ) {
    
                    require_once POST_SMTP_PATH . '/includes/postman-functions.php';
                }
    
                $retried = postman_get_log_meta( $log->id, 'retried' ) + 1;
                postman_update_log_meta( $log->id, 'retried', $retried );
    
            }

        }

   }

    /**
     * Get queued logs
     * 
     * @since 1.0.0
     * @version 1.0.0
     */
    public function get_queued_logs() {

        if( !class_exists( 'PostmanEmailLogs' ) ) {

            require_once POST_SMTP_PATH . '/Postman/PostmanEmailLogs.php';

        }

        global $wpdb;
        $emails_logs = new PostmanEmailLogs();
        $logs = array();

        if( $this->auto_retry ) {

            $logs = $wpdb->get_results(
                $wpdb->prepare( 
                    "SELECT pl.id, pl.original_to, pl.original_subject, pl.original_message, pl.original_headers, pl.success, lm.meta_value AS retried
                    FROM {$wpdb->prefix}{$emails_logs->db_name} AS pl
                    LEFT JOIN {$wpdb->prefix}{$emails_logs->meta_table} AS lm ON lm.log_id = pl.id AND lm.meta_key = 'retried'
                    WHERE ( pl.success = 'In Queue' OR pl.success != 1 ) AND ( lm.meta_value < %s OR lm.meta_key IS NULL )
                    ORDER BY pl.`time` ASC
                    LIMIT %d;",
                    $this->times_to_retry,
                    $this->emails_per_batch
                )
            );

        }
        else {

            $logs = $wpdb->get_results( 
                $wpdb->prepare(
                    "SELECT id, original_to, original_subject, original_message, original_headers, success
                    FROM {$wpdb->prefix}{$emails_logs->db_name} 
                    WHERE success = 'In Queue' 
                    ORDER BY `time` 
                    ASC LIMIT %d;",
                    $this->emails_per_batch
                )
            );

        }

        return $logs;

    }


    /**
     * Update Log | Filter Callback
     * 
     * @since 1.0.0
     * @version 1.0.0
     */
    public function update_log() {
        return $this->id;
    }


    /**
     * Set log status | Filter Callback
     * 
     * @since 1.0.0
     * @version 1.0.0
     */
    public function set_log_status( $status, $log ) {
        $status = $log->statusMessage;
        return $status;

    }


    /**
     * Render next/ Upcoming batch
     * 
     * @since 1.0.0
     * @version 1.0.00
     */
    public function render_next_batch() {

        $count = $this->get_queued_logs_count();
        $due_time = get_option( '_transient_timeout_' . self::TRANSIENT );
        $date_format = get_option( 'date_format' );
	    $time_format = get_option( 'time_format' );
        $due_time = $due_time ? date( "{$date_format} {$time_format}", $due_time ) : 'Prepairing...';
 
        if( $count ):
        ?>
        <table width="100%" class="ps-aedl-batch-table">
            <tr>
                <th>Status</th>
                <th>Queued Emails</th>
                <th>Next Batch No. of Emails</th>
                <th>Next Batch Due Time</th>
            </tr>
            <tr>
                <td class="ps-email-log-status-queued"><span></span> <?php _e( 'In Queue', 'ps-aedl' ); ?></td>
                <td><?php echo esc_attr( $count ); ?></td>
                <td><?php echo esc_attr( $this->emails_per_batch ); ?></td>
                <td><?php echo esc_attr( $due_time ); ?></td>
            </tr>
        </table>
        <?php
        endif;
    }


    /**
     * Get queued logs count
     * 
     * @since 1.0.0
     * @version 1.0.0
     */
    public function get_queued_logs_count() {

        if( !class_exists( 'PostmanEmailLogs' ) ) {

            require_once POST_SMTP_PATH . '/Postman/PostmanEmailLogs.php';

        }

        global $wpdb;
        $emails_logs = new PostmanEmailLogs();
        $count = '';

        if( $this->auto_retry ) {

            $count = $wpdb->get_results(
                $wpdb->prepare( 
                    "SELECT COUNT( * ) AS count
                    FROM {$wpdb->prefix}{$emails_logs->db_name} AS pl
                    LEFT JOIN {$wpdb->prefix}{$emails_logs->meta_table} AS lm ON lm.log_id = pl.id AND lm.meta_key = 'retried'
                    WHERE ( pl.success = 'In Queue' OR pl.success != 1 ) AND ( lm.meta_value < %s OR lm.meta_key IS NULL )
                    ORDER BY pl.`time` ASC
                    LIMIT %d;",
                    $this->times_to_retry,
                    $this->emails_per_batch
                )
            );

        }
        else {

            $count = $wpdb->get_results( 
                $wpdb->prepare(
                    "SELECT COUNT( * ) AS count
                    FROM {$wpdb->prefix}{$emails_logs->db_name} 
                    WHERE success = 'In Queue' 
                    ORDER BY `time` 
                    ASC LIMIT %d;",
                    $this->emails_per_batch
                )
            );

        }

        return isset( $count[0]->count ) ? $count[0]->count : false;


    }

}

PS_AEDL_Batch::get_instance();

endif;