<?php

declare(strict_types=1);

/*
 * This file is part of Contao.
 *
 * (c) Leo Feyer
 *
 * @license LGPL-3.0-or-later
 */

namespace Contao\CoreBundle\DataContainer;

use Contao\CoreBundle\DataContainer\ForeignKeyParser\ForeignKeyExpression;
use Doctrine\DBAL\Connection;

class ForeignKeyParser
{
    public function __construct(private readonly Connection $connection)
    {
    }

    /**
     * @throws \InvalidArgumentException if the expression is not valid
     */
    public function parse(string $foreignKeyDefinition): ForeignKeyExpression
    {
        $isIdentifier = static fn (string $expression): bool => 1 === preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/', $expression);

        $key = null;
        [$tableExpression, $columnExpression] = explode('.', $foreignKeyDefinition, 2);

        if (str_contains($tableExpression, ':')) {
            [$key, $tableExpression] = explode(':', $tableExpression, 2);
        }

        // Table name must be an identifier
        if (!$isIdentifier($tableExpression)) {
            throw new \InvalidArgumentException('Invalid foreign key expression: '.$foreignKeyDefinition);
        }

        // If the expression is a single identifier, quote it to support reserved column
        // names such as "group"
        if ($isIdentifier($columnExpression)) {
            $columnName = $columnExpression;

            /** @phpstan-ignore function.alreadyNarrowedType */
            if (!method_exists(Connection::class, 'quoteSingleIdentifier')) {
                // Backwards compatibility for doctrine/dbal < 4.3
                $columnExpression = $this->connection->quoteIdentifier($columnExpression);
            } else {
                $columnExpression = $this->connection->quoteSingleIdentifier($columnExpression);
            }

            $expression = new ForeignKeyExpression($tableExpression, $columnExpression);
            $expression = $expression->withColumnName($columnName);
        } else {
            $expression = new ForeignKeyExpression($tableExpression, $columnExpression);
        }

        return $expression->withKey($key ?? null);
    }
}
