<?php
/**
 * CRON Scheduler — manages all WP-Cron scheduling for Wisewand.
 *
 * Extracted from Plugin.php to give CRON scheduling a single,
 * focused responsibility.  Handles custom schedule registration,
 * initial scheduling on every page-load, and rescheduling when
 * the RSS import frequency option is changed.
 *
 * @package Wisewand
 * @since 2.8.2
 * @license GPL-2.0-or-later
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
 */
namespace Wisewand\Core;

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

use Wisewand\Utils\Options;
use Wisewand\Logging\Logger;

// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log -- error_log() is only used within WP_DEBUG conditionals for debugging

/**
 * CRON Scheduler
 *
 * Registers custom WP-Cron intervals and guarantees that the three
 * Wisewand scheduled hooks (status check, editorial calendar, RSS sync)
 * are always present.  Also handles rescheduling the RSS sync hook
 * when the global import-frequency option is updated.
 *
 * @package Wisewand
 * @since 2.8.2
 */
class CRON_Scheduler {

	/**
	 * Plugin options wrapper.
	 *
	 * @since 2.8.2
	 * @var Options
	 */
	private $options;

	/**
	 * Activity logger.
	 *
	 * @since 2.8.2
	 * @var Logger
	 */
	private $logger;

	/**
	 * Admin manager instance — injected after construction to avoid
	 * circular-dependency issues.
	 *
	 * @since 2.8.2
	 * @var mixed|null
	 */
	private $admin_manager = null;

	/**
	 * Constructor.
	 *
	 * @since 2.8.2
	 *
	 * @param Options $options Plugin options wrapper.
	 * @param Logger  $logger  Activity logger.
	 */
	public function __construct( Options $options, Logger $logger ) {
		$this->options = $options;
		$this->logger  = $logger;
	}

	/**
	 * Set the admin manager after construction.
	 *
	 * Used to avoid circular dependencies between Plugin and
	 * CRON_Scheduler while still giving reschedule_rss_sync()
	 * access to the RSS page / feed manager.
	 *
	 * @since 2.8.2
	 *
	 * @param mixed $admin_manager Admin_Manager instance.
	 */
	public function set_admin_manager( $admin_manager ) {
		$this->admin_manager = $admin_manager;
	}

	/**
	 * Ensure all Wisewand CRON hooks are scheduled.
	 *
	 * Runs on every plugin load so that CRON hooks are always present
	 * even if they were cleared by a WP update or plugin reactivation.
	 * Requires both wp_next_scheduled() and wp_schedule_event() to be
	 * available (not always the case in very early bootstrap contexts).
	 *
	 * @since 2.8.2
	 */
	public function ensure_cron_scheduled() {
		if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_event' ) ) {
			return;
		}

		// =========================================================================
		// TIER 1: Status Check CRON (Every 15 Minutes)
		// =========================================================================
		// Purpose: Check if generating articles are ready on Wisewand
		// Lightweight operation - only checks status, no heavy processing
		if ( ! wp_next_scheduled( 'wisewand_check_article_status' ) ) {
			// Try every_15_minutes schedule first
			$scheduled = wp_schedule_event( time(), 'every_15_minutes', 'wisewand_check_article_status' );

			// Fallback: If every_15_minutes doesn't exist, use hourly (built-in)
			if ( false === $scheduled ) {
				if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
					error_log( 'Wisewand: every_15_minutes schedule not available, falling back to hourly' );
				}
				$scheduled = wp_schedule_event( time(), 'hourly', 'wisewand_check_article_status' );
			}

			if ( $this->logger ) {
				if ( false !== $scheduled ) {
					$next_run = wp_next_scheduled( 'wisewand_check_article_status' );
					$this->logger->info(
						'Status check CRON auto-scheduled',
						array(
							'frequency'              => 'every_15_minutes (or hourly fallback)',
							'first_run'              => gmdate( 'Y-m-d H:i:s', time() ),
							'next_run'               => $next_run ? gmdate( 'Y-m-d H:i:s', $next_run ) : 'unknown',
							'scheduled_successfully' => true,
						)
					);
				} else {
					$this->logger->error( 'Failed to schedule status check CRON even with fallback' );
				}
			}
		}

		// =========================================================================
		// TIER 2: Daily Operations CRON (User Configurable Time)
		// =========================================================================
		// Purpose: Generate new articles, import ready ones, retry failed
		// Heavy operations - runs once per day
		if ( ! wp_next_scheduled( 'wisewand_editorial_calendar_cron' ) ) {
			$frequency  = 'daily';
			$daily_hour = $this->options->get( 'wisewand_cron_daily_hour', 2 );
			$daily_hour = max( 0, min( 23, $daily_hour ) ); // Ensure 0-23

			// Schedule for tomorrow at the specified hour
			$tomorrow_timestamp = strtotime( sprintf( 'tomorrow %02d:00', $daily_hour ) );

			$scheduled = wp_schedule_event( $tomorrow_timestamp, $frequency, 'wisewand_editorial_calendar_cron' );

			if ( $this->logger ) {
				if ( false !== $scheduled ) {
					$this->logger->info(
						'Editorial calendar CRON auto-scheduled',
						array(
							'frequency' => $frequency,
							'first_run' => gmdate( 'Y-m-d H:i:s', $tomorrow_timestamp ),
							'next_run'  => gmdate( 'Y-m-d H:i:s', wp_next_scheduled( 'wisewand_editorial_calendar_cron' ) ),
						)
					);
				} else {
					$this->logger->error(
						'Failed to schedule CRON',
						array(
							'frequency' => $frequency,
							'wp_error'  => 'wp_schedule_event returned false',
						)
					);
				}
			}
		}

		// =========================================================================
		// TIER 3: RSS Feeds Sync CRON (Adjustable Frequency)
		// =========================================================================
		// Purpose: Check RSS feeds and import new articles automatically
		// Default: every_5_minutes (Live), but respects global settings

		// v2.3.0: Simplified to single CRON hook (wisewand_rss_sync)
		// Removed: wisewand_rss_discover, wisewand_rss_import (legacy two-phase system)
		$rss_frequency = $this->options->get( 'wisewand_rss_default_import_frequency' );
		if ( empty( $rss_frequency ) ) {
			$rss_frequency = 'every_5_minutes';
		}

		// Single unified sync hook
		if ( ! wp_next_scheduled( 'wisewand_rss_sync' ) ) {
			wp_schedule_event( time(), $rss_frequency, 'wisewand_rss_sync' );
		} else {
			$schedule = wp_get_schedule( 'wisewand_rss_sync' );
			if ( $schedule !== $rss_frequency ) {
				wp_clear_scheduled_hook( 'wisewand_rss_sync' );
				wp_schedule_event( time(), $rss_frequency, 'wisewand_rss_sync' );
			}
		}

		// v2.3.0: Clean up legacy hooks from v2.2.0 if they exist
		if ( wp_next_scheduled( 'wisewand_rss_discover' ) ) {
			wp_clear_scheduled_hook( 'wisewand_rss_discover' );
		}
		if ( wp_next_scheduled( 'wisewand_rss_import' ) ) {
			wp_clear_scheduled_hook( 'wisewand_rss_import' );
		}
	}

	/**
	 * Reschedule RSS sync when the global import-frequency option changes.
	 *
	 * Hooked to update_option_wisewand_rss_default_import_frequency.
	 * After rescheduling the CRON hook, recalculates next_sync for all
	 * individual feeds that inherit the global frequency so they do not
	 * wait for the old interval to expire.
	 *
	 * @since 1.0.0
	 *
	 * @param mixed $old_value Old option value.
	 * @param mixed $value     New option value.
	 */
	public function reschedule_rss_sync( $old_value, $value ) {
		if ( $old_value === $value ) {
			return;
		}

		if ( ! empty( $value ) ) {
			// v2.3.0: Reschedule single unified hook
			wp_clear_scheduled_hook( 'wisewand_rss_sync' );
			wp_schedule_event( time(), $value, 'wisewand_rss_sync' );

			// v2.3.0: Clean up legacy hooks from v2.2.0
			wp_clear_scheduled_hook( 'wisewand_rss_discover' );
			wp_clear_scheduled_hook( 'wisewand_rss_import' );

			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( 'RSS_SYNC: Rescheduled wisewand_rss_sync to ' . $value );
			}

			// RSS v2.1 FIX: Also recalculate next_sync for all individual feeds inheriting this global setting.
			// Otherwise they wait for the OLD interval to expire.
			if ( $this->admin_manager && method_exists( $this->admin_manager, 'get_rss_page' ) ) {
				$rss_page = $this->admin_manager->get_rss_page();
				if ( $rss_page ) {
					// We need the Feed Manager. It's usually protected in RSS Page,
					// but let's check if we can access it or if we need to instantiate it freshly.
					// Since we are in the main plugin class (container), we might have access to components.

					// Better approach: Use the method we just added to Feed Manager.
					// The RSS Page has a getter for feed_manager? No.
					// But we can instantiate a fresh Feed Manager if needed, or rely on the one in RSS Page if accessible.

					// Let's rely on DI if possible, or simple instantiation.
					// Luckily, get_rss_page() returns the page instance.
					// Does RSS_Page expose feed_manager?
					// Checking RSS_Page source... usually private.

					// Alternative: Instantiate fresh manager.
					// Requires: RSS_Repository, Logger, Options.

					// Simplest: Check if we can use the existing component structure.
					// $this->rss_cron has feed_manager!
				}
			}

			// Direct Instantiation Approach (Reliable)
			if ( class_exists( 'Wisewand\\RSS\\RSS_Feed_Manager' ) ) {
				// Dependencies
				$logger     = new \Wisewand\Logging\Logger();
				$repository = new \Wisewand\Repositories\RSS_Repository(); // Assumes WPDB global

				// Fix: Correct order is (Logger, Repository). Options are not used in constructor.
				$feed_manager = new \Wisewand\RSS\RSS_Feed_Manager( $logger, $repository );
				$feed_manager->recalculate_global_feeds_next_sync();
			}
		}
	}

	/**
	 * Register custom WP-Cron intervals used by Wisewand.
	 *
	 * Hooked to the cron_schedules filter before ensure_cron_scheduled()
	 * is called so the custom intervals are available when scheduling.
	 *
	 * @since 2.3.0 Merged all custom schedules into a single method.
	 *
	 * @param array $schedules Existing WP-Cron schedules.
	 * @return array Modified schedules with Wisewand intervals added.
	 */
	public function add_cron_schedules( $schedules ) {
		// v2.3.0: Merged all custom schedules into single method (was split across 2 methods)
		// RSS feed synchronization schedules
		$schedules['every_5_minutes']  = array(
			'interval' => 300,
			'display'  => __( 'Every 5 Minutes (Wisewand RSS Live)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);
		$schedules['every_30_minutes'] = array(
			'interval' => 1800,
			'display'  => __( 'Every 30 Minutes (Wisewand)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);
		$schedules['every_6_hours']    = array(
			'interval' => 21600,
			'display'  => __( 'Every 6 Hours (Wisewand)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		// Status check and other schedules
		$schedules['every_15_minutes'] = array(
			'interval' => 900,
			'display'  => __( 'Every 15 Minutes (Wisewand Status Check)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		$schedules['wisewand_hourly'] = array(
			'interval' => 3600,
			'display'  => __( 'Every Hour (Wisewand)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		$schedules['wisewand_twice_daily'] = array(
			'interval' => 43200,
			'display'  => __( 'Twice Daily (Wisewand)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		$schedules['wisewand_four_times_daily'] = array(
			'interval' => 21600,
			'display'  => __( 'Four Times Daily (Wisewand)', 'wisewand-ai-content-writer-seo-content-generator' ),
		);

		return $schedules;
	}
}
