<?php

declare(strict_types=1);

namespace App\Framework\Filesystem;

use App\Framework\Filesystem\ValueObjects\FileMetadata;
use App\Framework\Filesystem\ValueObjects\FilePath;

/**
 * Service for file system operations.
 * Handles all file I/O operations separate from File value objects.
 */
final class FileSystemService
{
    public function __construct(
        private readonly Storage $storage = new FileStorage()
    ) {
    }

    /**
     * Read file content
     */
    public function readFile(File $file): string
    {
        return $this->storage->get($file->getPathString());
    }

    /**
     * Write content to file
     */
    public function writeFile(File $file, string $content): void
    {
        $this->storage->put($file->getPathString(), $content);
    }

    /**
     * Create File object from path (loads metadata automatically)
     */
    public function createFile(FilePath $path): File
    {
        $pathString = $path->toString();

        $metadata = new FileMetadata(
            size: $this->storage->size($pathString),
            lastModified: $this->storage->lastModified($pathString),
            mimeType: $this->storage->getMimeType($pathString),
            isReadable: $this->storage->isReadable($pathString),
            isWritable: $this->storage->isWritable($pathString)
        );

        return new File($path, $metadata);
    }

    /**
     * Check if file exists
     */
    public function fileExists(FilePath $path): bool
    {
        return $this->storage->exists($path->toString());
    }

    /**
     * Delete file
     */
    public function deleteFile(File $file): void
    {
        $this->storage->delete($file->getPathString());
    }

    /**
     * Copy file to destination
     */
    public function copyFile(File $file, FilePath $destination): File
    {
        $this->storage->copy($file->getPathString(), $destination->toString());

        return $this->createFile($destination);
    }

    /**
     * Get fresh metadata for existing file
     */
    public function getMetadata(FilePath $path): FileMetadata
    {
        $pathString = $path->toString();

        return new FileMetadata(
            size: $this->storage->size($pathString),
            lastModified: $this->storage->lastModified($pathString),
            mimeType: $this->storage->getMimeType($pathString),
            isReadable: $this->storage->isReadable($pathString),
            isWritable: $this->storage->isWritable($pathString)
        );
    }

    /**
     * Refresh file with current metadata
     */
    public function refreshFile(File $file): File
    {
        $metadata = $this->getMetadata($file->getPath());

        return new File($file->getPath(), $metadata);
    }

    /**
     * Stream file content line by line for memory-efficient processing
     * @return Generator<string>
     */
    public function streamFileLines(File $file): \Generator
    {
        $handle = fopen($file->getPathString(), 'r');
        if ($handle === false) {
            throw new \RuntimeException("Cannot open file: " . $file->getPathString());
        }

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

    /**
     * Stream file content in chunks for large file processing
     * @return Generator<string>
     */
    public function streamFileChunks(File $file, int $chunkSize = 8192): \Generator
    {
        $handle = fopen($file->getPathString(), 'r');
        if ($handle === false) {
            throw new \RuntimeException("Cannot open file: " . $file->getPathString());
        }

        try {
            while (! feof($handle)) {
                $chunk = fread($handle, $chunkSize);
                if ($chunk !== false && $chunk !== '') {
                    yield $chunk;
                }
            }
        } finally {
            fclose($handle);
        }
    }
}
