<?php
/**
 * Bulk Sync Manager
 *
 * Manages bulk synchronization of multiple RSS feeds with rate limiting protection.
 *
 * @package Wisewand
 * @subpackage RSS
 * @since 1.0.3
 * @license GPL-2.0-or-later
 */

namespace Wisewand\RSS;

use Wisewand\Utils\Rate_Limiter;

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

/**
 * Bulk Sync Manager class
 *
 * Coordinates synchronization of multiple feeds with intelligent rate limiting.
 *
 * @since 1.0.3
 */
class Bulk_Sync_Manager {

	/**
	 * Sync engine instance
	 *
	 * @var RSS_Sync_Engine
	 */
	private $sync_engine;

	/**
	 * Feed manager instance
	 *
	 * @var RSS_Feed_Manager
	 */
	private $feed_manager;

	/**
	 * Rate limiter instance
	 *
	 * @var Rate_Limiter
	 */
	private $rate_limiter;

	/**
	 * Logger instance
	 *
	 * @var \Wisewand\Utils\Logger
	 */
	private $logger;

	/**
	 * Delay between feeds in seconds
	 *
	 * @var int
	 */
	private $feed_delay = 2;

	/**
	 * Maximum retry attempts for rate limit errors
	 *
	 * @var int
	 */
	private $max_retries = 3;

	/**
	 * Constructor
	 *
	 * @param RSS_Sync_Engine           $sync_engine   Sync engine.
	 * @param RSS_Feed_Manager          $feed_manager  Feed manager.
	 * @param Rate_Limiter              $rate_limiter  Rate limiter.
	 * @param \Wisewand\Logging\Logger  $logger        Logger instance.
	 */
	public function __construct(
		RSS_Sync_Engine $sync_engine,
		RSS_Feed_Manager $feed_manager,
		Rate_Limiter $rate_limiter,
		$logger
	) {
		$this->sync_engine  = $sync_engine;
		$this->feed_manager = $feed_manager;
		$this->rate_limiter = $rate_limiter;
		$this->logger       = $logger;
	}

	/**
	 * Sync multiple feeds in batch
	 *
	 * @param array $feed_ids     Array of feed IDs to sync. Empty array = all enabled feeds.
	 * @param bool  $force_import Whether to force import even if not time yet.
	 * @return array Results array with detailed progress tracking.
	 */
	public function sync_feeds_batch( array $feed_ids = array(), $force_import = false ) {
		$start_time = microtime( true );

		// Timeout protection: Maximum execution time budget.
		// Set to 50 seconds to safely stay under 60-second server timeout limits.
		$max_execution_time = 50;
		$sync_start_time    = microtime( true );
		$feeds_processed    = 0;



		// Get feeds to sync.
		if ( empty( $feed_ids ) ) {
			$this->logger->debug(
				'Bulk sync: Fetching all feeds',
				array( 'force_import' => $force_import )
			);

			$feeds = $this->feed_manager->get_all_feeds();

			$this->logger->info(
				'Bulk sync: Retrieved feeds from database',
				array( 'total_feeds' => count( $feeds ) )
			);

			// Filter logic: Only filter if NOT force_import.
			// When force_import=true (Sync All button): Sync ALL feeds.
			// When force_import=false (scheduled CRON): Only sync auto_import_enabled=1 feeds.
			if ( ! $force_import ) {
				$feeds = array_filter(
					$feeds,
					function ( $feed ) {
						return ! empty( $feed['auto_import_enabled'] );
					}
				);

				$this->logger->info(
					'Bulk sync: Filtered to auto-import enabled feeds (scheduled sync)',
					array( 'enabled_count' => count( $feeds ) )
				);
			} else {
				$this->logger->info(
					'Bulk sync: Force import enabled - syncing ALL feeds',
					array( 'total_feeds' => count( $feeds ) )
				);
			}
		} else {
			$feeds = array();
			foreach ( $feed_ids as $feed_id ) {
				$feed = $this->feed_manager->get_feed( $feed_id );
				if ( $feed ) {
					$feeds[] = $feed;
				}
			}
		}

		$total_feeds = count( $feeds );

		// Initialize results.
		$results = array(
			'total_feeds'   => $total_feeds,
			'succeeded'     => 0,
			'failed'        => 0,
			'skipped'       => 0,
			'total_items'   => 0,
			'new_items'     => 0,
			'updated_items' => 0,
			'feed_results'  => array(),
			'duration'      => 0,
		);

		if ( $total_feeds === 0 ) {
			$this->logger->info( 'No feeds to sync' );
			return $results;
		}

		$this->logger->info(
			'Starting bulk sync',
			array(
				'total_feeds'  => $total_feeds,
				'force_import' => $force_import,
			)
		);

		// Process each feed.
		foreach ( $feeds as $index => $feed ) {
			// Check if approaching timeout limit.
			$elapsed_time = microtime( true ) - $sync_start_time;
			if ( $elapsed_time > $max_execution_time ) {
				$remaining_feeds = $total_feeds - $feeds_processed;



				$this->logger->warning(
					'Bulk sync stopped: approaching server timeout',
					array(
						'feeds_processed'  => $feeds_processed,
						'remaining_feeds'  => $remaining_feeds,
						'elapsed_time'     => round( $elapsed_time, 2 ),
						'max_allowed_time' => $max_execution_time,
					)
				);

				// Add timeout info to results.
				$results['timeout_reached'] = true;
				$results['remaining_feeds'] = $remaining_feeds;
				break; // Stop processing to avoid server timeout.
			}

			$feed_id = $feed['id'];

			// Add delay between feeds (except first one).
			if ( $index > 0 ) {
				$this->logger->debug(
					'Rate limiting delay',
					array(
						'delay_seconds' => $this->feed_delay,
						'feed_id'       => $feed_id,
					)
				);
				sleep( $this->feed_delay );
			}

			// Sync feed with retry logic.
			$feed_result = $this->sync_feed_with_retry( $feed_id, $force_import );

			// Track results.
			if ( $feed_result['status'] === 'success' ) {
				$results['succeeded']++;
				$results['total_items']   += $feed_result['total_items'];
				$results['new_items']     += $feed_result['new_items'];
				$results['updated_items'] += $feed_result['updated_items'];
			} elseif ( $feed_result['status'] === 'skipped' ) {
				$results['skipped']++;
			} else {
				$results['failed']++;
			}

			$results['feed_results'][] = $feed_result;

			$feeds_processed++;

			// Allow request to breathe.
			if ( function_exists( 'fastcgi_finish_request' ) ) {
				fastcgi_finish_request();
			}
		}

		// Calculate duration.
		$results['duration'] = round( microtime( true ) - $start_time, 2 );

		$this->logger->info(
			'Bulk sync completed',
			array(
				'total_feeds' => $results['total_feeds'],
				'succeeded'   => $results['succeeded'],
				'failed'      => $results['failed'],
				'skipped'     => $results['skipped'],
				'duration'    => $results['duration'],
			)
		);

		return $results;
	}

	/**
	 * Sync single feed with retry logic
	 *
	 * @param int  $feed_id      Feed ID.
	 * @param bool $force_import Whether to force import.
	 * @return array Feed sync result.
	 */
	private function sync_feed_with_retry( $feed_id, $force_import = false ) {
		$feed = $this->feed_manager->get_feed( $feed_id );

		if ( ! $feed ) {
			return array(
				'feed_id' => $feed_id,
				'status'  => 'error',
				'message' => __( 'Feed not found', 'wisewand-ai-content-writer-seo-content-generator' ),
			);
		}

		$attempt = 0;

		while ( $attempt < $this->max_retries ) {
			$attempt++;

			try {
				// Check rate limit before sync.
				$rate_check = $this->rate_limiter->check_rate_limit( 'feed_' . $feed_id );
				if ( false !== $rate_check ) {
					$wait_time = $rate_check['wait_time'];

					if ( $attempt === 1 ) {
						$this->logger->warning(
							'Rate limit encountered, waiting',
							array(
								'feed_id'   => $feed_id,
								'wait_time' => $wait_time,
							)
						);
					}

					// Wait and retry.
					sleep( $wait_time );
					continue;
				}

				// Attempt sync.
				$result = $this->sync_engine->sync_feed( $feed_id, $force_import );

				if ( is_wp_error( $result ) ) {
					// Check if it's a rate limit error.
					if ( $this->is_rate_limit_error( $result ) ) {
						if ( $attempt < $this->max_retries ) {
							$this->logger->warning(
								'Rate limit error, retrying',
								array(
									'feed_id' => $feed_id,
									'attempt' => $attempt,
									'error'   => $result->get_error_message(),
								)
							);

							// Intelligent backoff using rate limiter.
							$this->rate_limiter->record_request( 'feed_' . $feed_id );
							$wait_time = $this->rate_limiter->get_remaining_wait_time( 'feed_' . $feed_id );
							sleep( $wait_time );
							continue;
						}
					}

					// Other error or max retries reached.
					return array(
						'feed_id' => $feed_id,
						'status'  => 'error',
						'message' => $result->get_error_message(),
						'url'     => 'feed_' . $feed_id,
					);
				}

				// Success - record successful request.
				$this->rate_limiter->record_request( 'feed_' . $feed_id );

				return array(
					'feed_id'       => $feed_id,
					'status'        => isset( $result['skipped'] ) && $result['skipped'] ? 'skipped' : 'success',
					'total_items'   => $result['total_items'] ?? 0,
					'new_items'     => $result['new_items'] ?? 0,
					'updated_items' => $result['updated_items'] ?? 0,
					'url'           => 'feed_' . $feed_id,
					'message'       => $result['message'] ?? __( 'Sync completed', 'wisewand-ai-content-writer-seo-content-generator' ),
				);

			} catch ( \Exception $e ) {
				$this->logger->error(
					'Feed sync exception',
					array(
						'feed_id' => $feed_id,
						'attempt' => $attempt,
						'error'   => $e->getMessage(),
					)
				);

				if ( $attempt >= $this->max_retries ) {
					return array(
						'feed_id' => $feed_id,
						'status'  => 'error',
						'message' => $e->getMessage(),
						'url'     => 'feed_' . $feed_id,
					);
				}

				// Brief delay before retry.
				sleep( 1 );
			}
		}

		// Should not reach here, but handle gracefully.
		return array(
			'feed_id' => $feed_id,
			'status'  => 'error',
			'message' => __( 'Max retry attempts exceeded', 'wisewand-ai-content-writer-seo-content-generator' ),
			'url'     => 'feed_' . $feed_id ?? '',
		);
	}

	/**
	 * Check if error is a rate limit error
	 *
	 * @param \WP_Error $error Error object.
	 * @return bool True if rate limit error.
	 */
	private function is_rate_limit_error( $error ) {
		$message = strtolower( $error->get_error_message() );

		$rate_limit_indicators = array(
			'rate limit',
			'too many requests',
			'429',
			'slow down',
			'quota exceeded',
			'throttle',
		);

		foreach ( $rate_limit_indicators as $indicator ) {
			if ( strpos( $message, $indicator ) !== false ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Set delay between feeds
	 *
	 * @param int $seconds Delay in seconds.
	 */
	public function set_feed_delay( $seconds ) {
		$this->feed_delay = absint( $seconds );
	}

	/**
	 * Set maximum retry attempts
	 *
	 * @param int $attempts Maximum attempts.
	 */
	public function set_max_retries( $attempts ) {
		$this->max_retries = absint( $attempts );
	}

	/**
	 * Get current configuration
	 *
	 * @return array Configuration array.
	 */
	public function get_config() {
		return array(
			'feed_delay'  => $this->feed_delay,
			'max_retries' => $this->max_retries,
		);
	}
}
