<?php
/**
 * QuietHours — defers queued sends outside a configurable daily window.
 *
 * @package CartCue
 */

namespace CartCue;

defined( 'ABSPATH' ) || exit;

final class QuietHours {

	public static function default_settings(): array {
		return [
			'quiet_hours_enabled' => '0',
			'quiet_hours_start'   => '21:00',
			'quiet_hours_end'     => '08:00',
		];
	}

	/**
	 * @return array<string, string>
	 */
	public static function get_settings(): array {
		$stored = get_option( 'cartcue_settings', [] );
		if ( ! is_array( $stored ) ) {
			$stored = [];
		}

		return array_merge( self::default_settings(), $stored );
	}

	public static function is_enabled(): bool {
		$s = self::get_settings();
		return '1' === (string) ( $s['quiet_hours_enabled'] ?? '0' );
	}

	/**
	 * Extra seconds to add so $execute_at lands outside the quiet window.
	 *
	 * @param array<string, mixed> $data Trigger payload (honours _is_test bypass).
	 */
	public static function extra_delay_seconds( int $execute_at, array $data = [] ): int {
		if ( ! self::is_enabled() || ! empty( $data['_is_test'] ) ) {
			return 0;
		}

		$s     = self::get_settings();
		$start = self::parse_time( (string) ( $s['quiet_hours_start'] ?? '21:00' ) );
		$end   = self::parse_time( (string) ( $s['quiet_hours_end'] ?? '08:00' ) );

		$tz = wp_timezone();
		$dt = ( new \DateTimeImmutable( '@' . $execute_at ) )->setTimezone( $tz );

		if ( ! self::is_in_quiet_window( $dt, $start, $end ) ) {
			return 0;
		}

		$allowed = self::next_allowed_time( $dt, $start, $end );

		return max( 0, $allowed->getTimestamp() - $execute_at );
	}

	/**
	 * @return array{hour: int, minute: int}
	 */
	private static function parse_time( string $hhmm ): array {
		$parts = explode( ':', $hhmm );

		return [
			'hour'   => min( 23, max( 0, (int) ( $parts[0] ?? 0 ) ) ),
			'minute' => min( 59, max( 0, (int) ( $parts[1] ?? 0 ) ) ),
		];
	}

	/**
	 * @param array{hour: int, minute: int} $start
	 * @param array{hour: int, minute: int} $end
	 */
	private static function is_in_quiet_window( \DateTimeImmutable $dt, array $start, array $end ): bool {
		$minutes = (int) $dt->format( 'G' ) * 60 + (int) $dt->format( 'i' );
		$start_m = $start['hour'] * 60 + $start['minute'];
		$end_m   = $end['hour'] * 60 + $end['minute'];

		if ( $start_m === $end_m ) {
			return false;
		}

		if ( $start_m < $end_m ) {
			return $minutes >= $start_m && $minutes < $end_m;
		}

		return $minutes >= $start_m || $minutes < $end_m;
	}

	/**
	 * @param array{hour: int, minute: int} $start
	 * @param array{hour: int, minute: int} $end
	 */
	private static function next_allowed_time( \DateTimeImmutable $dt, array $start, array $end ): \DateTimeImmutable {
		$minutes = (int) $dt->format( 'G' ) * 60 + (int) $dt->format( 'i' );
		$end_m   = $end['hour'] * 60 + $end['minute'];

		$candidate = $dt->setTime( $end['hour'], $end['minute'], 0 );

		if ( $minutes < $end_m ) {
			return $candidate;
		}

		return $candidate->modify( '+1 day' );
	}
}
