<?php
/**
 * File responsible for defining utility class and methods.
 *
 * Author:          Uriahs Victor
 * Created on:      09/06/2023 (d/m/y)
 *
 * @link    https://uriahsvictor.com
 * @since   1.0.8
 * @package Helpers
 */

namespace Lpac_DPS\Helpers;

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

use SoaringLeads\Chwazi\Carbon\Carbon;
use SoaringLeads\Chwazi\Carbon\Exceptions\InvalidFormatException;
use SoaringLeads\Chwazi\Carbon\Exceptions\InvalidTimeZoneException;
use DateTime;

/**
 * Class that defines Utility methods.
 *
 * @package Lpac_DPS\Helpers
 * @since 1.0.8
 */
class UtilitiesHelper {

	/**
	 * Convert time to 24 hour format.
	 *
	 * @param string $time
	 * @param string $return_format
	 *
	 * @return string
	 * @since 1.2.4
	 */
	public static function convertTo24HourFormat( string $time, string $return_format = 'H:i:s' ): string {
		return Carbon::parse( $time, FunctionsHelper::getTimezone() )->format( $return_format );
	}

	/**
	 * Convert a date_time string to UTC.
	 *
	 * @param string $date_time In format Y-m-d H:i:s
	 * @return null|string
	 * @throws InvalidTimeZoneException
	 * @since 1.2.0
	 */
	public static function convertDateTimeToUTC( string $date_time ): ?string {
		return Carbon::createFromFormat( 'Y-m-d H:i:s', $date_time, FunctionsHelper::getTimezone() )->setTimezone( 'UTC' )->format( 'Y-m-d H:i:s' );
	}

	/**
	 * Check if a time is greater than or equal to another.
	 *
	 * @param string $time1 The time to start the comparison.
	 * @param string $time2 The time to compare against.
	 * @return bool
	 * @since 1.2.0
	 */
	public static function timeIsGreaterThanOrEqualTo( string $time1, string $time2 ): bool {
		$first  = Carbon::createFromFormat( 'Y-m-d H:i:s', $time1, 'UTC' );
		$second = Carbon::createFromFormat( 'Y-m-d H:i:s', $time2, 'UTC' );
		return $first->greaterThanOrEqualTo( $second );
	}

	/**
	 * Check if datetime1 is less than $datetime2
	 *
	 * @param string $datetime1 The time to start the comparison.
	 * @param string $datetime2 The time to compare against.
	 * @return bool
	 * @since 1.2.0
	 */
	public static function timeIsLessThan( string $datetime1, string $datetime2 ): bool {
		$first  = Carbon::createFromFormat( 'Y-m-d H:i:s', $datetime1, 'UTC' );
		$second = Carbon::createFromFormat( 'Y-m-d H:i:s', $datetime2, 'UTC' );
		return $first->lessThan( $second );
	}

	/**
	 * Check if a time is less than or equal to another.
	 *
	 * @param string $time1 The time to start the comparison.
	 * @param string $time2 The time to compare against.
	 * @return bool
	 * @since 1.2.0
	 */
	public static function timeIsLessThanOrEqualTo( string $time1, string $time2 ): bool {
		$first  = Carbon::createFromFormat( 'Y-m-d H:i:s', $time1, 'UTC' );
		$second = Carbon::createFromFormat( 'Y-m-d H:i:s', $time2, 'UTC' );
		return $first->lessThanOrEqualTo( $second );
	}

	/**
	 * Get the current time with a number of minutes added.
	 *
	 * @param int    $minutes
	 * @param string $return_format
	 * @return string
	 * @since 1.2.0
	 */
	public static function addMinutesToCurrentDateTime( int $minutes, string $return_format = 'Y-m-d H:i:s' ): string {
		$local_time = Carbon::now( FunctionsHelper::getTimezone() );
		return $local_time->addMinutes( $minutes )->format( $return_format );
	}

	/**
	 * Get the UTC version of a time.
	 *
	 * @param string $time
	 * @param string $return_format
	 * @return string
	 * @throws InvalidFormatException
	 * @throws InvalidTimeZoneException
	 * @since 1.2.0
	 */
	public static function getUTCFromTime( string $time, string $return_format = 'H:i:s' ): string {
		$utc_time = Carbon::createFromTimeString( $time, FunctionsHelper::getTimezone() )->setTimezone( 'UTC' );
		return $utc_time->format( $return_format );
	}

	/**
	 * Check if a time is between two times.
	 *
	 * @param string $time_to_check
	 * @param string $from In format H:i
	 * @param string $to   In format H:i
	 *
	 * @return bool
	 * @since 1.0.8
	 * @link  https://stackoverflow.com/a/27134087/4484799
	 */
	public static function timeIsBetween( string $time_to_check, string $from, string $to ): bool {
		$input = DateTime::createFromFormat( '!H:i', $time_to_check );
		$from  = DateTime::createFromFormat( '!H:i', $from );
		$to    = DateTime::createFromFormat( '!H:i', $to );

		if ( $from > $to ) {
			$to->modify( '+1 day' );
		}

		return ( $from <= $input && $input <= $to ) || ( $from <= $input->modify( '+1 day' ) && $input <= $to );
	}

	/**
	 * Get the starting time from a time slot.
	 *
	 * @param string $time_slot
	 * @return string
	 * @since 1.1.0
	 */
	public static function getStartTimeFromTimeSlot( string $time_slot ): string {
		$parts = explode( '-', $time_slot );

		if ( empty( $parts[1] ) ) {
			return $time_slot; // Not a time slot.
		}

		return trim( $parts[0] ); // Be sure to remove the trailing space as it can cause errors when working with DateTime
	}

	/**
	 * Get a timestamp after subtracting a defined number of minutes from it.
	 *
	 * @param int $timestamp
	 * @param int $minutes
	 * @return int
	 * @since 1.1.0
	 */
	public static function subtractMinutesFromTimestamp( int $timestamp, int $minutes ): int {
		return $timestamp - ( $minutes * 60 );
	}

	/**
	 * Replace magic tags in content with the relevant data.
	 *
	 * @param array  $tags_values
	 * @param string $content
	 * @return string
	 * @since 1.1.0
	 */
	public static function replaceMagicTags( array $tags_values, string $content ): string {
		return str_replace( array_keys( $tags_values ), array_values( $tags_values ), $content );
	}

	/**
	 * Normalize string $_POST data.
	 *
	 * When checking $_POST on certain WC hooks we get a string with the checkout form data.
	 * This method helps normalize it to easier work with.
	 *
	 * WooCommerce most likely sanitizes the html value attribute causing a time like 10:00 - 12:00 to be parsed as 1000-1200 times to co
	 *
	 * @param string $post_data
	 * @return array
	 * @since 1.2.2
	 */
	public static function normalizePostString( string $post_data ): array {
		$data   = array();
		$fields = explode( '&', $post_data );

		foreach ( $fields as $key => $value ) {
			$parts = explode( '=', sanitize_text_field( $value ) );
			if ( empty( $parts[1] ) ) {
				// This is important as in a rare case it was noticed that the array was later overridden.
				continue;
			}
			$data[ $parts[0] ] = $parts[1];
		}

		return $data;
	}

	/**
	 * Normalize the timeslot received from the $_POST data.
	 *
	 * @param string $time
	 * @return array
	 * @since 1.2.2
	 */
	public static function normalizePostTimeslot( string $time ): array {

		// "+" signs are added automatically by WooCommerce/the Browser into the $posted_data selected time data in place of spaces.
		// Below we're removing the "+" signs so this can properly be normalized.
		$time = str_replace( '+', '', $time );

		$time_parts = explode( '-', $time );

		$from = $time_parts[0] ?? '';
		$to   = $time_parts[1] ?? '';

		$to_normalized = '';

		if ( FunctionsHelper::using24hrTime() ) {

			$minutes         = substr( $from, 2 );
			$hours           = substr( $from, 0, 2 );
			$from_normalized = $hours . ':' . $minutes;

			if ( ! empty( $to ) ) {
				$minutes       = substr( $to, 2 );
				$hours         = substr( $to, 0, 2 );
				$to_normalized = $hours . ':' . $minutes;
			}
		} else {

			// Start from end of string to help easier normalize it.
			// There would always be 2 characters at the end (AM/PM).
			$from_am_pm      = substr( $from, -2 );
			$from_minutes    = substr( $from, -4, 2 );
			$from_hours      = str_replace( $from_minutes . $from_am_pm, '', $from );
			$from_normalized = $from_hours . ':' . $from_minutes . ' ' . $from_am_pm;

			if ( ! empty( $to ) ) {
				$to_am_pm      = substr( $to, -2 );
				$to_minutes    = substr( $to, -4, 2 );
				$to_hours      = str_replace( $to_minutes . $to_am_pm, '', $to );
				$to_normalized = $to_hours . ':' . $to_minutes . ' ' . $to_am_pm;
			}
		}

		$time_slot = array(
			'from' => $from_normalized,
			'to'   => $to_normalized,
		);

		if ( ! empty( $to ) ) {
			$time_slot['formatted'] = $from_normalized . ' - ' . $to_normalized;
		}

		return $time_slot;
	}

	/**
	 * Replace dashes in a date with underscores. Example: 2022_11_27.
	 *
	 * @param string $date
	 * @return string
	 * @since 1.2.2
	 */
	public static function underscoreDate( string $date ): string {
		return str_replace( '-', '_', $date );
	}

	/**
	 * Replace spaces in a time slot with underscores. Example: 12:00_AM_-_12:00_PM.
	 *
	 * @param string $time_slot The timeslot.
	 * @return string
	 * @since 1.0.0
	 */
	public static function underscoreTimeSlot( string $time_slot ): string {
		return str_replace( ' ', '_', $time_slot );
	}

	/**
	 * Create a range text given the from and to. Example: 12:00 AM - 12:00 PM.
	 *
	 * @param string $from The from time.
	 * @param string $to The to time.
	 * @return string
	 * @since 1.0.0
	 */
	public static function createTimeSlotDisplayText( string $from, string $to ): string {
		return $from . ' - ' . $to;
	}

	/**
	 * Check if a date is between other dates.
	 *
	 * @param string $datetime_to_check
	 * @param string $between_from_datetime
	 * @param string $between_to_datetime
	 * @param bool   $equal Whether to include the datetime being checked in the comparison.
	 *
	 * @return bool
	 * @since 1.5.0
	 */
	public static function isDateBetweenDates( string $datetime_to_check, string $between_from_datetime, string $between_to_datetime, bool $equal = true ): bool {
		return Carbon::parse( $datetime_to_check )->between( $between_from_datetime, $between_to_datetime, $equal );
	}

	/**
	 * Get full days between two given dates.
	 *
	 * @param string $start_datetime
	 * @param string $end_datetime
	 *
	 * @return array
	 * @since 1.6.0
	 */
	public static function getFullDaysBetween( string $start_datetime, string $end_datetime ): array {
		$start = Carbon::parse( $start_datetime )->addDay()->startOfDay();
		$end   = Carbon::parse( $end_datetime )->startOfDay();

		$days = array();

		while ( $start < $end ) {
			$days[] = $start->toDateString(); // Y-m-d format
			$start->addDay();
		}

		return $days;
	}
}
