<?php

namespace App\Service;

/**
 * Configuration Amazon S3 lue depuis les variables d'environnement (.env.local).
 * Les identifiants (clé d'accès + secret) ne transitent jamais par l'UI ni la base :
 * ils restent dans .env.local (chmod 600, non accessible via le web).
 *
 * Convention de clé pour le stockage des photos d'inventaire :
 *   <prefix>/<dossier-de-session>/<imageId>.jpg
 * où le dossier de session est calculé UNE FOIS à la création (figé) et stocké sur
 * ScanSession.s3Prefix — un renommage ultérieur du scan ne déplace donc rien sur S3
 * (S3 ne sait pas déplacer un objet).
 */
final class S3Settings
{
    public const MAX_EXPIRY = 604800; // 7 jours, plafond SigV4
    private const DEFAULT_EXPIRY = 3600;

    public function __construct(
        private readonly string $s3AccessKey,
        private readonly string $s3SecretKey,
        private readonly string $s3Region,
        private readonly string $s3Bucket,
        private readonly string $s3Endpoint,
        private readonly int $s3Expiry,
        private readonly string $s3Prefix = '',
        private readonly bool $s3StorageEnabled = false,
    ) {
    }

    public function isConfigured(): bool
    {
        return $this->s3Region !== '' && $this->s3Bucket !== '' && $this->s3AccessKey !== '' && $this->s3SecretKey !== '';
    }

    /**
     * True si les NOUVEAUX scans doivent stocker leurs photos sur S3 (upload navigateur→S3)
     * plutôt qu'en local. Piloté par l'env S3_STORAGE ; nécessite une config S3 complète.
     */
    public function isStorageEnabled(): bool
    {
        return $this->s3StorageEnabled && $this->isConfigured();
    }

    public function region(): string
    {
        return $this->s3Region;
    }

    public function bucket(): string
    {
        return $this->s3Bucket;
    }

    public function accessKey(): string
    {
        return $this->s3AccessKey;
    }

    public function secretKey(): string
    {
        return $this->s3SecretKey;
    }

    public function endpoint(): string
    {
        return $this->s3Endpoint;
    }

    public function expiry(): int
    {
        return $this->s3Expiry > 0 ? min($this->s3Expiry, self::MAX_EXPIRY) : self::DEFAULT_EXPIRY;
    }

    /** Préfixe racine dans le bucket (sans / de bord), ex. "APP_WEB_FINANCE/SCAN_INVENTAIRE/RECETTE". */
    public function prefix(): string
    {
        return trim($this->s3Prefix, '/');
    }

    /**
     * Dossier de session (figé à la création) : <prefix>/<date>_<createur>_<nom-scan>__<id-court>.
     * Tous les caractères hors [A-Za-z0-9._-] sont assainis (les noms de scan contiennent
     * des espaces, « / », « : », des accents…).
     */
    public function sessionPrefix(string $creator, \DateTimeImmutable $createdAt, string $label, string $sessionId): string
    {
        $date = $createdAt->setTimezone(new \DateTimeZone('UTC'))->format('Y-m-d');
        $local = self::slug(explode('@', $creator)[0]);
        $labelSlug = self::slug($label);
        $short = substr(preg_replace('/[^0-9a-f]/', '', strtolower($sessionId)) ?? '', 0, 8);
        $folder = sprintf('%s_%s_%s__%s', $date, $local, $labelSlug, $short);

        return '' === $this->prefix() ? $folder : $this->prefix() . '/' . $folder;
    }

    /** Clé d'objet d'une image : <sessionPrefix>/<imageId>.jpg. */
    public function objectKey(string $sessionPrefix, string $imageId): string
    {
        return trim($sessionPrefix, '/') . '/' . $imageId . '.jpg';
    }

    /** Assainit un segment pour une clé S3 : translittère les accents puis ne garde que [A-Za-z0-9._-]. */
    public static function slug(string $s): string
    {
        $ascii = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
        if (false !== $ascii) {
            $s = $ascii;
        }
        $s = preg_replace('/[^A-Za-z0-9._-]+/', '-', $s) ?? '';
        $s = trim($s, '-_.');

        return '' === $s ? 'x' : $s;
    }
}
