<?php

namespace App\Entity;

use App\Repository\ScanSessionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: ScanSessionRepository::class)]
#[ORM\Table(name: 'scan_sessions')]
#[ORM\Index(name: 'idx_sessions_email_created', columns: ['user_email', 'created_at'])]
#[ORM\Index(name: 'idx_sessions_email_status', columns: ['user_email', 'status'])]
class ScanSession
{
    public const STATUS_EN_COURS = 'en_cours';
    public const STATUS_A_VALIDER = 'a_valider';
    public const STATUS_VALIDE = 'valide';
    public const STATUS_ANNULE = 'annule';

    public const ALL_STATUSES = [
        self::STATUS_EN_COURS,
        self::STATUS_A_VALIDER,
        self::STATUS_VALIDE,
        self::STATUS_ANNULE,
    ];

    /** UUID généré côté client (clé primaire assignée). */
    #[ORM\Id]
    #[ORM\Column(length: 36)]
    private string $id;

    #[ORM\Column(length: 180)]
    private string $userEmail;

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
    private \DateTimeImmutable $createdAt;

    #[ORM\Column(length: 255)]
    private string $label;

    #[ORM\Column]
    private int $count = 0;

    #[ORM\Column]
    private int $totalFiles = 0;

    /** @var array<int, array<string, mixed>> Résultats de scan (paires plaque/VIN). */
    #[ORM\Column(type: Types::JSON)]
    private array $results = [];

    #[ORM\Column(length: 16, options: ['default' => self::STATUS_EN_COURS])]
    private string $status = self::STATUS_EN_COURS;

    #[ORM\Column(length: 255, nullable: true)]
    private ?string $locationNote = null;

    /** Type d'inventaire : 'VN' (véhicules neufs), 'VO' (véhicules d'occasion) ou 'VNVO' (les deux). */
    #[ORM\Column(length: 4, nullable: true)]
    private ?string $scanType = null;

    /** @var list<string> Concessions de l'utilisateur au moment du scan (snapshot). */
    #[ORM\Column(type: Types::JSON)]
    private array $concessions = [];

    #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
    private ?\DateTimeImmutable $archivedAt = null;

    /** Commentaire libre associé aux documents joints ("Autres documents"). */
    #[ORM\Column(type: Types::TEXT, nullable: true)]
    private ?string $attachmentsComment = null;

    /**
     * Signature manuscrite capturée au lancement du scan, sous forme d'image
     * (data-URI base64, ex. "data:image/jpeg;base64,..."). null = inventaire ancien
     * créé avant l'ajout de la signature. Stockée en MEDIUMTEXT (length > 64 Ko)
     * pour ne pas tronquer une image un peu volumineuse.
     */
    #[ORM\Column(type: Types::TEXT, length: 16_777_215, nullable: true)]
    private ?string $signature = null;

    /** Date à laquelle la signature a été apposée (au lancement du scan). */
    #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
    private ?\DateTimeImmutable $signatureDate = null;

    /**
     * Dossier S3 de cette session (figé à la création), ex.
     * "APP_WEB_FINANCE/SCAN_INVENTAIRE/RECETTE/2026-06-17_jdupont_Scan-...__2c1dbbe6".
     * null = photos stockées en local sur le serveur (comportement historique).
     * Les clés des images valent <s3Prefix>/<imageId>.jpg.
     */
    #[ORM\Column(length: 512, nullable: true)]
    private ?string $s3Prefix = null;

    /** @var Collection<int, ScanAttachment> */
    #[ORM\OneToMany(mappedBy: 'scan', targetEntity: ScanAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
    #[ORM\OrderBy(['uploadedAt' => 'ASC'])]
    private Collection $attachments;

    public function __construct(string $id, string $userEmail)
    {
        $this->id = $id;
        $this->userEmail = $userEmail;
        $this->createdAt = new \DateTimeImmutable();
        $this->attachments = new ArrayCollection();
    }

    public function getId(): string
    {
        return $this->id;
    }

    public function getUserEmail(): string
    {
        return $this->userEmail;
    }

    public function getCreatedAt(): \DateTimeImmutable
    {
        return $this->createdAt;
    }

    public function setCreatedAt(\DateTimeImmutable $createdAt): static
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getLabel(): string
    {
        return $this->label;
    }

    public function setLabel(string $label): static
    {
        $this->label = $label;

        return $this;
    }

    public function getCount(): int
    {
        return $this->count;
    }

    public function setCount(int $count): static
    {
        $this->count = $count;

        return $this;
    }

    public function getTotalFiles(): int
    {
        return $this->totalFiles;
    }

    public function setTotalFiles(int $totalFiles): static
    {
        $this->totalFiles = $totalFiles;

        return $this;
    }

    /** @return array<int, array<string, mixed>> */
    public function getResults(): array
    {
        return $this->results;
    }

    /** @param array<int, array<string, mixed>> $results */
    public function setResults(array $results): static
    {
        $this->results = $results;

        return $this;
    }

    public function getStatus(): string
    {
        return $this->status;
    }

    public function setStatus(string $status): static
    {
        if (!\in_array($status, self::ALL_STATUSES, true)) {
            throw new \InvalidArgumentException(sprintf('Statut invalide : %s', $status));
        }
        $this->status = $status;

        return $this;
    }

    public function getLocationNote(): ?string
    {
        return $this->locationNote;
    }

    public function setLocationNote(?string $locationNote): static
    {
        $locationNote = null === $locationNote ? null : trim($locationNote);
        $this->locationNote = '' === $locationNote ? null : $locationNote;

        return $this;
    }

    public const SCAN_TYPES = ['VN', 'VO', 'VNVO'];

    /** Libellés courts (badges) par type d'inventaire. */
    public const SCAN_TYPE_LABELS = [
        'VN' => 'VN',
        'VO' => 'VO',
        'VNVO' => 'VN & VO',
    ];

    public function getScanType(): ?string
    {
        return $this->scanType;
    }

    /** Libellé court affichable du type d'inventaire (badge), ou null si non renseigné. */
    public static function scanTypeLabel(?string $type): ?string
    {
        return $type === null ? null : (self::SCAN_TYPE_LABELS[$type] ?? $type);
    }

    public function setScanType(?string $scanType): static
    {
        $this->scanType = \in_array($scanType, self::SCAN_TYPES, true) ? $scanType : null;

        return $this;
    }

    /** @return list<string> */
    public function getConcessions(): array
    {
        return $this->concessions;
    }

    /** @param array<string> $concessions */
    public function setConcessions(array $concessions): static
    {
        $this->concessions = array_values(array_filter(
            array_map(static fn ($c) => \is_string($c) ? trim($c) : '', $concessions),
            static fn (string $c) => '' !== $c,
        ));

        return $this;
    }

    public function getArchivedAt(): ?\DateTimeImmutable
    {
        return $this->archivedAt;
    }

    public function isArchived(): bool
    {
        return null !== $this->archivedAt;
    }

    public function archive(): static
    {
        $this->archivedAt ??= new \DateTimeImmutable();

        return $this;
    }

    public function unarchive(): static
    {
        $this->archivedAt = null;

        return $this;
    }

    public function getS3Prefix(): ?string
    {
        return $this->s3Prefix;
    }

    public function setS3Prefix(?string $s3Prefix): static
    {
        $s3Prefix = null === $s3Prefix ? null : trim($s3Prefix);
        $this->s3Prefix = '' === $s3Prefix ? null : $s3Prefix;

        return $this;
    }

    /** True si les photos de cette session sont stockées sur S3 (et non en local). */
    public function isOnS3(): bool
    {
        return null !== $this->s3Prefix;
    }

    public function getSignature(): ?string
    {
        return $this->signature;
    }

    /** Accepte uniquement une data-URI d'image (sinon ignore) ; chaîne vide → null. */
    public function setSignature(?string $signature): static
    {
        $signature = null === $signature ? null : trim($signature);
        if ('' === $signature || null === $signature) {
            $this->signature = null;
        } elseif (str_starts_with($signature, 'data:image/')) {
            $this->signature = $signature;
        }

        return $this;
    }

    public function hasSignature(): bool
    {
        return null !== $this->signature && '' !== $this->signature;
    }

    public function getSignatureDate(): ?\DateTimeImmutable
    {
        return $this->signatureDate;
    }

    public function setSignatureDate(?\DateTimeImmutable $signatureDate): static
    {
        $this->signatureDate = $signatureDate;

        return $this;
    }

    public function getAttachmentsComment(): ?string
    {
        return $this->attachmentsComment;
    }

    public function setAttachmentsComment(?string $comment): static
    {
        $comment = null === $comment ? null : trim($comment);
        $this->attachmentsComment = '' === $comment ? null : $comment;

        return $this;
    }

    /** @return Collection<int, ScanAttachment> */
    public function getAttachments(): Collection
    {
        return $this->attachments;
    }

    public function addAttachment(ScanAttachment $attachment): static
    {
        if (!$this->attachments->contains($attachment)) {
            $this->attachments->add($attachment);
            $attachment->setScan($this);
        }

        return $this;
    }

    public function removeAttachment(ScanAttachment $attachment): static
    {
        $this->attachments->removeElement($attachment);

        return $this;
    }
}
