<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Doctrine\Type;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\DateIntervalType;

/**
 * Re-declare the date interval to use the implementation of date interval in
 * postgreql.
 */
class NativeDateIntervalType extends DateIntervalType
{
    final public const FORMAT = '%rP%YY%MM%DDT%HH%IM%SS';

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        if (null === $value) {
            return null;
        }

        if ($value instanceof \DateInterval) {
            return $value->format(self::FORMAT);
        }

        throw ConversionException::conversionFailedInvalidType($value, 'string', ['null', 'DateInterval']);
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        if (null === $value || $value instanceof \DateInterval) {
            return $value;
        }

        try {
            $strings = explode(' ', (string) $value);

            if (1 === count($strings) && ('' === $strings[0])) {
                return null;
            }
            $intervalSpec = 'P';
            \reset($strings);

            do {
                $intervalSpec .= $this->convertEntry($strings);
            } while (false !== next($strings));

            return new \DateInterval($intervalSpec);
        } catch (\Exception $exception) {
            throw $this->createConversionException($value, $exception);
        }
    }

    public function getName(): string
    {
        return \Doctrine\DBAL\Types\Types::DATEINTERVAL;
    }

    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
    {
        return 'INTERVAL';
    }

    protected function createConversionException($value, $exception = null)
    {
        return ConversionException::conversionFailedFormat($value, 'string', 'xx year xx mons xx days 01:02:03', $exception);
    }

    private function convertEntry(&$strings)
    {
        $current = \current($strings);

        if (is_numeric($current)) {
            $next = next($strings);

            $unit = match ($next) {
                'year', 'years' => 'Y',
                'mon', 'mons' => 'M',
                'day', 'days' => 'D',
                default => throw $this->createConversionException(implode('', $strings)),
            };

            return $current.$unit;
        }

        if (1 === \preg_match('/([0-9]{2}\:[0-9]{2}:[0-9]{2})/', (string) $current)) {
            $tExploded = explode(':', (string) $current);
            $intervalSpec = 'T';
            $intervalSpec .= $tExploded[0].'H';
            $intervalSpec .= $tExploded[1].'M';
            $intervalSpec .= $tExploded[2].'S';

            return $intervalSpec;
        }

        throw new \LogicException();
    }
}
