<?php

declare(strict_types=1);

namespace Install\Services;

final class SqlParser
{
    /**
     * @return array<int,string>
     */
    public function parseFile(string $filePath): array
    {
        if (!is_file($filePath) || !is_readable($filePath)) {
            throw new \RuntimeException(
                'Fichier SQL introuvable ou illisible : '
                . basename($filePath)
            );
        }

        $sql = (string)file_get_contents($filePath);

        if (trim($sql) === '') {
            return [];
        }

        if (preg_match('/^\s*DELIMITER\s+/mi', $sql) === 1) {
            throw new \RuntimeException(
                'Les directives DELIMITER ne sont pas prises en charge : '
                . basename($filePath)
            );
        }

        return $this->splitStatements($sql);
    }

    /**
     * Découpe le SQL sur les points-virgules qui ne se trouvent pas
     * dans une chaîne, un identifiant entre accents graves ou un commentaire.
     *
     * @return array<int,string>
     */
    private function splitStatements(string $sql): array
    {
        $statements = [];
        $buffer = '';
        $length = strlen($sql);

        $quote = null;
        $inLineComment = false;
        $inBlockComment = false;
        $escaped = false;

        for ($index = 0; $index < $length; $index++) {
            $char = $sql[$index];
            $next = $index + 1 < $length
                ? $sql[$index + 1]
                : '';

            if ($inLineComment) {
                if ($char === "\n") {
                    $inLineComment = false;
                    $buffer .= $char;
                }

                continue;
            }

            if ($inBlockComment) {
                if ($char === '*' && $next === '/') {
                    $inBlockComment = false;
                    $index++;
                }

                continue;
            }

            if ($quote !== null) {
                $buffer .= $char;

                if ($escaped) {
                    $escaped = false;
                    continue;
                }

                if ($char === '\\') {
                    $escaped = true;
                    continue;
                }

                if ($char === $quote) {
                    if (
                        $quote !== '`'
                        && $next === $quote
                    ) {
                        $buffer .= $next;
                        $index++;
                        continue;
                    }

                    $quote = null;
                }

                continue;
            }

            if ($char === '-' && $next === '-') {
                $third = $index + 2 < $length
                    ? $sql[$index + 2]
                    : '';

                if ($third === '' || ctype_space($third)) {
                    $inLineComment = true;
                    $index++;
                    continue;
                }
            }

            if ($char === '#') {
                $inLineComment = true;
                continue;
            }

            if ($char === '/' && $next === '*') {
                $inBlockComment = true;
                $index++;
                continue;
            }

            if (
                $char === "'"
                || $char === '"'
                || $char === '`'
            ) {
                $quote = $char;
                $buffer .= $char;
                continue;
            }

            if ($char === ';') {
                $statement = trim($buffer);

                if ($statement !== '') {
                    $statements[] = $statement;
                }

                $buffer = '';
                continue;
            }

            $buffer .= $char;
        }

        $statement = trim($buffer);

        if ($statement !== '') {
            $statements[] = $statement;
        }

        return $statements;
    }
}
