<?php

namespace App\Controller;

use App\Entity\User;
use App\Repository\ScanSessionRepository;
use App\Security\Voter\ScanVoter;
use App\Service\ImageStorage;
use App\Service\S3PostPolicy;
use App\Service\S3Settings;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

/**
 * Signe les POST policies S3 permettant au navigateur de téléverser les photos
 * directement sur S3 (le serveur OVH ne pouvant pas joindre S3). La clé d'objet
 * est calculée côté serveur (jamais dictée par le client) :
 *  - scan en cours de création (session pas encore enregistrée) → préfixe calculé
 *    depuis le créateur courant + date + libellé (identique à ce que pose save()) ;
 *  - session existante migrée S3 (remplacement de photo) → on réutilise son s3Prefix figé.
 */
#[IsGranted('ROLE_USER')]
final class S3UploadController extends AbstractController
{
    private const MAX_SIZE = 20 * 1024 * 1024;

    #[Route('/api/s3/sign', name: 'app_s3_sign', methods: ['POST'])]
    public function sign(
        Request $request,
        ScanSessionRepository $sessions,
        S3Settings $s3,
        S3PostPolicy $policy,
    ): JsonResponse {
        if (!$s3->isStorageEnabled()) {
            return $this->json(['error' => 'Stockage S3 non activé.'], Response::HTTP_CONFLICT);
        }

        $data = json_decode($request->getContent(), true);
        $sessionId = \is_array($data) ? ($data['sessionId'] ?? null) : null;
        $imageId = \is_array($data) ? ($data['imageId'] ?? null) : null;
        if (!\is_string($sessionId) || !\is_string($imageId)
            || !ImageStorage::isValidId($sessionId) || !ImageStorage::isValidId($imageId)) {
            return $this->json(['error' => 'Identifiants invalides.'], Response::HTTP_BAD_REQUEST);
        }

        /** @var User $user */
        $user = $this->getUser();

        $session = $sessions->findOneInScope($sessionId, $user->scopeConcessions());
        if (null !== $session) {
            // Remplacement de photo sur une session existante : doit être migrée S3 et éditable.
            if (!$session->isOnS3()) {
                return $this->json(['error' => 'Cet inventaire n\'est pas stocké sur S3.'], Response::HTTP_CONFLICT);
            }
            $this->denyAccessUnlessGranted(ScanVoter::EDIT_DOCUMENTS, $session);
            $prefix = (string) $session->getS3Prefix();
        } else {
            // Nouveau scan (offline-first, session pas encore enregistrée).
            $this->denyAccessUnlessGranted(ScanVoter::CREATE);
            $label = \is_array($data) && \is_string($data['label'] ?? null) ? $data['label'] : '';
            $createdAtRaw = \is_array($data) && \is_string($data['createdAt'] ?? null) ? $data['createdAt'] : 'now';
            try {
                $createdAt = new \DateTimeImmutable($createdAtRaw);
            } catch (\Exception) {
                $createdAt = new \DateTimeImmutable();
            }
            $prefix = $s3->sessionPrefix($user->getUserIdentifier(), $createdAt, $label, $sessionId);
        }

        $key = $s3->objectKey($prefix, $imageId);
        // Pas de success_action_redirect : le navigateur dépose en no-cors (réponse opaque),
        // donc success_action_status=201 suffit. Évite la redirection retour /s3/done dont
        // la lecture cross-origin était bloquée derrière le proxy OVH (cf. S3PostPolicy).
        $built = $policy->build($key, 'image/jpeg', self::MAX_SIZE);

        return $this->json([
            'url' => $built['url'],
            'fields' => $built['fields'],
            'key' => $key,
        ]);
    }

    /**
     * Cible de redirection (success_action_redirect) après un upload S3 réussi.
     * Chargée dans l'iframe cachée : comme elle est de notre origine, le JS peut lire
     * l'URL de l'iframe et en déduire le succès. Réponse minimale volontairement.
     */
    #[Route('/s3/done', name: 'app_s3_done', methods: ['GET'])]
    public function done(): Response
    {
        return new Response('ok', Response::HTTP_OK, ['Content-Type' => 'text/plain', 'X-Robots-Tag' => 'noindex']);
    }
}
