<?php

declare(strict_types=1);

namespace App\Framework\DateTime;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\DateTime\Exceptions\InvalidDateTimeException;
use App\Framework\DateTime\Exceptions\InvalidTimezoneException;
use DateTimeImmutable;
use DateTimeZone;

use function microtime;

final readonly class SystemClock implements Clock
{
    private DateTimeZone $timezone;

    /**
     * @param DateTimeZone|string|null $timezone Die zu verwendende Zeitzone (Standard: UTC)
     * @throws InvalidTimezoneException wenn die Zeitzone ungültig ist
     */
    public function __construct(DateTimeZone|string|null $timezone = null)
    {
        $this->timezone = $timezone !== null
            ? DateTime::createTimezone($timezone)
            : DateTime::getDefaultTimezone();
    }

    /**
     * {@inheritDoc}
     * @throws InvalidDateTimeException
     */
    public function now(): DateTimeImmutable
    {
        try {
            return new DateTimeImmutable('now', $this->timezone);
        } catch (\Exception $e) {
            throw new InvalidDateTimeException('now', null, $e);
        }
    }

    /**
     * {@inheritDoc}
     * @throws InvalidTimezoneException|InvalidDateTimeException
     */
    public function fromTimestamp(Timestamp $timestamp): DateTimeImmutable
    {
        return DateTime::fromTimestamp($timestamp->toTimestamp(), $this->timezone);
    }

    /**
     * {@inheritDoc}
     */
    public function fromString(string $dateTime, ?string $format = null): DateTimeImmutable
    {
        if ($format === null) {
            return DateTime::fromString($dateTime, $this->timezone);
        }

        return DateTime::fromFormat($dateTime, $format, $this->timezone);
    }

    /**
     * Erstellt ein Today-Datum (00:00:00).
     *
     * @return DateTimeImmutable
     */
    public function today(): DateTimeImmutable
    {
        return DateTime::fromString('today', $this->timezone);
    }

    /**
     * Erstellt ein Tomorrow-Datum (00:00:00).
     *
     * @return DateTimeImmutable
     */
    public function tomorrow(): DateTimeImmutable
    {
        return DateTime::fromString('tomorrow', $this->timezone);
    }

    /**
     * Erstellt ein Yesterday-Datum (00:00:00).
     *
     * @return DateTimeImmutable
     */
    public function yesterday(): DateTimeImmutable
    {
        return DateTime::fromString('yesterday', $this->timezone);
    }

    public function time(): Timestamp
    {
        return Timestamp::fromFloat(microtime(true));
    }
}
