<?php

declare(strict_types=1);

namespace App\Service;

use DateTimeImmutable;
use RuntimeException;
use Symfony\Component\HttpKernel\KernelInterface;

use function dirname;

class DocumentStorageService
{
    private readonly string $storageDir;

    public function __construct(
        private readonly KernelInterface $kernel,
    ) {
        $this->storageDir = $this->kernel->getProjectDir().'/var/storage/documents';
    }

    public function getStorageDir(): string
    {
        return $this->storageDir;
    }

    public function getAbsolutePath(string $relativePath): string
    {
        if (str_contains($relativePath, '..')) {
            throw new RuntimeException(sprintf('Path traversal detected: "%s"', $relativePath));
        }

        $absolutePath = $this->storageDir.'/'.$relativePath;
        $realPath     = realpath($absolutePath);

        if (false !== $realPath && !str_starts_with($realPath, realpath($this->storageDir))) {
            throw new RuntimeException(sprintf('Path traversal detected: "%s"', $relativePath));
        }

        return $absolutePath;
    }

    /**
     * Store binary content and return the relative path.
     * Path format: {year}/{month}/{documentId}.{ext}.
     */
    public function store(string $content, string $documentId, string $extension): string
    {
        $now          = new DateTimeImmutable();
        $subDir       = $now->format('Y').'/'.$now->format('m');
        $relativePath = $subDir.'/'.$documentId.'.'.$extension;
        $absolutePath = $this->storageDir.'/'.$relativePath;

        $dir = dirname($absolutePath);
        if (!is_dir($dir)) {
            if (!mkdir($dir, 0o775, true) && !is_dir($dir)) {
                throw new RuntimeException(sprintf('Cannot create directory "%s"', $dir));
            }
        }

        $bytesWritten = file_put_contents($absolutePath, $content);
        if (false === $bytesWritten) {
            throw new RuntimeException(sprintf('Cannot write file "%s"', $absolutePath));
        }

        return $relativePath;
    }

    /**
     * Store a file from a given source path (e.g., temp upload).
     */
    public function storeFromPath(string $sourcePath, string $documentId, string $extension): string
    {
        $content = file_get_contents($sourcePath);
        if (false === $content) {
            throw new RuntimeException(sprintf('Cannot read source file "%s"', $sourcePath));
        }

        return $this->store($content, $documentId, $extension);
    }

    public function read(string $relativePath): string
    {
        $absolutePath = $this->getAbsolutePath($relativePath);
        if (!file_exists($absolutePath)) {
            throw new RuntimeException(sprintf('File not found: "%s"', $absolutePath));
        }

        $content = file_get_contents($absolutePath);
        if (false === $content) {
            throw new RuntimeException(sprintf('Cannot read file "%s"', $absolutePath));
        }

        return $content;
    }

    public function delete(string $relativePath): bool
    {
        $absolutePath = $this->getAbsolutePath($relativePath);
        if (!file_exists($absolutePath)) {
            return false;
        }

        return @unlink($absolutePath);
    }

    public function exists(string $relativePath): bool
    {
        return file_exists($this->getAbsolutePath($relativePath));
    }

    public function isBase64DataUri(string $path): bool
    {
        return str_starts_with($path, 'data:');
    }

    public function extensionFromMimeType(string $mimeType): string
    {
        $map = [
            'application/pdf'                                                         => 'pdf',
            'image/jpeg'                                                              => 'jpg',
            'image/png'                                                               => 'png',
            'image/gif'                                                               => 'gif',
            'image/webp'                                                              => 'webp',
            'image/svg+xml'                                                           => 'svg',
            'image/bmp'                                                               => 'bmp',
            'text/plain'                                                              => 'txt',
            'text/csv'                                                                => 'csv',
            'application/json'                                                        => 'json',
            'application/xml'                                                         => 'xml',
            'application/zip'                                                         => 'zip',
            'audio/mpeg'                                                              => 'mp3',
            'audio/ogg'                                                               => 'ogg',
            'video/mp4'                                                               => 'mp4',
            'video/webm'                                                              => 'webm',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'       => 'xlsx',
            'application/msword'                                                      => 'doc',
            'application/vnd.ms-excel'                                                => 'xls',
        ];

        return $map[$mimeType] ?? 'bin';
    }

    public function extensionFromFilename(string $filename): string
    {
        $ext = pathinfo($filename, PATHINFO_EXTENSION);

        return '' !== $ext ? strtolower($ext) : 'bin';
    }
}
