<?php
/**
 * Date Time Condition
 *
 * @package GenerateBlocks
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Date/time condition evaluator.
 */
class GenerateBlocks_Pro_Condition_Date_Time extends GenerateBlocks_Pro_Condition_Abstract {
	/**
	 * Evaluate the condition.
	 *
	 * @param string $rule     The condition rule.
	 * @param string $operator The condition operator.
	 * @param mixed  $value    The value to check against.
	 * @param array  $context  Additional context data.
	 * @return bool
	 */
	public function evaluate( $rule, $operator, $value, $context = [] ) {
		// Handle day of week separately with multi-select support.
		if ( 'day_of_week' === $rule ) {
			return $this->evaluate_day_of_week( $operator, $value );
		}

		// Get current time as DateTime object for proper timezone handling.
		$current_datetime = current_datetime();
		$current_time = $current_datetime->getTimestamp();

		if ( empty( $value ) ) {
			return false;
		}

		switch ( $operator ) {
			case 'before':
				return $this->is_before( $current_time, $value );

			case 'after':
				return $this->is_after( $current_time, $value );

			case 'on':
				return $this->is_on( $current_time, $value, $rule );

			case 'between':
				return $this->is_between( $current_time, $value );

			default:
				return false;
		}
	}

	/**
	 * Evaluate day of week condition.
	 *
	 * @param string $operator The condition operator.
	 * @param mixed  $value    The value to check against.
	 * @return bool
	 */
	private function evaluate_day_of_week( $operator, $value ) {
		// Get current day of week (1 = Monday, 7 = Sunday).
		$current_day = intval( current_datetime()->format( 'N' ) );

		// Handle multi-value operators.
		if ( $this->is_multi_value_operator( $operator ) ) {
			return $this->evaluate_multi_value( $operator, $current_day, $value );
		}

		// Parse value for single operators.
		$target_day = intval( $value );

		switch ( $operator ) {
			case 'is':
				return $current_day === $target_day;

			case 'is_not':
				return $current_day !== $target_day;

			default:
				return false;
		}
	}

	/**
	 * Check if current time is before target time.
	 *
	 * @param int    $current_time Current timestamp.
	 * @param string $target_time  Target time string.
	 * @return bool
	 */
	private function is_before( $current_time, $target_time ) {
		$compare_time = strtotime( $target_time );
		return $compare_time && $current_time < $compare_time;
	}

	/**
	 * Check if current time is after target time.
	 *
	 * @param int    $current_time Current timestamp.
	 * @param string $target_time  Target time string.
	 * @return bool
	 */
	private function is_after( $current_time, $target_time ) {
		$compare_time = strtotime( $target_time );
		return $compare_time && $current_time > $compare_time;
	}

	/**
	 * Check if current time is on target date.
	 *
	 * @param int    $current_time Current timestamp.
	 * @param string $target_time  Target time string.
	 * @param string $rule         The rule type.
	 * @return bool
	 */
	private function is_on( $current_time, $target_time, $rule ) {
		$compare_time = strtotime( $target_time );
		if ( ! $compare_time ) {
			return false;
		}

		if ( 'current_date' === $rule ) {
			return gmdate( 'Y-m-d', $current_time ) === gmdate( 'Y-m-d', $compare_time );
		}

		// For time comparison, check if within the same hour.
		return gmdate( 'Y-m-d H', $current_time ) === gmdate( 'Y-m-d H', $compare_time );
	}

	/**
	 * Check if current time is between two times.
	 *
	 * @param int    $current_time Current timestamp.
	 * @param string $value        Comma-separated start and end times.
	 * @return bool
	 */
	private function is_between( $current_time, $value ) {
		$dates = array_map( 'trim', explode( ',', $value ) );
		if ( 2 !== count( $dates ) ) {
			return false;
		}

		$start_time = strtotime( $dates[0] );
		$end_time   = strtotime( $dates[1] );

		if ( ! $start_time || ! $end_time ) {
			return false;
		}

		return $current_time >= $start_time && $current_time <= $end_time;
	}

	/**
	 * Get available rules for this condition type.
	 *
	 * @return array
	 */
	public function get_rules() {
		$rules = [
			'current_date' => __( 'Current Date', 'generateblocks-pro' ),
			'current_time' => __( 'Current Time', 'generateblocks-pro' ),
			'day_of_week'  => __( 'Day of Week', 'generateblocks-pro' ),
			'time_of_day'  => __( 'Time of Day', 'generateblocks-pro' ),
		];

		return apply_filters( 'generateblocks_date_time_rules', $rules );
	}

	/**
	 * Get metadata for a specific rule.
	 *
	 * @param string $rule The rule key.
	 * @return array
	 */
	public function get_rule_metadata( $rule ) {
		if ( 'day_of_week' === $rule ) {
			return [
				'needs_value'     => true,
				'value_type'      => 'day_selector',
				'supports_multi'  => true,
			];
		}

		return [
			'needs_value' => true,
			'value_type'  => 'datetime',
		];
	}

	/**
	 * Get operators available for a specific date/time rule.
	 *
	 * @param string $rule The rule key.
	 * @return array Array of operator keys.
	 */
	public function get_operators_for_rule( $rule ) {
		if ( 'day_of_week' === $rule ) {
			return [ 'is', 'is_not', 'includes_any', 'excludes_any' ];
		}

		// Other date/time rules use temporal operators.
		return [ 'before', 'after', 'between', 'on' ];
	}

	/**
	 * Get available day options.
	 *
	 * @return array
	 */
	public function get_day_options() {
		return [
			'1' => __( 'Monday', 'generateblocks-pro' ),
			'2' => __( 'Tuesday', 'generateblocks-pro' ),
			'3' => __( 'Wednesday', 'generateblocks-pro' ),
			'4' => __( 'Thursday', 'generateblocks-pro' ),
			'5' => __( 'Friday', 'generateblocks-pro' ),
			'6' => __( 'Saturday', 'generateblocks-pro' ),
			'7' => __( 'Sunday', 'generateblocks-pro' ),
		];
	}

	/**
	 * Sanitize the condition value.
	 *
	 * @param mixed  $value The value to sanitize.
	 * @param string $rule  The rule being used.
	 * @return mixed
	 */
	public function sanitize_value( $value, $rule ) {
		if ( 'day_of_week' === $rule ) {
			// Handle array values for multi-select.
			if ( is_array( $value ) ) {
				return array_map( 'intval', $value );
			}

			// Try to decode JSON array.
			$decoded = json_decode( $value, true );
			if ( is_array( $decoded ) ) {
				return wp_json_encode( array_map( 'intval', $decoded ) );
			}

			// Single value.
			return intval( $value );
		}

		// For between operator, ensure proper format.
		if ( false !== strpos( $value, ',' ) ) {
			$dates = array_map( 'trim', explode( ',', $value ) );
			$sanitized_dates = array_map( 'sanitize_text_field', $dates );
			return implode( ', ', $sanitized_dates );
		}

		return sanitize_text_field( $value );
	}
}
