<?php

namespace GemBoards\Utils\Traits;

use DateTimeImmutable;
use DateTimeZone;
use Exception;
use GemBoards\Classes\Datetime;
use GemBoards\Classes\Exception\GemboardsException;

trait DatetimeHelper {

	public static function is_datetime( ?string $maybe_datetime ): bool {
		/** @noinspection RegExpSingleCharAlternation */
		return $maybe_datetime && preg_match( '/^(\d{4})-(\d{2})-(\d{2}).*(\d{2}):(\d{2}):(\d{2})(Z|((-|\+)\d{2}:\d{2}))?$/', $maybe_datetime );
	}

	/**
	 * Convert a date string to a Datetime.
	 *
	 * @param string|int|null $time_string Time string.
	 *
	 * @return Datetime
	 * @throws GemboardsException
	 */
	public static function string_to_datetime( $time_string = null ): Datetime {
		try {
			$time_string = $time_string ?? '';

			if ( is_a( $time_string, Datetime::class ) ) {
				$datetime = clone $time_string;
			} elseif ( is_numeric( $time_string ) ) {
				// Timestamps are handled as UTC timestamps in all cases.
				$datetime = new Datetime( "@$time_string", new DateTimeZone( 'UTC' ) );
			} else {
				// Strings are defined in local WP timezone. Convert to UTC.
				/** @noinspection RegExpSingleCharAlternation */
				if ( 1 === preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|((-|\+)\d{2}:\d{2}))$/', $time_string, $date_bits ) ) {
					$offset    = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : self::timezone_offset();
					$timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset;
				} else {
					$timestamp = self::string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', self::string_to_timestamp( $time_string ) ) ) );
				}

				$datetime = new Datetime( "@$timestamp", new DateTimeZone( 'UTC' ) );
			}

			// Set local timezone
			$datetime->setTimezone( wp_timezone() );

			return $datetime;
		} catch ( Exception $e ) {
			// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped
			throw new GemboardsException( esc_html($e->getMessage()), 'failed-to-convert-string-into-datetime', [ 'datetime_string' => esc_html($time_string) ], esc_html($e->getCode()), $e );
			// phpcs:enable
		}
	}

	/**
	 * Convert mysql datetime to PHP timestamp, forcing UTC. Wrapper for strtotime.
	 * Based on wcs_strtotime_dark_knight() from WC Subscriptions by Prospress.
	 *
	 * @param string|null $time_string Time string.
	 * @param int|null $from_timestamp Timestamp to convert from.
	 *
	 * @return int
	 * @noinspection SpellCheckingInspection
	 */
	public static function string_to_timestamp(
		?string $time_string = null,
		?int $from_timestamp = null
	): int {
		if ( empty( $time_string ) ) {
			return 0;
		}

		try {
			$timezone = new DateTimeZone( 'UTC' );

			if ( null !== $from_timestamp ) {
				$base = ( new DateTimeImmutable( '@' . $from_timestamp ) )
					->setTimezone( $timezone );

				$datetime = new DateTimeImmutable( $time_string, $timezone );
				$datetime = $datetime->modify( $time_string )->setTimestamp( $base->getTimestamp() );
			} else {
				$datetime = new DateTimeImmutable( $time_string, $timezone );
			}

			return $datetime->getTimestamp();
		} catch ( Exception $e ) {
			return 0;
		}
	}

	/**
	 * Get timezone offset in seconds.
	 *
	 * @return float
	 * @throws Exception
	 */
	public static function timezone_offset() {
		$timezone = get_option( 'timezone_string' );

		if ( $timezone ) {
			$timezone_object = new DateTimeZone( $timezone );

			return $timezone_object->getOffset( new DateTime( 'now' ) );
		} else {
			return floatval( get_option( 'gmt_offset', 0 ) ) * HOUR_IN_SECONDS;
		}
	}

}
