<?php

namespace App\Services\Files;

use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;

class FileServeService
{
    public function meta(string $baseDir, string $name, array $allowedExtensions, ?string $downloadName = null): array
    {
        return $this->resolveFile($baseDir, $name, $allowedExtensions, $downloadName);
    }

    public function serveInline(
        string $baseDir,
        string $name,
        array $allowedExtensions,
        Request $request,
        ?string $downloadName = null,
        bool $nosniff = false
    ): Response {
        $meta = $this->resolveFile($baseDir, $name, $allowedExtensions, $downloadName);

        $ifNoneMatch = trim((string) $request->headers->get('If-None-Match', ''), '"');
        $ifModifiedSince = (string) $request->headers->get('If-Modified-Since', '');
        $imsTime = $ifModifiedSince !== '' ? strtotime($ifModifiedSince) : false;

        if (($ifNoneMatch !== '' && $ifNoneMatch === $meta['etag']) ||
            ($imsTime !== false && $imsTime >= $meta['mtime'])
        ) {
            return response('', 304, $this->notModifiedHeaders($meta));
        }

        $headers = [
            'Content-Type' => $meta['mime'],
            'Content-Disposition' => 'inline; filename="' . $meta['download_name'] . '"',
            'Content-Length' => (string) $meta['size'],
            'ETag' => $meta['etag'],
            'Last-Modified' => $meta['last_modified'],
            'Cache-Control' => 'public, max-age=86400',
        ];

        if ($nosniff) {
            $headers['X-Content-Type-Options'] = 'nosniff';
        }

        return response(file_get_contents($meta['path']), 200, $headers);
    }

    private function resolveFile(string $baseDir, string $name, array $allowedExtensions, ?string $downloadName): array
    {
        if ($name !== basename($name)) {
            throw new HttpException(400, 'Invalid filename');
        }

        $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
        if (!in_array($ext, $allowedExtensions, true)) {
            throw new HttpException(404, 'File not found');
        }

        $path = rtrim($baseDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
        if (!is_file($path)) {
            throw new HttpException(404, 'File not found');
        }

        $mime = $this->detectMime($path);
        $mtime = filemtime($path) ?: time();
        $size = filesize($path) ?: 0;
        $etag = md5($name . '|' . $mtime . '|' . $size);
        $downloadName = $downloadName ? $downloadName . '.' . $ext : $name;

        return [
            'name' => $name,
            'path' => $path,
            'mime' => $mime,
            'mtime' => $mtime,
            'size' => $size,
            'etag' => $etag,
            'download_name' => $downloadName,
            'last_modified' => gmdate('D, d M Y H:i:s', $mtime) . ' GMT',
        ];
    }

    private function detectMime(string $path): string
    {
        $mime = 'application/octet-stream';
        if (function_exists('finfo_open')) {
            $fi = finfo_open(FILEINFO_MIME_TYPE);
            if ($fi) {
                $detected = finfo_file($fi, $path);
                if ($detected) {
                    $mime = $detected;
                }
                finfo_close($fi);
            }
        } elseif (function_exists('mime_content_type')) {
            $mime = mime_content_type($path) ?: $mime;
        }

        return $mime;
    }

    private function notModifiedHeaders(array $meta): array
    {
        return [
            'ETag' => $meta['etag'],
            'Last-Modified' => $meta['last_modified'],
        ];
    }
}
