<?php

declare(strict_types=1);

namespace NoahVet\Reef\Domain\Tool;

class DateTool implements DateToolInterface
{
    public const PREVIOUS_MONTH = '-1 month';

    public static function fromNullableStringToDateTime(
        ?string $dateString,
        string $format = \DateTimeInterface::ATOM,
        string $timeZone = self::UTC,
        bool $isImmutable = true,
    ): \DateTimeInterface|\DateTimeImmutable|null {
        return \is_string($dateString)
            ? self::fromStringToDateTime($dateString, $format, $timeZone, $isImmutable)
            : null;
    }

    public static function fromStringToDateTime(
        string $dateString,
        string $format = \DateTimeInterface::ATOM,
        string $timeZone = self::UTC,
        bool $isImmutable = true,
    ): \DateTimeInterface|\DateTimeImmutable {
        if ($isImmutable) {
            $dateTime = \DateTimeImmutable::createFromFormat($format, $dateString);
        } else {
            $dateTime = \DateTime::createFromFormat($format, $dateString);
        }

        if ($dateTime && $dateTime->format($format) === $dateString && !empty($timeZone)) {
            return $dateTime->setTimezone(new \DateTimeZone($timeZone));
        }

        throw new \LogicException('The $dateString parameter is not in '.$format.' format.');
    }

    public static function toFirstSecondOfTheDay(\DateTimeInterface|\DateTime $datetime): \DateTimeInterface
    {
        /** @var \DateTime $dt */
        $dt = $datetime;

        return $dt->setTime(0, 0);
    }

    public static function toFirstSecondOfTheMonth(\DateTimeInterface|\DateTime $datetime): \DateTimeInterface
    {
        return self::toFirstSecondOfTheDay(
            self::getSecondOfTheDay(
                $datetime,
                'first',
            ),
        );
    }

    public static function toLastSecondOfTheDay(\DateTimeInterface|\DateTime $datetime): \DateTimeInterface
    {
        /** @var \DateTime $dt */
        $dt = $datetime;

        return $dt->setTime(23, 59, 59);
    }

    public static function toLastSecondOfTheMonth(\DateTimeInterface|\DateTime $datetime): \DateTimeInterface
    {
        return self::toLastSecondOfTheDay(
            self::getSecondOfTheDay(
                $datetime,
                'last',
            ),
        );
    }

    public static function toLastSecondOfThePreviousMonth(\DateTimeInterface|\DateTime $datetime): \DateTimeInterface
    {
        /** @var \DateTime $dt */
        $dt = $datetime;

        return self::toLastSecondOfTheMonth(
            $dt->modify('last day of previous month'),
        );
    }

    public static function toLastSecondOfTheYear(\DateTimeInterface|\DateTime $datetime): \DateTimeInterface
    {
        /** @var \DateTime $dt */
        $dt = $datetime;

        return self::toLastSecondOfTheDay(
            $dt
                ->modify(
                    \sprintf(
                        'last day of December %s',
                        $dt->format('Y'),
                    ),
                ),
        );
    }

    public static function isFirstDayOfTheMonth(\DateTimeInterface|\DateTime $datetime): bool
    {
        return '01' === $datetime->format('d');
    }

    public static function isDate(string $date, string $expectedFormat): bool
    {
        $d = \DateTime::createFromFormat($expectedFormat, $date);

        return $d
            && $d->format($expectedFormat) === $date && \checkdate(
                (int) $d->format('m'), // Mois
                (int) $d->format('d'), // Jour
                (int) $d->format('Y'),  // Année
            );
    }

    private static function getSecondOfTheDay(
        \DateTimeInterface|\DateTime $datetime,
        string $firstOrLast,
    ): \DateTimeInterface {
        /** @var \DateTime $dt */
        $dt = $datetime;

        return $dt
            ->modify(
                \sprintf(
                    '%s day of %s',
                    $firstOrLast,
                    $dt->format('F'),
                ),
            )
        ;
    }
}
