<?php

declare(strict_types=1);

namespace App\Framework\DateTime;

use DateInvalidTimeZoneException;
use DateMalformedStringException;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;

final readonly class DateTimeFormatter
{
    /**
     * Standard-Zeitzone für Formatierungen
     */
    private DateTimeZone $timezone;

    /**
     * @param DateTimeZone|string|null $timezone Die zu verwendende Zeitzone (Standard: UTC)
     * @throws DateInvalidTimeZoneException
     */
    public function __construct(DateTimeZone|string|null $timezone = null)
    {
        if (is_string($timezone)) {
            $timezone = new DateTimeZone($timezone);
        }

        $this->timezone = $timezone ?? new DateTimeZone('Europe/Berlin');
    }

    /**
     * Formatiert ein DateTime im ISO8601-Format.
     *
     * @param DateTimeInterface $dateTime Das zu formatierende DateTime
     * @return string
     * @throws DateMalformedStringException
     */
    public function formatIso8601(DateTimeInterface $dateTime): string
    {
        return $this->format($dateTime, DateTimeInterface::ATOM);
    }

    /**
     * Formatiert ein DateTime im SQL-Datetime-Format.
     *
     * @param DateTimeInterface $dateTime Das zu formatierende DateTime
     * @return string
     * @throws DateMalformedStringException
     */
    public function formatSql(DateTimeInterface $dateTime): string
    {
        return $this->format($dateTime, 'Y-m-d H:i:s');
    }

    /**
     * Formatiert ein DateTime nur als Datum.
     *
     * @param DateTimeInterface $dateTime Das zu formatierende DateTime
     * @return string
     * @throws DateMalformedStringException
     */
    public function formatDate(DateTimeInterface $dateTime): string
    {
        #return $this->format($dateTime, 'Y-m-d');

        return $this->format($dateTime, 'd.m.Y');
    }

    /**
     * Formatiert ein DateTime nur als Zeit.
     *
     * @param DateTimeInterface $dateTime Das zu formatierende DateTime
     * @return string
     * @throws DateMalformedStringException
     */
    public function formatTime(DateTimeInterface $dateTime): string
    {
        return $this->format($dateTime, 'H:i:s');
    }

    /**
     * Formatiert ein DateTime in einem benutzerdefinierten Format.
     *
     * @param DateTimeInterface $dateTime Das zu formatierende DateTime
     * @param string $format Das Format-Pattern
     * @return string
     * @throws DateMalformedStringException
     */
    public function format(DateTimeInterface $dateTime, string $format): string
    {
        // Stelle sicher, dass die Zeitzone korrekt ist
        $dateInCorrectTimezone = $this->ensureTimezone($dateTime);

        return $dateInCorrectTimezone->format($format);
    }

    /**
     * Stellt sicher, dass ein DateTime die korrekte Zeitzone hat.
     *
     * @param DateTimeInterface $dateTime Das zu überprüfende DateTime
     * @return DateTimeImmutable
     * @throws DateMalformedStringException
     */
    private function ensureTimezone(DateTimeInterface $dateTime): DateTimeImmutable
    {
        if ($dateTime->getTimezone()->getName() !== $this->timezone->getName()) {
            if ($dateTime instanceof DateTimeImmutable) {
                return $dateTime->setTimezone($this->timezone);
            }

            // Konvertiere DateTime zu DateTimeImmutable mit der richtigen Zeitzone
            return new DateTimeImmutable('@' . $dateTime->getTimestamp())
                ->setTimezone($this->timezone);
        }

        if ($dateTime instanceof DateTimeImmutable) {
            return $dateTime;
        }

        // Konvertiere DateTime zu DateTimeImmutable
        return DateTimeImmutable::createFromInterface($dateTime);
    }

    /**
     * Gibt die verwendete Zeitzone zurück.
     *
     * @return DateTimeZone
     */
    public function getTimezone(): DateTimeZone
    {
        return $this->timezone;
    }
}
