<?php
/**
 * Background Sync Processor Class
 *
 * @package ShopExplorer\Sync
 * @since 1.0.0
 */

namespace SHOPEXPLORER\Sync;

use SHOPEXPLORER\Exceptions\SyncException;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Background Sync Processor class
 *
 * Handles WordPress cron jobs and background processing for sync operations
 *
 * @since 1.0.0
 */
class SHOPEX_BackgroundSyncProcessor {

	/**
	 * Data sync engine instance
	 *
	 * @var SHOPEX_DataSyncEngine
	 */
	private $sync_engine;

	/**
	 * Cron hook names
	 *
	 * @var array
	 */
	private $cron_hooks = [
		'sync_process' => 'shopex_process_sync_queue',
		'sync_cleanup' => 'shopex_cleanup_sync_queue',
		'sync_retry' => 'shopex_retry_failed_syncs',
	];

	/**
	 * Progress tracking option names
	 *
	 * @var array
	 */
	private $progress_options = [
		'current_batch' => 'shopex_sync_current_batch',
		'total_batches' => 'shopex_sync_total_batches',
		'processed_items' => 'shopex_sync_processed_items',
		'total_items' => 'shopex_sync_total_items',
		'start_time' => 'shopex_sync_start_time',
		'status' => 'shopex_sync_status',
	];

	/**
	 * Constructor
	 */
	public function __construct() {
		$this->sync_engine = new SHOPEX_DataSyncEngine();
		$this->init_hooks();
	}

	/**
	 * Initialize WordPress hooks
	 */
	private function init_hooks() {
		// Register cron hooks
		add_action( $this->cron_hooks['sync_process'], [ $this, 'process_sync_queue_cron' ] );
		add_action( $this->cron_hooks['sync_cleanup'], [ $this, 'cleanup_sync_queue_cron' ] );
		add_action( $this->cron_hooks['sync_retry'], [ $this, 'retry_failed_syncs_cron' ] );

		// Register custom cron intervals
		add_filter( 'cron_schedules', [ $this, 'add_custom_cron_intervals' ] );

		// AJAX hooks for manual processing
		add_action( 'wp_ajax_shopex_process_sync', [ $this, 'ajax_process_sync' ] );
		add_action( 'wp_ajax_shopex_get_sync_progress', [ $this, 'ajax_get_sync_progress' ] );
	}

	/**
	 * Schedule background sync processing
	 *
	 * @param string $interval Cron interval (default: 'every_5_minutes')
	 * @return bool
	 */
	public function schedule_sync_processing( $interval = 'every_5_minutes' ) {
		if ( ! wp_next_scheduled( $this->cron_hooks['sync_process'] ) ) {
			return wp_schedule_event( time(), $interval, $this->cron_hooks['sync_process'] );
		}
		return true;
	}

	/**
	 * Schedule sync cleanup
	 *
	 * @param string $interval Cron interval (default: 'daily')
	 * @return bool
	 */
	public function schedule_sync_cleanup( $interval = 'daily' ) {
		if ( ! wp_next_scheduled( $this->cron_hooks['sync_cleanup'] ) ) {
			return wp_schedule_event( time(), $interval, $this->cron_hooks['sync_cleanup'] );
		}
		return true;
	}

	/**
	 * Schedule retry processing
	 *
	 * @param string $interval Cron interval (default: 'hourly')
	 * @return bool
	 */
	public function schedule_retry_processing( $interval = 'hourly' ) {
		if ( ! wp_next_scheduled( $this->cron_hooks['sync_retry'] ) ) {
			return wp_schedule_event( time(), $interval, $this->cron_hooks['sync_retry'] );
		}
		return true;
	}

	/**
	 * Unschedule all sync processing
	 */
	public function unschedule_all() {
		foreach ( $this->cron_hooks as $hook ) {
			wp_clear_scheduled_hook( $hook );
		}
	}

	/**
	 * Process sync queue via cron
	 */
	public function process_sync_queue_cron() {
		try {
			$this->update_sync_status( 'processing' );

			$results = $this->sync_engine->process_queue();

			$this->log_cron_results( 'sync_process', $results );

			if ( $results['processed'] === 0 ) {
				$this->update_sync_status( 'idle' );
			}
		} catch ( \Exception $e ) {
						return false;
		}
	}

	/**
	 * Cleanup sync queue via cron
	 */
	public function cleanup_sync_queue_cron() {
		try {
			$cleared_count = $this->sync_engine->clear_completed_operations( 7 );

			$this->log_cron_results( 'sync_cleanup', [
				'cleared_operations' => $cleared_count,
			] );

		} catch ( \Exception $e ) {
						return false;
		}
	}

	/**
	 * Retry failed syncs via cron
	 */
	public function retry_failed_syncs_cron() {
		try {
			$retried_count = $this->retry_failed_operations();

			$this->log_cron_results( 'sync_retry', [
				'retried_operations' => $retried_count,
			] );

		} catch ( \Exception $e ) {
						return false;
		}
	}

	/**
	 * Process large dataset in batches
	 *
	 * @param array  $entity_ids Array of entity IDs to sync
	 * @param string $entity_type Entity type
	 * @param string $operation Operation type
	 * @param array  $options Processing options
	 * @return array Processing results
	 */
	public function process_large_dataset( $entity_ids, $entity_type, $operation = 'sync', $options = [] ) {
		$batch_size = $options['batch_size'] ?? 50;
		$delay_between_batches = $options['delay'] ?? 5; // seconds

		$total_items = count( $entity_ids );
		$total_batches = ceil( $total_items / $batch_size );
		$current_batch = 0;
		$processed_items = 0;

		// Initialize progress tracking
		$this->init_progress_tracking( $total_items, $total_batches );

		$results = [
			'total_items' => $total_items,
			'total_batches' => $total_batches,
			'processed_items' => 0,
			'successful_items' => 0,
			'failed_items' => 0,
			'batch_results' => [],
		];

		try {
			$batches = array_chunk( $entity_ids, $batch_size );

			foreach ( $batches as $batch_index => $batch_ids ) {
				$current_batch = $batch_index + 1;

				$this->update_progress( $current_batch, $processed_items );

				// Queue batch items
				$batch_results = [
					'batch' => $current_batch,
					'items' => count( $batch_ids ),
					'queued' => 0,
					'errors' => [],
				];

				foreach ( $batch_ids as $entity_id ) {
					try {
						$queue_id = $this->sync_engine->queue_sync(
							$entity_type,
							$entity_id,
							$operation,
							$options['direction'] ?? 'bidirectional',
							$options
						);

						if ( $queue_id ) {
							$batch_results['queued']++;
							$results['successful_items']++;
						} else {
							$batch_results['errors'][] = "Failed to queue entity ID: {$entity_id}";
							$results['failed_items']++;
						}

						$processed_items++;

					} catch ( \Exception $e ) {
									return false;
					}
				}

				$results['batch_results'][] = $batch_results;
				$results['processed_items'] = $processed_items;

				// Process the queued items immediately if requested
				if ( ! empty( $options['process_immediately'] ) ) {
					$process_results = $this->sync_engine->process_queue( count( $batch_ids ) );
					$batch_results['process_results'] = $process_results;
				}

				// Delay between batches to prevent overwhelming the system
				if ( $current_batch < $total_batches && $delay_between_batches > 0 ) {
					sleep( $delay_between_batches );
				}
			}

			$this->complete_progress_tracking( 'completed' );

		} catch ( \Exception $e ) {
						return false;
		}

		return $results;
	}

	/**
	 * Retry failed operations
	 *
	 * @param int $max_retries Maximum number of operations to retry
	 * @return int Number of operations retried
	 */
	public function retry_failed_operations( $max_retries = 50 ) {
		global $wpdb;

		$queue_table = $wpdb->prefix . 'shopex_sync_queue';

		// Get failed operations that can be retried
		$query = $wpdb->prepare(
			"SELECT * FROM {$queue_table} 
			 WHERE status = 'failed' 
			 AND retry_count < max_retries 
			 ORDER BY priority DESC, created_at ASC 
			 LIMIT %d",
			$max_retries
		);

		$failed_operations = $wpdb->get_results( $query, ARRAY_A );
		$retried_count = 0;

		foreach ( $failed_operations as $operation ) {
			try {
				// Reset status to pending and increment retry count
				$wpdb->update(
					$queue_table,
					[
						'status' => 'pending',
						'retry_count' => $operation['retry_count'] + 1,
						'updated_at' => current_time( 'mysql' ),
						'error_message' => null,
					],
					[ 'id' => $operation['id'] ],
					[ '%s', '%d', '%s', '%s' ],
					[ '%d' ]
				);

				$retried_count++;

			} catch ( \Exception $e ) {
							return false;
			}
		}

		return $retried_count;
	}

	/**
	 * Get sync progress
	 *
	 * @return array Progress information
	 */
	public function get_sync_progress() {
		$progress = [];

		foreach ( $this->progress_options as $key => $option_name ) {
			$progress[ $key ] = get_option( $option_name, 0 );
		}

		// Calculate percentage
		if ( $progress['total_items'] > 0 ) {
			$progress['percentage'] = round( ( $progress['processed_items'] / $progress['total_items'] ) * 100, 2 );
		} else {
			$progress['percentage'] = 0;
		}

		// Calculate estimated time remaining
		if ( $progress['start_time'] && $progress['processed_items'] > 0 ) {
			$elapsed_time = time() - $progress['start_time'];
			$items_per_second = $progress['processed_items'] / $elapsed_time;
			$remaining_items = $progress['total_items'] - $progress['processed_items'];

			if ( $items_per_second > 0 ) {
				$progress['estimated_remaining'] = round( $remaining_items / $items_per_second );
			} else {
				$progress['estimated_remaining'] = null;
			}
		} else {
			$progress['estimated_remaining'] = null;
		}

		return $progress;
	}

	/**
	 * Initialize progress tracking
	 *
	 * @param int $total_items Total items
	 * @param int $total_batches Total batches
	 */
	private function init_progress_tracking( $total_items, $total_batches ) {
		update_option( $this->progress_options['total_items'], $total_items );
		update_option( $this->progress_options['total_batches'], $total_batches );
		update_option( $this->progress_options['current_batch'], 0 );
		update_option( $this->progress_options['processed_items'], 0 );
		update_option( $this->progress_options['start_time'], time() );
		update_option( $this->progress_options['status'], 'processing' );
	}

	/**
	 * Update progress
	 *
	 * @param int $current_batch Current batch
	 * @param int $processed_items Processed items
	 */
	private function update_progress( $current_batch, $processed_items ) {
		update_option( $this->progress_options['current_batch'], $current_batch );
		update_option( $this->progress_options['processed_items'], $processed_items );
	}

	/**
	 * Complete progress tracking
	 *
	 * @param string $status Final status
	 * @param string $message Optional message
	 */
	private function complete_progress_tracking( $status, $message = '' ) {
		update_option( $this->progress_options['status'], $status );

		if ( $message ) {
			update_option( 'shopex_sync_message', $message );
		}
	}

	/**
	 * Update sync status
	 *
	 * @param string $status Status
	 */
	private function update_sync_status( $status ) {
		update_option( $this->progress_options['status'], $status );
	}

	/**
	 * Add custom cron intervals
	 *
	 * @param array $schedules Existing schedules
	 * @return array Modified schedules
	 */
	public function add_custom_cron_intervals( $schedules ) {
		$schedules['every_5_minutes'] = [
			'interval' => 300,
			'display' => __( 'Every 5 Minutes', 'shop-explorer' ),
		];

		$schedules['every_15_minutes'] = [
			'interval' => 900,
			'display' => __( 'Every 15 Minutes', 'shop-explorer' ),
		];

		$schedules['every_30_minutes'] = [
			'interval' => 1800,
			'display' => __( 'Every 30 Minutes', 'shop-explorer' ),
		];

		return $schedules;
	}

	/**
	 * AJAX handler for manual sync processing
	 */
	public function ajax_process_sync() {
		check_ajax_referer( 'shopex_sync', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( 'Insufficient permissions' );
		}

		try {
			$batch_size = isset( $_POST['batch_size'] ) ? (int) $_POST['batch_size'] : 10;
			$results = $this->sync_engine->process_queue( $batch_size );

			wp_send_json_success( $results );

		} catch ( \Exception $e ) {
			wp_send_json_error( [
				'message' => __( 'Failed.', 'shop-explorer' ),
			] );
		}
	}

	/**
	 * AJAX handler for getting sync progress
	 */
	public function ajax_get_sync_progress() {
		check_ajax_referer( 'shopex_sync', 'nonce' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( 'Insufficient permissions' );
		}

		$progress = $this->get_sync_progress();
		wp_send_json_success( $progress );
	}

	/**
	 * Log cron results
	 *
	 * @param string $operation Operation name
	 * @param array  $results Results array
	 */
	private function log_cron_results( $operation, $results ) {
		$message = sprintf(
			/* translators: %s: is the product id */
			'Cron operation %s completed: %s',
			$operation,
			wp_json_encode( $results )
		);

		error_log( $message );

		// Store in option for admin viewing
		$log_option = 'shopex_cron_log_' . $operation;
		$log_entry = [
			'timestamp' => current_time( 'mysql' ),
			'results' => $results,
		];

		update_option( $log_option, $log_entry );
	}

	/**
	 * Log cron errors
	 *
	 * @param string $operation Operation name
	 * @param string $error Error message
	 */
	private function log_cron_error( $operation, $error ) {
		$message = sprintf(
			/* translators: %s: is the product id */
			'Cron operation %s failed: %s',
			$operation,
			$error
		);

		error_log( $message );

		// Store in option for admin viewing
		$log_option = 'shopex_cron_error_' . $operation;
		$log_entry = [
			'timestamp' => current_time( 'mysql' ),
			'error' => $error,
		];

		update_option( $log_option, $log_entry );
	}

	/**
	 * Get cron status
	 *
	 * @return array Cron status information
	 */
	public function get_cron_status() {
		$status = [];

		foreach ( $this->cron_hooks as $name => $hook ) {
			$next_run = wp_next_scheduled( $hook );
			$status[ $name ] = [
				'hook' => $hook,
				'scheduled' => $next_run !== false,
				'next_run' => $next_run ? gmdate( 'Y-m-d H:i:s', $next_run ) : null,
				'next_run_relative' => $next_run ? human_time_diff( time(), $next_run ) : null,
			];
		}

		return $status;
	}

	/**
	 * Clear all progress tracking
	 */
	public function clear_progress_tracking() {
		foreach ( $this->progress_options as $option_name ) {
			delete_option( $option_name );
		}
		delete_option( 'shopex_sync_message' );
	}
}
