<?php
/**
 * RSS Cleanup CRON - Automatic recovery for stuck articles
 *
 * Resets articles stuck in "importing" state back to "pending"
 * so they can be retried on next sync. Prevents permanent stuck state.
 *
 * FIX REL-005: Implement cleanup job for stuck articles
 *
 * @package Wisewand
 * @since 2.1.2
 * @license GPL-2.0-or-later
 */

namespace Wisewand\RSS;

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

// phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_error_log, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- error_log() only used within WP_DEBUG conditionals; direct queries needed for cleanup

/**
 * RSS Cleanup CRON class
 *
 * Handles automatic cleanup of articles stuck in importing state.
 * Runs hourly to reset stuck articles for retry.
 *
 * @since 2.1.2
 */
class RSS_Cleanup_CRON {

	/**
	 * CRON hook name
	 *
	 * @since 2.1.2
	 * @var string
	 */
	const CRON_HOOK = 'wisewand_rss_cleanup_stuck_articles';

	/**
	 * Time threshold (articles importing longer than this are considered stuck)
	 *
	 * @since 2.1.2
	 * @var int Seconds (1 hour = 3600)
	 */
	const STUCK_THRESHOLD = 3600;

	/**
	 * Constructor
	 *
	 * Registers CRON hook.
	 *
	 * @since 2.1.2
	 */
	public function __construct() {
		add_action( self::CRON_HOOK, array( $this, 'cleanup_stuck_articles' ) );
	}

	/**
	 * Cleanup articles stuck in importing state
	 *
	 * Resets articles that have been in "importing" state for more than 1 hour.
	 * These are likely from interrupted sync operations (timeout, server restart, etc.)
	 *
	 * @since 2.1.2
	 * @global wpdb $wpdb WordPress database abstraction object.
	 * @return int Number of articles reset.
	 */
	public function cleanup_stuck_articles() {
		global $wpdb;

		$table_name = $wpdb->prefix . 'wisewand_rss_articles';

		// Find articles stuck in importing state for over 1 hour
		// v2.1.2: FIX REL-005 - Automatic recovery for interrupted imports
		// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name from $wpdb->prefix is safe
		$reset_count = $wpdb->query(
			$wpdb->prepare(
				"UPDATE `{$table_name}`
				SET import_status = 'pending'
				WHERE import_status = 'importing'
				AND updated_at < DATE_SUB(NOW(), INTERVAL %d SECOND)",
				self::STUCK_THRESHOLD
			)
		);

		// Log cleanup action
		if ( $reset_count > 0 ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( sprintf( 'Wisewand RSS Cleanup: Reset %d stuck articles from importing to pending', $reset_count ) );
			}

			// Store last cleanup stats for monitoring
			update_option( 'wisewand_last_cleanup_run', array(
				'time'          => current_time( 'mysql' ),
				'articles_reset' => $reset_count,
			) );
		}

		return $reset_count;
	}

	/**
	 * Schedule the cleanup CRON job
	 *
	 * Called during plugin activation.
	 *
	 * @since 2.1.2
	 */
	public static function schedule() {
		if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
			wp_schedule_event( time(), 'hourly', self::CRON_HOOK );

			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( 'Wisewand RSS Cleanup: Scheduled hourly cleanup CRON' );
			}
		}
	}

	/**
	 * Unschedule the cleanup CRON job
	 *
	 * Called during plugin deactivation.
	 *
	 * @since 2.1.2
	 */
	public static function unschedule() {
		$timestamp = wp_next_scheduled( self::CRON_HOOK );
		if ( $timestamp ) {
			wp_unschedule_event( $timestamp, self::CRON_HOOK );

			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( 'Wisewand RSS Cleanup: Unscheduled cleanup CRON' );
			}
		}
	}

	/**
	 * Get last cleanup run statistics
	 *
	 * Useful for monitoring cleanup job effectiveness.
	 *
	 * @since 2.1.2
	 * @return array|null {
	 *     Last cleanup run stats, or null if never run.
	 *
	 *     @type string $time          Timestamp of last run.
	 *     @type int    $articles_reset Number of articles reset.
	 * }
	 */
	public static function get_last_run_stats() {
		return get_option( 'wisewand_last_cleanup_run', null );
	}
}
