<?php

declare(strict_types=1);

namespace App\Framework\Filesystem\Traits;

use App\Framework\Filesystem\Exceptions\FileNotFoundException;
use App\Framework\Filesystem\Exceptions\FileReadException;
use App\Framework\Filesystem\Exceptions\FileWriteException;
use Generator;

/**
 * Trait für Stream-basierte Storage-Operationen
 *
 * Implementiert Stream-Funktionalität für große Dateien
 * ohne komplettes Laden in den Speicher.
 */
trait StreamableStorageTrait
{
    public function readStream(string $path)
    {
        if (! $this->exists($path)) {
            throw new FileNotFoundException($path);
        }

        $resolvedPath = $this->resolvePath($path);
        $stream = @fopen($resolvedPath, 'r');
        if ($stream === false) {
            throw new FileReadException($path);
        }

        return $stream;
    }

    public function writeStream(string $path)
    {
        $resolvedPath = $this->resolvePath($path);
        $dir = dirname($resolvedPath);
        if (! is_dir($dir)) {
            $this->createDirectory($path);
        }

        $stream = @fopen($resolvedPath, 'w');
        if ($stream === false) {
            throw new FileWriteException($path);
        }

        return $stream;
    }

    public function appendStream(string $path)
    {
        $resolvedPath = $this->resolvePath($path);
        $dir = dirname($resolvedPath);
        if (! is_dir($dir)) {
            $this->createDirectory($path);
        }

        $stream = @fopen($resolvedPath, 'a');
        if ($stream === false) {
            throw new FileWriteException($path);
        }

        return $stream;
    }

    public function putStream(string $path, $stream): void
    {
        $writeStream = $this->writeStream($path);

        if (@stream_copy_to_stream($stream, $writeStream) === false) {
            @fclose($writeStream);

            throw new FileWriteException($path);
        }

        @fclose($writeStream);
    }

    public function copyStream(string $source, string $destination): void
    {
        $sourceStream = $this->readStream($source);
        $this->putStream($destination, $sourceStream);
        @fclose($sourceStream);
    }

    public function readLines(string $path): Generator
    {
        $stream = $this->readStream($path);

        try {
            while (($line = @fgets($stream)) !== false) {
                yield rtrim($line, "\r\n");
            }
        } finally {
            @fclose($stream);
        }
    }
}
