<?php
namespace Dreamfox\DeliveryDate\Helpers;

use DateTime;
use Dreamfox\DeliveryDate\Facades\SystemFacade;
use Dreamfox\DeliveryDate\Premium\Facades\SystemFacade as PremiumSystemFacade;

class DeliveryDateHelper
{
	/**
	 * Weekday number matching JS datepicker logic: 1 = Sunday … 7 = Saturday.
	 */
	public static function getWeekdayNumber( DateTime $date )
	{
		return (int) $date->format( 'w' ) + 1;
	}

	/**
	 * Expand holiday range objects into a flat Y-m-d list.
	 *
	 * @param  array $holidayRanges Objects with startDate and endDate properties.
	 * @return array
	 */
	public static function expandHolidayRanges( array $holidayRanges )
	{
		$result = array();
		foreach ( $holidayRanges as $range ) {
			$startDate = isset( $range->startDate ) ? $range->startDate : '';
			$endDate   = isset( $range->endDate ) ? $range->endDate : '';
			if ( strlen( $startDate ) < 8 || strlen( $endDate ) < 8 ) {
				continue;
			}
			$startYmd = DateHelper::toDateYmd( $startDate );
			$endYmd   = DateHelper::toDateYmd( $endDate );
			$days     = DateHelper::createDateRange( $startYmd, $endYmd, '+1 day' );
			$result   = array_merge( $result, $days );
		}
		return array_values( array_unique( $result ) );
	}

	/**
	 * Collect checked weekday ids (1 = Sunday … 7 = Saturday) from disable-day objects.
	 *
	 * @param  array $disableDayItems Objects with id and checked properties.
	 * @return array
	 */
	public static function getDisabledWeekdayIds( array $disableDayItems )
	{
		$result = array();
		foreach ( $disableDayItems as $day ) {
			if ( ! empty( $day->checked ) ) {
				$result[] = (int) $day->id;
			}
		}
		return $result;
	}

	/**
	 * Calculate the earliest allowed delivery timestamp (start of day, site timezone).
	 */
	public static function calculateMinTimestamp(
		$deliveryDays,
		$countOnlyWorkingDays,
		array $disabledWeekdays,
		array $holidayDatesYmd
	) {
		$deliveryDays = max( 0, (int) $deliveryDays );
		$timezone     = wp_timezone();
		$current      = new DateTime( 'today', $timezone );

		if ( ! $countOnlyWorkingDays ) {
			if ( $deliveryDays > 0 ) {
				$current->modify( '+' . $deliveryDays . ' days' );
			}
			return $current->getTimestamp();
		}

		$counted = 0;
		while ( $counted < $deliveryDays ) {
			$current->modify( '+1 day' );
			if ( ! self::isDateBlocked( $current, $disabledWeekdays, $holidayDatesYmd ) ) {
				$counted++;
			}
		}

		// When $deliveryDays is 0 the loop above never runs, so "today" could
		// still land on a disabled weekday or holiday. Advance until it doesn't.
		while ( self::isDateBlocked( $current, $disabledWeekdays, $holidayDatesYmd ) ) {
			$current->modify( '+1 day' );
		}

		return $current->getTimestamp();
	}

	/**
	 * Whether a date falls on a disabled weekday or holiday.
	 */
	public static function isDateBlocked( DateTime $date, array $disabledWeekdays, array $holidayDatesYmd )
	{
		if ( in_array( self::getWeekdayNumber( $date ), $disabledWeekdays, true ) ) {
			return true;
		}

		return in_array( $date->format( 'Y-m-d' ), $holidayDatesYmd, true );
	}

	/**
	 * Format a timestamp as a jQuery UI datepicker minDate string.
	 */
	public static function toJsMinDate( $timestamp )
	{
		$year  = (int) wp_date( 'Y', $timestamp );
		$month = (int) wp_date( 'n', $timestamp ) - 1;
		$day   = (int) wp_date( 'j', $timestamp );
		return DateHelper::toJsDateObject( $year, $month, $day );
	}

	/**
	 * Format minimum delivery date for the checkout field default value.
	 */
	public static function formatMinDeliveryDate( $timestamp )
	{
		return wp_date( get_option( 'date_format' ), $timestamp );
	}

	/**
	 * Resolve the effective lead time (in days) for the current cart, honoring
	 * per-category overrides configured in the premium Categories tab.
	 *
	 * A category's `days` value overrides the global delivery days when it is
	 * selected AND its cart matches AND `days` is set (not null/blank — blank
	 * means "use the global setting"). When multiple matching categories in
	 * the cart specify different overrides, the longest lead time is used so
	 * every product gets enough preparation time.
	 *
	 * @param  int   $globalDays Global "days to deliver" setting.
	 * @param  array $categories Category setting objects with id/selected/days.
	 * @return int
	 */
	public static function resolveCategoryDeliveryDays( $globalDays, array $categories )
	{
		if ( ! function_exists( 'WC' ) || ! WC()->cart ) {
			return (int) $globalDays;
		}

		$cartCategoryIds = array_map( 'intval', CartHelper::getCategories() );
		$overrides       = array();

		foreach ( $categories as $category ) {
			if ( empty( $category->selected ) ) {
				continue;
			}
			if ( ! in_array( (int) $category->id, $cartCategoryIds, true ) ) {
				continue;
			}
			if ( isset( $category->days ) && '' !== $category->days && null !== $category->days ) {
				$overrides[] = max( 0, (int) $category->days );
			}
		}

		if ( empty( $overrides ) ) {
			return (int) $globalDays;
		}

		return max( $overrides );
	}

	/**
	 * Whether the delivery date field should appear for the current cart.
	 */
	public static function isDeliveryAvailableForCart()
	{
		if ( ! function_exists( 'WC' ) || ! WC()->cart ) {
			return false;
		}

		$cartCategories = CartHelper::getCategories();

		if ( function_exists( 'dreamfox_delivery_date' ) && dreamfox_delivery_date()->is__premium_only() ) {
			$categorySetting     = PremiumSystemFacade::getInstance()->loadCategorySettings();
			$appliedCategories   = array();
			foreach ( $categorySetting->getCategories() as $item ) {
				if ( ! empty( $item->selected ) ) {
					$appliedCategories[] = $item->id;
				}
			}
		} else {
			$appliedCategories = SystemFacade::getInstance()->loadSettings()->getCategories();
		}

		return ! empty( array_intersect( $cartCategories, $appliedCategories ) );
	}

	/**
	 * Build validation context for the free plugin build.
	 *
	 * @return array|null Null when delivery is not applicable.
	 */
	public static function buildFreeValidationContext()
	{
		if ( ! self::isDeliveryAvailableForCart() ) {
			return null;
		}

		$deliveryDays = SystemFacade::getInstance()->loadSettings()->getDeliveryDays();
		$minTimestamp = self::calculateMinTimestamp( $deliveryDays, false, array(), array() );

		return array(
			'minTimestamp'       => $minTimestamp,
			'disabledWeekdays'   => array(),
			'holidayDatesYmd'    => array(),
			'countOnlyWorkingDays' => false,
		);
	}

	/**
	 * Build validation context for the premium plugin build.
	 *
	 * @return array|null Null when delivery is not applicable.
	 */
	public static function buildPremiumValidationContext()
	{
		if ( ! self::isDeliveryAvailableForCart() ) {
			return null;
		}

		$premium        = PremiumSystemFacade::getInstance();
		$deliveryDays   = self::resolveCategoryDeliveryDays(
			$premium->loadCommonSettings()->getDeliveryDays(),
			$premium->loadCategorySettings()->getCategories()
		);
		$holidaySetting = $premium->loadHolidaySettings();

		$disabledWeekdays = self::getDisabledWeekdayIds( (array) $holidaySetting->getDisableDays() );
		$holidayDatesYmd  = self::expandHolidayRanges( (array) $holidaySetting->getHolidays() );
		$countOnlyWorking = (bool) $holidaySetting->getCountOnlyWorkingDays();

		$minTimestamp = self::calculateMinTimestamp(
			$deliveryDays,
			$countOnlyWorking,
			$disabledWeekdays,
			$holidayDatesYmd
		);

		return array(
			'minTimestamp'         => $minTimestamp,
			'disabledWeekdays'     => $disabledWeekdays,
			'holidayDatesYmd'      => $holidayDatesYmd,
			'countOnlyWorkingDays' => $countOnlyWorking,
		);
	}

	/**
	 * Validate a submitted delivery date string.
	 *
	 * @param  string     $dateString Raw date from checkout POST.
	 * @param  array|null $context    From buildFreeValidationContext or buildPremiumValidationContext.
	 * @return string|null Error message, or null when valid.
	 */
	public static function validateSubmittedDate( $dateString, $context )
	{
		if ( null === $context || '' === $dateString ) {
			return null;
		}

		$dateYmd = TimeslotHelper::parseDateToYmd( $dateString );
		if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $dateYmd ) ) {
			return __( 'Please enter a valid delivery date.', 'woocommerce-delivery-date' );
		}

		$timezone = wp_timezone();
		$selected = DateTime::createFromFormat( 'Y-m-d', $dateYmd, $timezone );
		if ( ! $selected ) {
			return __( 'Please enter a valid delivery date.', 'woocommerce-delivery-date' );
		}
		$selected->setTime( 0, 0, 0 );

		$minDate = ( new DateTime( '@' . $context['minTimestamp'] ) )->setTimezone( $timezone );
		$minDate->setTime( 0, 0, 0 );

		if ( $selected < $minDate ) {
			return __( 'The selected delivery date is too soon. Please choose a later date.', 'woocommerce-delivery-date' );
		}

		if ( self::isDateBlocked( $selected, $context['disabledWeekdays'], $context['holidayDatesYmd'] ) ) {
			return __( 'The selected delivery date is not available. Please choose another date.', 'woocommerce-delivery-date' );
		}

		return null;
	}
}
