<?php

declare(strict_types=1);

namespace App\Controller;

use App\Repository\DocumentRepository;
use App\Service\DocumentStorageService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Routing\Attribute\Route;

use function strlen;

#[Route('/api/documents')]
class DocumentServeController extends AbstractController
{
    public function __construct(
        private readonly DocumentRepository $documentRepository,
        private readonly DocumentStorageService $storageService,
    ) {}

    #[Route('/{id}/file', name: 'document_serve_file', methods: ['GET'])]
    public function serve(string $id): Response
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        $document = $this->documentRepository->find($id);
        if (!$document) {
            return $this->json(['error' => 'Document not found.'], 404);
        }

        $path = $document->getPath();

        // Backward compatibility: serve Base64 data URIs from DB
        if ($this->storageService->isBase64DataUri($path)) {
            $parts   = explode(',', $path, 2);
            $content = base64_decode($parts[1] ?? '', true);
            if (false === $content) {
                return $this->json(['error' => 'Invalid document data.'], 500);
            }

            $disposition = HeaderUtils::makeDisposition(ResponseHeaderBag::DISPOSITION_INLINE, $document->getFilename());

            return new Response($content, 200, [
                'Content-Type'            => $document->getMimeType(),
                'Content-Disposition'     => $disposition,
                'Content-Length'          => (string) strlen($content),
                'Cache-Control'           => 'private, max-age=3600',
                'X-Content-Type-Options'  => 'nosniff',
                'Content-Security-Policy' => 'sandbox',
            ]);
        }

        // File-based path: serve from disk
        $absolutePath = $this->storageService->getAbsolutePath($path);
        if (!file_exists($absolutePath)) {
            return $this->json(['error' => 'File not found on disk.'], 404);
        }

        $response = new BinaryFileResponse($absolutePath);
        $response->headers->set('Content-Type', $document->getMimeType());
        $response->setContentDisposition(
            ResponseHeaderBag::DISPOSITION_INLINE,
            $document->getFilename()
        );
        $response->headers->set('Cache-Control', 'private, max-age=3600');
        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('Content-Security-Policy', 'sandbox');

        return $response;
    }

    #[Route('/{id}/download', name: 'document_download_file', methods: ['GET'])]
    public function download(string $id): Response
    {
        $this->denyAccessUnlessGranted('ROLE_VIEWER');

        $document = $this->documentRepository->find($id);
        if (!$document) {
            return $this->json(['error' => 'Document not found.'], 404);
        }

        $path = $document->getPath();

        if ($this->storageService->isBase64DataUri($path)) {
            $parts   = explode(',', $path, 2);
            $content = base64_decode($parts[1] ?? '', true);
            if (false === $content) {
                return $this->json(['error' => 'Invalid document data.'], 500);
            }

            $disposition = HeaderUtils::makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $document->getFilename());

            return new Response($content, 200, [
                'Content-Type'        => 'application/octet-stream',
                'Content-Disposition' => $disposition,
                'Content-Length'      => (string) strlen($content),
            ]);
        }

        $absolutePath = $this->storageService->getAbsolutePath($path);
        if (!file_exists($absolutePath)) {
            return $this->json(['error' => 'File not found on disk.'], 404);
        }

        $response = new BinaryFileResponse($absolutePath);
        $response->setContentDisposition(
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
            $document->getFilename()
        );

        return $response;
    }
}
