<?php
/**
 * This file is part of the Wisewand WordPress plugin.
 *
 * @package Wisewand
 * @since 1.0.0
 * @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\RSS;

use Wisewand\Utils\Publish_Mode_Migrator;

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

use Wisewand\Utils\Options;

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

/**
 * RSS Publication Scheduler
 *
 * Calculates when RSS articles should be published based on time and day restrictions.
 * Reuses publication settings from Calendar (DRY principle) or uses custom per-feed settings.
 *
 * Example Use Case:
 * - Article imported at 1 AM Saturday
 * - Settings: Morning 9-12, Mon-Fri only
 * - Result: Scheduled for Monday 10:23 AM
 *
 * @package Wisewand
 * @subpackage RSS
 * @since 1.0.0
 */
class RSS_Publication_Scheduler {

	/**
	 * Options instance
	 *
	 * @var Options
	 */
	private $options;

	/**
	 * Constructor
	 *
	 * @param Options $options Options instance for accessing settings
	 */
	public function __construct( $options ) {
		$this->options = $options;
	}

	/**
	 * Calculate next allowed publication time for an RSS article
	 *
	 * Uses either custom feed settings or global Calendar settings.
	 * Respects time range (morning/afternoon/evening) and allowed days (Mon-Fri, etc).
	 *
	 * @param array       $feed         Feed settings from database
	 * @param string|null $imported_at  When article was imported (MySQL datetime)
	 * @return string MySQL datetime for scheduled publication
	 */
	public function calculate_publication_time( $feed, $imported_at = null ) {
		// Determine which publication settings to use
		if ( ! empty( $feed['use_custom_publication_schedule'] ) ) {
			// Use custom feed settings
			$time_range        = $feed['publication_time_range'] ?? 'random';
			$allowed_days_json = $feed['publication_allowed_days'] ?? null;
			$allowed_days      = $allowed_days_json ? json_decode( $allowed_days_json, true ) : array( '1', '2', '3', '4', '5' );

			if ( WP_DEBUG ) {
				error_log( 'RSS_SCHEDULER: Using custom schedule for feed ' . $feed['id'] );
				error_log( 'RSS_SCHEDULER: Time range: ' . $time_range . ', Allowed days: ' . implode( ',', $allowed_days ) );
			}
		} else {
			// Use global Calendar settings (DRY - reuse existing settings!)
			$time_range   = $this->options->get( 'wisewand_calendar_time_range', 'random' );
			$allowed_days = $this->options->get( 'wisewand_allowed_publication_days', array( '1', '2', '3', '4', '5' ) );

			if ( WP_DEBUG ) {
				error_log( 'RSS_SCHEDULER: Using global Calendar settings' );
				error_log( 'RSS_SCHEDULER: Time range: ' . $time_range . ', Allowed days: ' . implode( ',', $allowed_days ) );
			}
		}

		// Calculate next allowed publication time
		$scheduled_time = $this->find_next_allowed_time( $time_range, $allowed_days, $imported_at );

		if ( WP_DEBUG ) {
			error_log( 'RSS_SCHEDULER: Imported at: ' . ( $imported_at ?? current_time( 'mysql' ) ) );
			error_log( 'RSS_SCHEDULER: Scheduled for: ' . $scheduled_time );
		}

		return $scheduled_time;
	}

	/**
	 * Find next allowed publication time
	 *
	 * Searches up to 14 days ahead to find the next allowed day, then generates
	 * a random time within the allowed time range.
	 *
	 * @param string      $time_range   Time range: morning/afternoon/evening/random
	 * @param array       $allowed_days Array of allowed days (1=Monday, 7=Sunday)
	 * @param string|null $from_time    Start time for search (MySQL datetime or null for now)
	 * @return string MySQL datetime for publication
	 */
	private function find_next_allowed_time( $time_range, $allowed_days, $from_time ) {
		// v1.2.5: CRITICAL FIX - Always use current time as minimum starting point
		$specified_time = strtotime( $from_time ?? current_time( 'mysql' ) );
		$current_time   = current_time( 'timestamp' );
		$current        = max( $specified_time, $current_time ); // Never search from past
		$max_days_ahead = 14; // Search up to 2 weeks ahead

		if ( WP_DEBUG ) {
			error_log( 'RSS_SCHEDULER: Searching for next allowed time from ' . wp_date( 'Y-m-d H:i:s', $current ) );
		}

		for ( $i = 0; $i < $max_days_ahead; $i++ ) {
			$candidate   = strtotime( "+{$i} days", $current );
			$day_of_week = wp_date( 'N', $candidate ); // 1=Monday, 7=Sunday (WordPress timezone)

			if ( WP_DEBUG ) {
				error_log( 'RSS_SCHEDULER: Checking day ' . wp_date( 'Y-m-d (D)', $candidate ) . ' - day_of_week: ' . $day_of_week );
			}

			// Check if day is allowed
			if ( in_array( $day_of_week, $allowed_days, true ) ) {
				// Calculate time within range (generate_time_in_range now includes future check)
				$time   = $this->generate_time_in_range( $time_range, $candidate );
				$result = wp_date( 'Y-m-d H:i:s', $time );

				if ( WP_DEBUG ) {
					error_log( 'RSS_SCHEDULER: Found allowed day! Scheduled for: ' . $result );
				}

				return $result;
			}
		}

		// Fallback: tomorrow at 9 AM (should never reach here)
		$tomorrow_timestamp = strtotime( 'tomorrow', current_time( 'timestamp' ) );
		$fallback           = wp_date( 'Y-m-d 09:00:00', $tomorrow_timestamp );

		if ( WP_DEBUG ) {
			error_log( 'RSS_SCHEDULER: WARNING - No allowed day found in 14 days! Using fallback: ' . $fallback );
		}

		return $fallback;
	}

	/**
	 * Generate random time within range
	 *
	 * REUSES EXACT logic from Calendar Roadmap_Generator (DRY principle)
	 *
	 * @param string $range          Time range: morning/afternoon/evening/random
	 * @param int    $date_timestamp Unix timestamp of the date
	 * @return int Unix timestamp with random time in range
	 */
	private function generate_time_in_range( $range, $date_timestamp ) {
		// v1.2.2: Fixed time ranges - end times are exclusive (strictly before)
		$ranges = array(
			'random'    => array( '09:00:00', '19:59:59' ),  // 9 AM - 7:59:59 PM (strictly before 8 PM)
			'morning'   => array( '09:00:00', '11:59:59' ),  // 9 AM - 11:59:59 AM
			'afternoon' => array( '12:00:00', '16:59:59' ),  // 12 PM - 4:59:59 PM
			'evening'   => array( '17:00:00', '19:59:59' ),  // 5 PM - 7:59:59 PM
		);

		$time_range = $ranges[ $range ] ?? $ranges['random'];
		$date_str   = wp_date( 'Y-m-d', $date_timestamp );
		$start      = strtotime( $date_str . ' ' . $time_range[0] );
		$end        = strtotime( $date_str . ' ' . $time_range[1] ); // Reuse $date_str from line above

		$random_time = wp_rand( $start, $end );

		// v1.2.5: CRITICAL FIX - Ensure scheduled time is ALWAYS in the FUTURE
		$now_timestamp = current_time( 'timestamp' );

		if ( $random_time <= $now_timestamp ) {
			// Generated time is NOW or PAST - schedule for tomorrow instead
			if ( WP_DEBUG ) {
				error_log(
					sprintf(
						'RSS_SCHEDULER: Generated time (%s) is in past/now (%s) - moving to next day',
						wp_date( 'Y-m-d H:i:s', $random_time ),
						current_time( 'mysql' )
					)
				);
			}

			// Add 24 hours to move to tomorrow
			$random_time = strtotime( '+1 day', $random_time );
		}

		return $random_time;
	}

	/**
	 * Check if current time is within allowed publication window
	 *
	 * Used to determine if an article can be published immediately vs scheduled.
	 *
	 * @param array $feed Feed settings
	 * @return bool True if current time is allowed, false otherwise
	 */
	public function is_allowed_time_now( $feed ) {
		// Get publication settings
		if ( ! empty( $feed['use_custom_publication_schedule'] ) ) {
			$time_range        = $feed['publication_time_range'] ?? 'random';
			$allowed_days_json = $feed['publication_allowed_days'] ?? null;
			$allowed_days      = $allowed_days_json ? json_decode( $allowed_days_json, true ) : array( '1', '2', '3', '4', '5' );
		} else {
			$time_range   = $this->options->get( 'wisewand_calendar_time_range', 'random' );
			$allowed_days = $this->options->get( 'wisewand_allowed_publication_days', array( '1', '2', '3', '4', '5' ) );
		}

		// Check if today is an allowed day
		$today = wp_date( 'N', current_time( 'timestamp' ) ); // 1=Monday, 7=Sunday (WordPress timezone)
		if ( ! in_array( $today, $allowed_days, true ) ) {
			return false;
		}

		// Check if current time is in allowed range
		// v1.2.2: Fixed time ranges - end times are exclusive (strictly before)
		$ranges = array(
			'random'    => array( '09:00:00', '19:59:59' ),
			'morning'   => array( '09:00:00', '11:59:59' ),
			'afternoon' => array( '12:00:00', '16:59:59' ),
			'evening'   => array( '17:00:00', '19:59:59' ),
		);

		$time_range_hours = $ranges[ $time_range ] ?? $ranges['random'];
		$now              = current_time( 'timestamp' );
		$today_date       = wp_date( 'Y-m-d', current_time( 'timestamp' ) );
		$start            = strtotime( $today_date . ' ' . $time_range_hours[0] );
		$end              = strtotime( $today_date . ' ' . $time_range_hours[1] ); // Reuse $today_date

		return ( $now >= $start && $now <= $end );
	}

	/**
	 * Format scheduled time for human reading
	 *
	 * @param string $scheduled_time MySQL datetime
	 * @return string Human-readable time (e.g., "in 2 hours" or "Monday at 10:23 AM")
	 */
	public function format_scheduled_time( $scheduled_time ) {
		$timestamp = strtotime( $scheduled_time );
		$now       = current_time( 'timestamp' );
		$diff      = $timestamp - $now;

		// If within 24 hours, show relative time
		if ( $diff > 0 && $diff < 86400 ) {
			return 'in ' . human_time_diff( $now, $timestamp );
		}

		// Otherwise show date and time
		return wp_date( 'l \a\t g:i A', $timestamp );
	}

	/**
	 * Calculate simple time-based publication schedule
	 *
	 * NEW in v2.1.3: Simpler scheduling based on global or per-feed time settings.
	 * Replaces complex time range/day restrictions with simple daily time scheduling.
	 *
	 * How it works:
	 * 1. Check global publish_mode (immediate, draft, scheduled)
	 * 2. If scheduled, get global publish_time (default: 11:00)
	 * 3. Check feed's schedule_mode (inherit, custom)
	 * 4. If custom, use feed's schedule_time instead
	 * 5. Return appropriate post_date for WordPress
	 *
	 * @since 2.1.3
	 * @param array $feed Feed settings from database.
	 * @return string|null {
	 *     MySQL datetime for scheduled publication, or null for other modes.
	 *
	 *     - null = Use 'immediate' or 'draft' mode (no scheduling)
	 *     - string = Schedule for specific date/time (YYYY-MM-DD HH:MM:SS)
	 * }
	 */
	public function calculate_simple_schedule( $feed ) {
		// Get global publish mode
		$publish_mode = Publish_Mode_Migrator::get_publish_mode_with_fallback( 'wisewand_rss_publish_mode', 'immediate' );

		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
			error_log( sprintf(
				'Wisewand Publication Scheduler: Feed #%d - global publish_mode: %s',
				$feed['id'] ?? 0,
				$publish_mode
			) );
		}

		// If not scheduled mode, return null (article will be published immediately or as draft)
		if ( 'scheduled' !== $publish_mode ) {
			return null;
		}

		// Get schedule time - check for per-feed override first
		$schedule_time = null;

		if ( ! empty( $feed['schedule_mode'] ) && 'custom' === $feed['schedule_mode'] ) {
			// Use per-feed custom time
			$schedule_time = $feed['schedule_time'] ?? null;

			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( sprintf(
					'Wisewand Publication Scheduler: Feed #%d using CUSTOM schedule_time: %s',
					$feed['id'] ?? 0,
					$schedule_time ? $schedule_time : 'null (ERROR)'
				) );
			}
		}

		// If no custom time, use global time
		if ( empty( $schedule_time ) ) {
			$schedule_time = $this->options->get( 'wisewand_rss_publish_time', '11:00' );

			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( sprintf(
					'Wisewand Publication Scheduler: Feed #%d using GLOBAL schedule_time: %s',
					$feed['id'] ?? 0,
					$schedule_time
				) );
			}
		}

		// Validate time format
		if ( ! \Wisewand\Utils\Schedule_Validator::validate_time_format( $schedule_time ) ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( sprintf(
					'Wisewand Publication Scheduler: Invalid schedule_time "%s" for feed #%d - falling back to 11:00',
					$schedule_time,
					$feed['id'] ?? 0
				) );
			}
			$schedule_time = '11:00';
		}

		// Convert to WordPress timezone DateTime
		$datetime = \Wisewand\Utils\Timezone_Helper::get_wp_datetime( $schedule_time );

		if ( false === $datetime ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				error_log( sprintf(
					'Wisewand Publication Scheduler: Failed to parse schedule_time "%s" - using fallback',
					$schedule_time
				) );
			}
			// Fallback: tomorrow at 11:00 AM
			$datetime = \Wisewand\Utils\Timezone_Helper::get_wp_datetime( '11:00' );
		}

		// Format for WordPress post_date
		$post_date = \Wisewand\Utils\Timezone_Helper::format_for_post_date( $datetime );

		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
			error_log( sprintf(
				'Wisewand Publication Scheduler: Feed #%d scheduled for: %s (WordPress timezone: %s)',
				$feed['id'] ?? 0,
				$post_date,
				\Wisewand\Utils\Timezone_Helper::get_timezone_string()
			) );
		}

		return $post_date;
	}
}
