<?php
declare(strict_types=1);

namespace Archive\StreamWrapper\Wrappers;

use App\Framework\Database\DatabaseManager;
use App\Framework\StreamWrapper\Helper\StreamWrapperHelper;
use Archive\StreamWrapper\Context\StreamContext;
use Archive\StreamWrapper\StreamWrapperInterface;

/**
 * Stream-Wrapper für Database-Operationen
 * Syntax: db://connection/table/operation?params
 */
class DatabaseStreamWrapper implements StreamWrapperInterface
{
    public $context;
    private string $content = '';
    private int $position = 0;
    private string $mode = 'r';
    private array $parsedUrl = [];
    private ?StreamContext $streamContext = null;

    private DatabaseManager $database;
    private string $connection;
    private string $table;
    private string $operation;
    private array $params = [];

    public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
    {
        $this->streamContext = StreamWrapperHelper::initializeContext($this->context);
        $this->parsedUrl = StreamWrapperHelper::parseUrl($path);
        $this->mode = $mode;

        $this->connection = $this->parsedUrl['host'] ?: 'default';

        $pathParts = explode('/', $this->parsedUrl['path']);
        $this->table = $pathParts[0] ?? '';
        $this->operation = $pathParts[1] ?? 'select';

        // Query-Parameter parsen
        if ($this->parsedUrl['query']) {
            parse_str($this->parsedUrl['query'], $this->params);
        }

        // Database-Manager initialisieren
        $this->database = new DatabaseManager();

        // Bei Lese-Operationen Query ausführen
        if (str_contains($mode, 'r') || str_contains($mode, '+')) {
            $this->content = $this->loadContent($this->operation);
        }

        return true;
    }

    public function stream_read(int $count): string|false
    {
        return StreamWrapperHelper::streamRead($this->content, $this->position, $count);
    }

    public function stream_write(string $data): int|false
    {
        return StreamWrapperHelper::streamWrite($this->content, $this->position, $data);
    }

    public function stream_tell(): int|false
    {
        return $this->position;
    }

    public function stream_eof(): bool
    {
        return $this->position >= strlen($this->content);
    }

    public function stream_seek(int $offset, int $whence = SEEK_SET): bool
    {
        return StreamWrapperHelper::streamSeek($this->content, $this->position, $offset, $whence);
    }

    public function stream_close(): void
    {
        // Bei Schreib-Modi Operation ausführen
        if (str_contains($this->mode, 'w') || str_contains($this->mode, 'a') || str_contains($this->mode, '+')) {
            $this->saveContent($this->operation, $this->content);
        }

        $this->content = '';
        $this->position = 0;
    }

    public function stream_stat(): array|false
    {
        return StreamWrapperHelper::streamStat($this->content);
    }

    public function url_stat(string $path, int $flags): array|false
    {
        return StreamWrapperHelper::createDefaultStat(strlen($this->content));
    }

    // Nicht unterstützte Operationen
    public function mkdir(string $path, int $mode, int $options): bool { return false; }
    public function rmdir(string $path, int $options): bool { return false; }
    public function dir_opendir(string $path, int $options): bool { return false; }
    public function dir_readdir(): string|false { return false; }
    public function dir_rewinddir(): bool { return false; }
    public function dir_closedir(): bool { return false; }
    public function rename(string $path_from, string $path_to): bool { return false; }
    public function unlink(string $path): bool { return false; }

    private function loadContent(string $operation): string
    {
        $connection = $this->streamContext?->getOption('db', 'connection', $this->connection);

        $result = match($operation) {
            'select', 'all' => $this->executeSelect(),
            'count' => $this->executeCount(),
            'exists' => $this->executeExists(),
            default => []
        };

        return json_encode($result);
    }

    private function saveContent(string $operation, string $content): bool
    {
        $data = json_decode($content, true);

        return match($operation) {
            'insert' => $this->executeInsert($data),
            'update' => $this->executeUpdate($data),
            'delete' => $this->executeDelete($data),
            'bulk-insert' => $this->executeBulkInsert($data),
            default => false
        };
    }

    private function executeSelect(): array
    {
        $query = $this->database->connection($this->connection)->table($this->table);

        foreach ($this->params as $key => $value) {
            if ($key === 'id') {
                $query->where('id', $value);
            } elseif (str_starts_with($key, 'where_')) {
                $field = substr($key, 6);
                $query->where($field, $value);
            }
        }

        return $query->get()->toArray();
    }

    private function executeCount(): array
    {
        $count = $this->database->connection($this->connection)->table($this->table)->count();
        return ['count' => $count];
    }

    private function executeExists(): array
    {
        $exists = $this->database->connection($this->connection)->table($this->table);

        if (isset($this->params['id'])) {
            $exists->where('id', $this->params['id']);
        }

        return ['exists' => $exists->exists()];
    }

    private function executeInsert(array $data): bool
    {
        return $this->database->connection($this->connection)->table($this->table)->insert($data);
    }

    private function executeUpdate(array $data): bool
    {
        $query = $this->database->connection($this->connection)->table($this->table);

        if (isset($this->params['id'])) {
            $query->where('id', $this->params['id']);
        }

        return $query->update($data) > 0;
    }

    private function executeDelete(array $data): bool
    {
        $query = $this->database->connection($this->connection)->table($this->table);

        if (isset($this->params['id'])) {
            $query->where('id', $this->params['id']);
        }

        return $query->delete() > 0;
    }

    private function executeBulkInsert(array $data): bool
    {
        return $this->database->connection($this->connection)->table($this->table)->insert($data);
    }
}
