<?php

declare(strict_types=1);

namespace App\Module\Integration\Infrastructure\Controller;

use App\Module\Integration\Domain\Exception\InvalidPathException;
use App\Module\Integration\Domain\Exception\ShareConnectionException;
use App\Module\Integration\Domain\Exception\ShareNotConfiguredException;
use App\Module\Integration\Domain\Service\FileSource;
use App\Module\Integration\Domain\Service\SharePathResolver;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Mime\MimeTypes;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

use function is_resource;

class ShareDownloadController extends AbstractController
{
    public function __construct(
        private readonly FileSource $fileSource,
        private readonly SharePathResolver $pathResolver,
    ) {}

    #[Route('/api/share/download', name: 'share_download', methods: ['GET'], priority: 1)]
    #[IsGranted('ROLE_USER')]
    public function __invoke(Request $request): Response
    {
        $rawPath = (string) $request->query->get('path', '');

        try {
            $path = $this->pathResolver->normalizeRelative($rawPath);
        } catch (InvalidPathException) {
            return new Response('Invalid path.', 400);
        }

        if ('' === $path) {
            throw new NotFoundHttpException('No file requested.');
        }

        try {
            $stream = $this->fileSource->read($path);
        } catch (ShareNotConfiguredException) {
            return new Response('Share not configured.', 409);
        } catch (ShareConnectionException) {
            throw new NotFoundHttpException('File not found.');
        }

        $name      = basename($path);
        $extension = pathinfo($name, PATHINFO_EXTENSION);
        $mime      = MimeTypes::getDefault()->getMimeTypes($extension)[0] ?? 'application/octet-stream';

        // Anti-XSS : seuls des types non exécutables sont servis inline (images hors SVG, PDF).
        // Tout le reste (HTML, SVG, octet-stream, etc.) est forcé en attachment, même si inline est demandé.
        $inlineSafe  = ('image/svg+xml' !== $mime && str_starts_with($mime, 'image/')) || 'application/pdf' === $mime;
        $wantInline  = 'attachment' !== $request->query->get('disposition');
        $disposition = ($inlineSafe && $wantInline) ? HeaderUtils::DISPOSITION_INLINE : HeaderUtils::DISPOSITION_ATTACHMENT;

        $response = new StreamedResponse(function () use ($stream): void {
            if (is_resource($stream)) {
                fpassthru($stream);
                fclose($stream);
            }
        });
        $response->headers->set('Content-Type', $mime);
        $response->headers->set('Content-Disposition', HeaderUtils::makeDisposition($disposition, $name));
        // Empêche le navigateur de "deviner" un type exécutable à partir du contenu.
        $response->headers->set('X-Content-Type-Options', 'nosniff');

        return $response;
    }
}
