<?php // phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase, WordPress.Files.FileName.InvalidClassFileName
/**
 * Pure-logic helpers for the Snooze composite status.
 *
 * Snooze is not a distinct backend status — it is a composite of three
 * inputs surfaced together in the subscriber UI:
 *   1. Status is "Paused"
 *   2. snoozeUntilUtc is set (a valid resume date)
 *   3. snoozeDuration > 0
 *
 * The QPilot API translates the internal "Snoozed" status to "Paused"
 * for external consumers, so we detect the state by the presence of
 * the snooze metadata fields.
 *
 * This class has no WordPress dependencies and is safe to call from
 * any context.
 *
 * @package Autoship\Domain\Snooze
 * @since   2.13.0
 */

namespace Autoship\Domain\Snooze;

use DateTime;
use DateTimeZone;

/**
 * Pure-logic helpers for the Snooze composite status.
 */
class SnoozeStatus {

	/**
	 * Determine whether a scheduled order is in the composite snoozed state.
	 *
	 * Accepts either an array or an object representation of the order to
	 * stay compatible with both the legacy procedural code (objects from
	 * the QPilot API) and the modern templates (arrays after conversion).
	 *
	 * @param object|array $order The scheduled order.
	 *
	 * @return bool True when the order is snoozed.
	 */
	public static function is_snoozed( $order ): bool {
		$is_array = is_array( $order );

		$status           = $is_array ? ( $order['status'] ?? '' ) : ( $order->status ?? '' );
		$snooze_until_utc = $is_array ? ( $order['snoozeUntilUtc'] ?? '' ) : ( $order->snoozeUntilUtc ?? '' ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
		$snooze_duration  = $is_array ? ( $order['snoozeDuration'] ?? 0 ) : ( $order->snoozeDuration ?? 0 ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

		return 'Paused' === $status && ! empty( $snooze_until_utc ) && (int) $snooze_duration > 0;
	}

	/**
	 * Format a duration as a human-readable label.
	 *
	 * Examples: (1, 'Weeks') → "1 Week"; (3, 'Months') → "3 Months".
	 *
	 * @param int    $duration      The duration value.
	 * @param string $duration_type The duration type: Days, Weeks, or Months.
	 *
	 * @return string The formatted label.
	 */
	public static function format_duration_label( int $duration, string $duration_type ): string {
		$singular = rtrim( $duration_type, 's' );

		if ( 1 === $duration ) {
			return sprintf( '%d %s', $duration, $singular );
		}

		$plural = substr( $duration_type, -1 ) === 's' ? $duration_type : $duration_type . 's';

		return sprintf( '%d %s', $duration, $plural );
	}

	/**
	 * Determine whether a preset duration exceeds the merchant-configured cap.
	 *
	 * Normalises the chosen Days/Weeks/Months duration into days using a
	 * conservative upper bound for Months (31 days) so that the cap is
	 * never under-enforced, then compares against `$max_days`. A cap of
	 * zero (or negative) is treated as "no cap".
	 *
	 * @param int    $duration      The duration value.
	 * @param string $duration_type The duration type: Days, Weeks, or Months.
	 * @param int    $max_days      Maximum allowed snooze in days (0 = no cap).
	 *
	 * @return bool True when the preset duration exceeds the cap.
	 */
	public static function exceeds_max_duration( int $duration, string $duration_type, int $max_days ): bool {
		if ( $max_days <= 0 ) {
			return false;
		}

		$preset_days = $duration;
		if ( 'Weeks' === $duration_type ) {
			$preset_days = $duration * 7;
		} elseif ( 'Months' === $duration_type ) {
			$preset_days = $duration * 31; // Conservative upper bound.
		}

		return $preset_days > $max_days;
	}

	/**
	 * Calculate the UTC ISO 8601 resume date from a duration and type.
	 *
	 * Mirrors the V2 Subscriber Portal calculation logic: Days/Weeks/Months
	 * advance from "now" in the UTC timezone. Unknown duration types fall
	 * through and return the current time unchanged.
	 *
	 * @param int    $duration      The duration value.
	 * @param string $duration_type The duration type: Days, Weeks, or Months.
	 *
	 * @return string Resume date in `Y-m-d\TH:i:s\Z` format.
	 */
	public static function calculate_resume_date( int $duration, string $duration_type ): string {
		$now = new DateTime( 'now', new DateTimeZone( 'UTC' ) );

		switch ( $duration_type ) {
			case 'Days':
				$now->modify( "+{$duration} days" );
				break;
			case 'Weeks':
				$days = $duration * 7;
				$now->modify( "+{$days} days" );
				break;
			case 'Months':
				$now->modify( "+{$duration} months" );
				break;
		}

		return $now->format( 'Y-m-d\TH:i:s\Z' );
	}
}
