<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Ferienpass\CoreBundle\Repository\AttendanceExtraFileRepository;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;

/**
 * The answer to an offer's extra application question of type `file` — a photo or
 * PDF the applicant uploaded (e.g. a swimming certificate, a medical note).
 *
 * The bytes live in the same tenant-isolated S3 storage as the inbound email
 * attachments (`inbound_attachments`), and they arrive from exactly as untrusted
 * a source, so they pass the very same gate:
 * {@see \Ferienpass\CoreBundle\Comment\InboundAttachmentPolicy} — scoped down to
 * the kinds the question allows. `name`/`mimeType`/`size` are therefore the
 * sanitized, verified values, never the client's claims.
 *
 * A row with `attendance = null` is a *staged* upload: the application form lets
 * people attach a file before the application exists, so the file is stored (and
 * this row written) right away and adopted by the attendance on submit. Staged
 * rows that never get adopted are pruned by
 * {@see \Ferienpass\CoreBundle\Command\PruneExtraFilesCommand}.
 */
#[ORM\Entity(repositoryClass: AttendanceExtraFileRepository::class)]
#[ORM\Index(columns: ['attendance_id'])]
#[ORM\Index(columns: ['createdAt'])]
class AttendanceExtraFile implements \Stringable
{
    #[ORM\Id]
    #[ORM\Column(type: UuidType::NAME)]
    private Uuid $uuid;

    #[ORM\ManyToOne(targetEntity: Attendance::class, inversedBy: 'extraFiles')]
    #[ORM\JoinColumn(name: 'attendance_id', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')]
    private ?Attendance $attendance = null;

    /** The uuid of the offer's extra question this file answers. */
    #[ORM\Column(type: 'string', length: 36)]
    private string $question;

    /** Path within the `inbound_attachments` virtual filesystem (never client-controlled). */
    #[ORM\Column(type: 'string', length: 255)]
    private string $path;

    /** Sanitized original filename, used as the download filename and label. */
    #[ORM\Column(type: 'string', length: 255)]
    private string $name;

    /** Canonical MIME type, derived from the verified content (not the claimed one). */
    #[ORM\Column(type: 'string', length: 128)]
    private string $mimeType;

    #[ORM\Column(type: 'integer', options: ['unsigned' => true])]
    private int $size;

    #[ORM\Column(type: 'datetime_immutable', options: ['default' => 'CURRENT_TIMESTAMP'])]
    private \DateTimeImmutable $createdAt;

    public function __construct(string $question, string $path, string $name, string $mimeType, int $size, ?Attendance $attendance = null)
    {
        $this->uuid = Uuid::v7();
        $this->question = $question;
        $this->path = $path;
        $this->name = $name;
        $this->mimeType = $mimeType;
        $this->size = $size;
        $this->createdAt = new \DateTimeImmutable();

        $this->setAttendance($attendance);
    }

    public function __toString(): string
    {
        return $this->name;
    }

    public function getUuid(): Uuid
    {
        return $this->uuid;
    }

    public function getAttendance(): ?Attendance
    {
        return $this->attendance;
    }

    public function setAttendance(?Attendance $attendance): void
    {
        $this->attendance = $attendance;

        if (null !== $attendance) {
            $attendance->addExtraFile($this);
        }
    }

    public function getQuestion(): string
    {
        return $this->question;
    }

    public function getPath(): string
    {
        return $this->path;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function getMimeType(): string
    {
        return $this->mimeType;
    }

    public function getSize(): int
    {
        return $this->size;
    }

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

    public function isImage(): bool
    {
        return str_starts_with($this->mimeType, 'image/');
    }

    public function isPdf(): bool
    {
        return 'application/pdf' === $this->mimeType;
    }
}
