<?php 
declare(strict_types=1);
namespace Lcmigrator\LcMigrator\Importer\QueryParser;

use Lcmigrator\LcMigrator\Logger\Logger;
use Exception;



class SqlParser {
    private $fileHandle;
    private $lineCache = [''];
    private $delimiter = ';';
    private $logger;

    public function __construct(Logger $logger = null) {
        $this->logger = $logger;
    }

    public function __destruct() {
        $this->closeFile();
    }

    public function openFile(string $filePath): bool {
        if (!is_file($filePath)) {
            throw new Exception(esc_html("SQL file does not exist: {$filePath}"));
        }

        if (!is_readable($filePath)) {
            throw new Exception(esc_html("SQL file is not readable: {$filePath}"));
        }

        $path = $this->getFilePath($filePath);
        $handle = fopen($path, 'r');

        if ($handle === false) {
            throw new Exception(esc_html("Could not open SQL file: {$filePath}"));
        }

        $this->fileHandle = $handle;
        return true;
    }

    public function getNextQuery(): ?string {
        $query = '';
        
        while (($line = $this->getNextLine()) !== false) {
            // Skip comments
            if (empty($line) || substr($line, 0, 2) === '--' || substr($line, 0, 1) === '#') {
                continue;
            }

            // Handle DELIMITER changes
            if (preg_match('/^DELIMITER\s+([^ ]+)/i', $line, $matches)) {
                $this->delimiter = trim($matches[1]);
                continue;
            }

            $query .= $line;
            
            if (preg_match('/' . preg_quote($this->delimiter, '/') . '\s*$/', $query)) {
                // Remove delimiter and return query
                return substr(rtrim($query), 0, -strlen($this->delimiter)) . ';';
            }
            
            $query .= "\n";
        }

        return empty($query) ? null : $query;
    }

    public function getPosition(): array {
        if (!$this->isFileOpen()) {
            throw new Exception('Cannot get SQL file position - file not open');
        }

        $pos = ftell($this->fileHandle);
        if ($pos === false) {
            throw new Exception('Cannot determine file position');
        }

        return [
            'pos' => $pos,
            'line_cache' => $this->lineCache
        ];
    }

    public function setPosition(array $position): bool {
        if (!isset($position['pos'], $position['line_cache'])) {
            throw new Exception('Invalid position data');
        }

        $this->lineCache = $position['line_cache'];

        if (!$this->isFileOpen()) {
            throw new Exception('File not open');
        }

        if (fseek($this->fileHandle, $position['pos']) === -1) {
            throw new Exception('Cannot set file position');
        }

        return true;
    }

    private function getNextLine() {
        if (!$this->isFileOpen()) {
            return false;
        }

        if (count($this->lineCache) === 1) {
            $content = '';
            do {
                $chunk = fread($this->fileHandle, 8192);
                if ($chunk === false) {
                    break;
                }
                $content .= $chunk;
            } while (!empty($chunk) && strpos($chunk, "\n") === false);

            if (empty($content)) {
                return false;
            }

            $lines = explode("\n", $content);
            
            if (!empty($this->lineCache[0])) {
                $lines[0] = $this->lineCache[0] . $lines[0];
            }

            $this->lineCache = $lines;
        }

        return array_shift($this->lineCache);
    }

    private function getFilePath(string $file): string {
        if (preg_match('/\.sql\.gz$/i', $file)) {
            return 'compress.zlib://' . $file;
        }
        if (preg_match('/\.sql\.bz2$/i', $file)) {
            return 'compress.bzip2://' . $file;
        }
        if (preg_match('/\.sql$/i', $file)) {
            return $file;
        }
        
        throw new Exception('Invalid SQL file extension');
    }

    public function getFileSize(): int {
        if (!$this->isFileOpen()) throw new Exception('Cannot get SQL file size - file not open');
        
        $currentPos = ftell($this->fileHandle);
        if ($currentPos === false) throw new Exception('Cannot determine current file position');
        
        if (fseek($this->fileHandle, 0, SEEK_END) === -1) throw new Exception('Cannot seek to end of file');
        $size = ftell($this->fileHandle);
        if ($size === false) throw new Exception('Cannot determine file size');
        
        if (fseek($this->fileHandle, $currentPos, SEEK_SET) === -1) {
            throw new Exception('Cannot return to original file position');
        }
        
        return $size;
    }
    private function isFileOpen(): bool {
        return is_resource($this->fileHandle);
    }

    private function closeFile(): void {
        if ($this->isFileOpen()) {
            fclose($this->fileHandle);
        }
    }
}
