<?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\DQL;

use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Parser;

/**
 * DQL function for OVERLAPS function in postgresql.
 *
 * If a value is null in period start, it will be replaced by -infinity.
 * If a value is null in period end, it will be replaced by infinity
 */
class OverlapsI extends FunctionNode
{
    private ?\Doctrine\ORM\Query\AST\Node $firstPeriodEnd = null;

    private ?\Doctrine\ORM\Query\AST\Node $firstPeriodStart = null;

    private ?\Doctrine\ORM\Query\AST\Node $secondPeriodEnd = null;

    private ?\Doctrine\ORM\Query\AST\Node $secondPeriodStart = null;

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
    {
        return sprintf(
            '(%s, %s) OVERLAPS (%s, %s)',
            $this->makeCase($sqlWalker, $this->firstPeriodStart, 'start'),
            $this->makeCase($sqlWalker, $this->firstPeriodEnd, 'end'),
            $this->makeCase($sqlWalker, $this->secondPeriodStart, 'start'),
            $this->makeCase($sqlWalker, $this->secondPeriodEnd, 'end')
        );
    }

    public function parse(Parser $parser): void
    {
        $parser->match(\Doctrine\ORM\Query\TokenType::T_IDENTIFIER);

        $parser->match(\Doctrine\ORM\Query\TokenType::T_OPEN_PARENTHESIS);

        $this->firstPeriodStart = $parser->StringPrimary();

        $parser->match(\Doctrine\ORM\Query\TokenType::T_COMMA);

        $this->firstPeriodEnd = $parser->StringPrimary();

        $parser->match(\Doctrine\ORM\Query\TokenType::T_CLOSE_PARENTHESIS);

        $parser->match(\Doctrine\ORM\Query\TokenType::T_COMMA);

        $parser->match(\Doctrine\ORM\Query\TokenType::T_OPEN_PARENTHESIS);

        $this->secondPeriodStart = $parser->StringPrimary();

        $parser->match(\Doctrine\ORM\Query\TokenType::T_COMMA);

        $this->secondPeriodEnd = $parser->StringPrimary();

        $parser->match(\Doctrine\ORM\Query\TokenType::T_CLOSE_PARENTHESIS);
    }

    protected function makeCase($sqlWalker, $part, string $position): string
    {
        $p = match ($position) {
            'start' => '-infinity',
            'end' => 'infinity',
            default => throw new \Exception('Unexpected position value.'),
        };

        if ($part instanceof PathExpression) {
            return sprintf(
                "COALESCE(%s, '%s'::date)",
                $part->dispatch($sqlWalker),
                $p
            );
        }

        return sprintf(
            "COALESCE(%s::date, '%s'::date)",
            $part->dispatch($sqlWalker),
            $p
        );
    }
}
