<?php
/**
 * Class WorkingDayDateCalculator
 *
 * @package DhlPolska\Shipment
 */

namespace DhlPolska\Shipment;

use DateTimeImmutable;
use DateTimeZone;

/**
 * Calculates shipment dates based on working days and cutoff time.
 */
class WorkingDayDateCalculator {
	private const TIME_PATTERN = '/^([01][0-9]|2[0-3]):[0-5][0-9]$/';

	private DateTimeZone $timezone;

	public function __construct( ?DateTimeZone $timezone = null ) {
		$this->timezone = $timezone ?? wp_timezone();
	}

	public function get_date_after_cutoff( ?string $cutoff_time, ?DateTimeImmutable $now = null ): ?string {
		$cutoff_time = is_string( $cutoff_time ) ? trim( $cutoff_time ) : '';
		if ( '' === $cutoff_time || ! $this->is_valid_time_format( $cutoff_time ) ) {
			return null;
		}

		$now  = $now ?? new DateTimeImmutable( 'now', $this->timezone );
		$date = $this->get_working_date( $now, $cutoff_time );

		return $date->format( 'Y-m-d' );
	}

	private function get_working_date( DateTimeImmutable $now, string $cutoff_time ): DateTimeImmutable {
		if ( ! $this->is_working_day( $now ) ) {
			return $this->next_working_day( $now );
		}

		if ( $this->is_after_cutoff( $now, $cutoff_time ) ) {
			return $this->next_working_day( $now );
		}

		return $now;
	}

	private function is_after_cutoff( DateTimeImmutable $now, string $cutoff_time ): bool {
		$cutoff = DateTimeImmutable::createFromFormat(
			'Y-m-d H:i',
			$now->format( 'Y-m-d' ) . ' ' . $cutoff_time,
			$this->timezone
		);

		if ( false === $cutoff ) {
			return false;
		}

		return $now->getTimestamp() >= $cutoff->getTimestamp();
	}

	private function is_working_day( DateTimeImmutable $date ): bool {
		$weekday = (int) $date->format( 'N' );

		return $weekday <= 5;
	}

	private function next_working_day( DateTimeImmutable $date ): DateTimeImmutable {
		$candidate = $date->modify( '+1 day' );

		while ( ! $this->is_working_day( $candidate ) ) {
			$candidate = $candidate->modify( '+1 day' );
		}

		return $candidate;
	}

	private function is_valid_time_format( string $value ): bool {
		return 1 === preg_match( self::TIME_PATTERN, $value );
	}
}
