<?php declare(strict_types=1);

namespace Newland\Toubiz\Events\Neos\ViewHelpers\Format;

use DateTimeInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\I18n\Formatter\DatetimeFormatter;
use Neos\Flow\I18n\Locale;
use Neos\FluidAdaptor\Core\ViewHelper\AbstractViewHelper;
use Newland\Toubiz\Sync\Neos\Domain\Model\Event;
use Newland\Toubiz\Sync\Neos\Domain\Model\EventDate;

class TimeRangeViewHelper extends AbstractViewHelper
{

    /**
     * @Flow\Inject
     * @var DatetimeFormatter
     */
    protected $datetimeFormatter;

    public function initializeArguments(): void
    {
        parent::initializeArguments();
        $this->registerArgument(
            'event',
            Event::class,
            'Event of which the times should be displayed',
            true
        );
        $this->registerArgument(
            'firstDate',
            EventDate::class,
            'event date that marks the beginning of the range. Defaults to first date of event',
            false,
            null
        );
        $this->registerArgument(
            'lastDate',
            EventDate::class,
            'event date that marks the end of the range. Defaults to last date of event',
            false,
            null
        );
        $this->registerArgument(
            'dateFormat',
            'string',
            'Format of the date in CLDR format',
            true
        );
        $this->registerArgument(
            'timeFormat',
            'string',
            'Format of the time in CLDR format',
            true
        );
        $this->registerArgument(
            'dateTimeFormat',
            'string',
            'Format of the date time in CLDR format',
            true
        );
    }

    public function render(): string
    {
        /** @var Event $event */
        $event = $this->arguments['event'];

        /** @var EventDate|false $firstDate */
        $firstDate = $this->arguments['firstDate'] ?? $event->getEventDates()->first();

        /** @var EventDate|false $firstDate */
        $lastDate = $this->arguments['lastDate'] ?? $event->getEventDates()->last();

        if (!$firstDate || !$lastDate) {
            return '';
        }

        if ($firstDate->getBeginsAt() === null || $lastDate->getEndsAt() === null) {
            return '';
        }

        if ($this->isOnSameDate($firstDate->getBeginsAt(), $lastDate->getEndsAt())) {
            return $this->formatStartAndEndOnSameDay($firstDate, $lastDate);
        }

        return $this->formatStartAndEndOnDifferentDays($firstDate, $lastDate);
    }

    private function formatStartAndEndOnSameDay(EventDate $firstDate, EventDate $lastDate): string
    {
        if (!$firstDate->getBeginsAtSpecificTime()) {
            // If there is no definitive start time then we don't display any times
            return $this->formatDate($firstDate->getBeginsAt(), $firstDate->getLanguage());
        }

        if ($lastDate->getEndsAtSpecificTime()) {
            // If there is a definitive start & end time then both is displayed (end without date)
            return sprintf(
                '%s — %s',
                $this->formatDateTime($firstDate->getBeginsAt(), $firstDate->getLanguage()),
                $this->formatTime($lastDate->getEndsAt(), $lastDate->getLanguage())
            );
        }

        // If there is a definitive start time but no definitive end time then only start time is displayed
        return $this->formatDateTime($firstDate->getBeginsAt(), $firstDate->getLanguage());
    }

    private function formatStartAndEndOnDifferentDays(EventDate $firstDate, EventDate $lastDate): string
    {
        return sprintf(
            '%s — %s',
            $this->formatDate($firstDate->getBeginsAt(), $firstDate->getLanguage()),
            $this->formatDate($lastDate->getEndsAt(), $lastDate->getLanguage())
        );
    }

    private function isOnSameDate(DateTimeInterface $a, DateTimeInterface $b): bool
    {
        return $a->format('Y-m-d') === $b->format('Y-m-d');
    }

    private function formatDate(?DateTimeInterface $date, ?string $locale): string
    {
        if ($date === null) {
            return '';
        }

        return $this->datetimeFormatter->formatDateTimeWithCustomPattern(
            $date,
            $this->arguments['dateFormat'],
            new Locale($locale ?? 'de')
        );
    }

    private function formatTime(?DateTimeInterface $date, ?string $locale): string
    {
        if ($date === null) {
            return '';
        }

        return $this->datetimeFormatter->formatDateTimeWithCustomPattern(
            $date,
            $this->arguments['timeFormat'],
            new Locale($locale ?? 'de')
        );
    }

    private function formatDateTime(?DateTimeInterface $date, ?string $locale): string
    {
        if ($date === null) {
            return '';
        }

        return $this->datetimeFormatter->formatDateTimeWithCustomPattern(
            $date,
            $this->arguments['dateTimeFormat'],
            new Locale($locale ?? 'de')
        );
    }
}
