<?php
/**
 * Withdrawal window for the consumer and refund deadline for the trader.
 *
 * @package SkyServers\Retragere
 */

declare(strict_types=1);

namespace SkyServers\Retragere\Core;

use DateTimeImmutable;

/**
 * The withdrawal window is configurable upwards only. The refund deadline is
 * fixed by law and is not affected by a longer commercial window.
 */
final class DeadlineCalculator
{
    public const MIN_WINDOW_DAYS = 14;
    public const REFUND_DAYS     = 14;

    /** @var int */
    private $windowDays;

    public function __construct(int $windowDays = self::MIN_WINDOW_DAYS)
    {
        $this->windowDays = max(self::MIN_WINDOW_DAYS, $windowDays);
    }

    public function windowDays(): int
    {
        return $this->windowDays;
    }

    /**
     * Calendar days, so the window always ends at the close of its last day.
     * The delivery time of day must never shorten it.
     */
    public function windowEndsAt(DateTimeImmutable $referenceDate): DateTimeImmutable
    {
        return $referenceDate
            ->setTime(0, 0, 0)
            ->modify('+' . $this->windowDays . ' days')
            ->setTime(23, 59, 59);
    }

    public function isWithinWindow(DateTimeImmutable $referenceDate, DateTimeImmutable $now): bool
    {
        return $now <= $this->windowEndsAt($referenceDate);
    }

    public function refundDueAt(DateTimeImmutable $submittedAt): DateTimeImmutable
    {
        return $submittedAt->modify('+' . self::REFUND_DAYS . ' days');
    }

    /**
     * The clock stops when the money is actually returned, not when the
     * request is approved.
     */
    public function isOverdue(
        DateTimeImmutable $refundDueAt,
        DateTimeImmutable $now,
        ?DateTimeImmutable $settledAt = null
    ): bool {
        if (null !== $settledAt) {
            return $settledAt > $refundDueAt;
        }

        return $now > $refundDueAt;
    }
}
