<?php

namespace App\Service;

use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;

/**
 * Stockage privé des images scannées sur le filesystem (hors du DocumentRoot).
 * Path : {baseDir}/{md5(userEmail)}/{sessionId}/{imageId}.jpg
 *
 * Pourquoi md5(userEmail) : évite l'email en clair dans le path, et garde une longueur fixe.
 * Pourquoi {imageId} en UUID : pas de path traversal possible (regex strict côté contrôleur).
 */
final class ImageStorage
{
    private const UUID_PATTERN = '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/';

    public function __construct(
        #[Autowire('%kernel.project_dir%/var/uploads')]
        private readonly string $baseDir,
        private readonly Filesystem $fs = new Filesystem(),
    ) {
    }

    public static function isValidId(string $id): bool
    {
        return 1 === preg_match(self::UUID_PATTERN, $id);
    }

    /**
     * Persiste l'image (base64) sous {baseDir}/{md5(email)}/{sessionId}/{imageId}.jpg.
     * Lève si les IDs sont invalides ou si le décodage échoue.
     */
    public function store(string $userEmail, string $sessionId, string $imageId, string $base64): void
    {
        if (!self::isValidId($sessionId) || !self::isValidId($imageId)) {
            throw new \InvalidArgumentException('Identifiant de session ou d\'image invalide.');
        }

        $binary = $this->decode($base64);
        $dir = $this->sessionDir($userEmail, $sessionId);
        $this->fs->mkdir($dir, 0750);
        $this->fs->dumpFile($dir . '/' . $imageId . '.jpg', $binary);
    }

    /**
     * Chemin absolu vers une image, ou null si elle n'existe pas / IDs invalides.
     */
    public function pathFor(string $userEmail, string $sessionId, string $imageId): ?string
    {
        if (!self::isValidId($sessionId) || !self::isValidId($imageId)) {
            return null;
        }

        $path = $this->sessionDir($userEmail, $sessionId) . '/' . $imageId . '.jpg';

        return is_file($path) ? $path : null;
    }

    /**
     * Copie une image existante vers un autre emplacement (autre session, éventuellement
     * autre propriétaire), en conservant le même imageId. Utilisé par la fusion d'inventaires.
     * Retourne false si la source n'existe pas ou si les IDs cibles sont invalides.
     */
    public function copyImage(string $fromEmail, string $fromSession, string $imageId, string $toEmail, string $toSession): bool
    {
        $src = $this->pathFor($fromEmail, $fromSession, $imageId);
        if (null === $src || !self::isValidId($toSession) || !self::isValidId($imageId)) {
            return false;
        }
        $dir = $this->sessionDir($toEmail, $toSession);
        $this->fs->mkdir($dir, 0750);
        $this->fs->copy($src, $dir . '/' . $imageId . '.jpg', true);

        return true;
    }

    /** Supprime une image précise (utilisé lors du remplacement/suppression d'une photo de paire). */
    public function delete(string $userEmail, string $sessionId, string $imageId): void
    {
        if (!self::isValidId($sessionId) || !self::isValidId($imageId)) {
            return;
        }
        $path = $this->sessionDir($userEmail, $sessionId) . '/' . $imageId . '.jpg';
        if (is_file($path)) {
            $this->fs->remove($path);
        }
    }

    /** Supprime tout le dossier d'une session (utilisé au DELETE de session). */
    public function deleteSession(string $userEmail, string $sessionId): void
    {
        if (!self::isValidId($sessionId)) {
            return;
        }
        $dir = $this->sessionDir($userEmail, $sessionId);
        if (is_dir($dir)) {
            $this->fs->remove($dir);
        }
    }

    /** Supprime tout le dossier d'un utilisateur (utilisé au "tout effacer"). */
    public function deleteAllForUser(string $userEmail): void
    {
        $dir = $this->userDir($userEmail);
        if (is_dir($dir)) {
            $this->fs->remove($dir);
        }
    }

    private function sessionDir(string $userEmail, string $sessionId): string
    {
        return $this->userDir($userEmail) . '/' . $sessionId;
    }

    private function userDir(string $userEmail): string
    {
        return $this->baseDir . '/' . md5(strtolower(trim($userEmail)));
    }

    private function decode(string $base64): string
    {
        $clean = preg_replace('/^data:[^;]+;base64,/', '', $base64) ?? $base64;
        $binary = base64_decode($clean, true);
        if (false === $binary) {
            throw new \InvalidArgumentException('Image base64 invalide.');
        }

        return $binary;
    }
}
