<?php

declare(strict_types=1);

namespace App\Entity\Core;

use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Metadata\Put;
use App\Entity\AccessWish\DocumentWish;
use App\Entity\Billing\SddRegie;
use App\Entity\Booking\CalendarSynchro;
use App\Entity\Booking\Event;
use App\Entity\Booking\EventReport;
use App\Entity\Booking\Work;
use App\Entity\Message\TemplateSystem;
use App\Entity\Network\Network;
use App\Entity\Organization\Activity;
use App\Entity\Organization\OnlineRegistrationSettings;
use App\Entity\Organization\Organization;
use App\Entity\Organization\Parameters;
use App\Entity\Person\Person;
use App\Enum\Core\FileHostEnum;
use App\Enum\Core\FileStatusEnum;
use App\Enum\Core\FileTypeEnum;
use App\Enum\Core\FileVisibilityEnum;
// use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
use App\Repository\Core\FileRepository;
use App\State\Processor\Core\FileProcessor;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JetBrains\PhpStorm\Pure;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * Fichier, généré ou uploadé, appartenant à une organisation ou à une personne.
 *
 * Security :
 *
 *   * @see App\Security\Voter\FileVoter
 */
#[ApiResource(
    operations: [
        new Get(security: "is_granted('READ', object)"),
        new Put(security: "is_granted('EDIT', object)"),
        new Patch(security: "is_granted('EDIT', object)"),
        new Post(security: "is_granted('CREATE', object)"),
        new Delete(security: "is_granted('DELETE', object)"),
    ],
    processor: FileProcessor::class
)]
// #[Auditable]
#[ORM\Entity(repositoryClass: FileRepository::class)]
class File
{
    #[ORM\Id]
    #[ORM\Column]
    #[ORM\GeneratedValue]
    private ?int $id = null;

    /**
     * Propriétaire du fichier.
     */
    #[ORM\ManyToOne(inversedBy: 'files')]
    #[ORM\JoinColumn(name: 'person_id', referencedColumnName: 'id')]
    private ?Person $person;

    /**
     * Organisation propriétaire du fichier.
     */
    #[ORM\ManyToOne(inversedBy: 'files')]
    #[ORM\JoinColumn(name: 'organization_id', referencedColumnName: 'id')]
    private ?Organization $organization;

    /**
     * Slug du fichier (i.e. le chemin d'accès relatif).
     */
    #[ORM\Column(length: 255, nullable: true)]
    private ?string $slug;

    /**
     * Chemin d'accès du fichier.
     */
    #[ORM\Column(length: 255, nullable: true)]
    private ?string $path;

    /**
     * Nom du fichier.
     */
    #[ORM\Column(length: 255)]
    private string $name;

    /**
     * Mimetype du fichier.
     */
    #[ORM\Column(length: 255, nullable: true)]
    private ?string $mimeType = null;

    /**
     * Visibilité du fichier (tout le monde, personne, l'organisation seulement...).
     */
    #[ORM\Column(length: 24, enumType: FileVisibilityEnum::class, options: ['default' => FileVisibilityEnum::NOBODY])]
    private FileVisibilityEnum $visibility = FileVisibilityEnum::NOBODY;

    /**
     * Configuration particulière associée au fichier (exemple: image recadrée).
     */
    #[ORM\Column(type: 'text', length: 255, nullable: true)]
    private ?string $config;

    /**
     * Type de document (uploaded, mail, bill...etc).
     */
    #[ORM\Column(length: 50, enumType: FileTypeEnum::class, options: ['default' => FileTypeEnum::NONE])]
    private FileTypeEnum $type = FileTypeEnum::NONE;

    /**
     * Taille du document en octets.
     */
    #[ORM\Column(nullable: true)]
    private ?int $size;

    /**
     * Un fichier est temporaire par exemple s'il a été généré et est stocké pour être téléchargé dans la foulée
     * Les fichiers temporaires peuvent être supprimés sans risque, à l'inverse des fichiers uploadés par les
     * utilisateurs par exemple.
     */
    #[ORM\Column(options: ['default' => false])]
    private bool $isTemporaryFile = false;

    /**
     * Date de création du fichier.
     */
    #[ORM\Column]
    private \DateTime $createDate;

    /**
     * Id de l'access ayant créé ce fichier.
     */
    #[ORM\Column(nullable: true, options: ['default' => null])]
    private ?int $createdBy = null;

    /**
     * Date de dernière mise à jour du fichier.
     */
    #[ORM\Column(nullable: true, options: ['default' => null])]
    private ?\DateTime $updateDate = null;

    /**
     * Id de l'access ayant mis à jour ce fichier le dernier.
     */
    #[ORM\Column(nullable: true, options: ['default' => null])]
    private ?int $updatedBy = null;

    /**
     * Statut du fichier (en cours de génération, prêt, supprimé, etc.).
     */
    #[ORM\Column(length: 50, enumType: FileStatusEnum::class)]
    private FileStatusEnum $status;

    /**
     * Serveur sur lequel le fichier est physiquement stocké.
     */
    #[ORM\Column(length: 5, enumType: FileHostEnum::class, options: ['default' => FileHostEnum::AP2I])]
    private FileHostEnum $host = FileHostEnum::AP2I;

    //    #[ORM\Column]
    //    private ?int $eventReport_id;

    /**
     * TODO: complete.
     */
    #[ORM\Column(nullable: true, options: ['default' => null])]
    private ?\DateTime $availabilityDate = null;

    //    #[ORM\Column]
    //    private ?int $documentWish_id;
    //
    //    #[ORM\Column]
    //    private ?int $onlineRegistrationSetting_id;
    //
    //    #[ORM\Column]
    //    private ?int $templateSystem_id;
    //
    //    #[ORM\Column]
    //    private ?int $work_id;

    /** @var Collection<int, Person> */
    #[ORM\ManyToMany(targetEntity: Person::class, inversedBy: 'personFiles')]
    #[Groups(['file_person'])]
    private Collection $accessPersons;

    /** @var Collection<int, Person> */
    #[ORM\OneToMany(targetEntity: Person::class, mappedBy: 'image', orphanRemoval: true)]
    private Collection $personImages;

    /** @var Collection<int, Organization> */
    #[ORM\OneToMany(targetEntity: Organization::class, mappedBy: 'logo', orphanRemoval: true)]
    private Collection $organizationLogos;

    /** @var Collection<int, Organization> */
    #[ORM\OneToMany(targetEntity: Organization::class, mappedBy: 'image', orphanRemoval: true)]
    private Collection $organizationImages;

    #[ORM\OneToOne(targetEntity: Parameters::class, mappedBy: 'qrCode', fetch: 'EAGER')]
    private ?Parameters $qrCode = null;

    /** @var Collection<int, Event> */
    #[ORM\OneToMany(targetEntity: Event::class, mappedBy: 'image')]
    private Collection $events;

    /** @var Collection<int, Activity> */
    #[ORM\OneToMany(targetEntity: Activity::class, mappedBy: 'logo')]
    private Collection $activityLogos;

    /** @var Collection<int, Activity> */
    #[ORM\OneToMany(targetEntity: Activity::class, mappedBy: 'imageActivity')]
    private Collection $activityImages;

    #[ORM\ManyToOne(inversedBy: 'files')]
    #[ORM\JoinColumn(onDelete: 'CASCADE')]
    private ?EventReport $eventReport = null;

    #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'files')]
    #[ORM\JoinColumn(onDelete: 'CASCADE')]
    private ?DocumentWish $documentWish = null;

    #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'introductionFiles')]
    #[ORM\JoinColumn(onDelete: 'CASCADE')]
    private ?OnlineRegistrationSettings $onlineRegistrationSetting = null;

    #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'introductionFilesNewEnrolments')]
    #[ORM\JoinColumn(onDelete: 'CASCADE')]
    private ?OnlineRegistrationSettings $onlineRegistrationSettingNewEnrolments = null;

    #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'files')]
    #[ORM\JoinColumn(onDelete: 'CASCADE')]
    private ?Work $work = null;

    #[ORM\ManyToOne(cascade: ['persist'], inversedBy: 'files')]
    #[ORM\JoinColumn(onDelete: 'CASCADE')]
    private ?TemplateSystem $templateSystem = null;

    /** @var Collection<int, Network> */
    #[ORM\OneToMany(targetEntity: Network::class, mappedBy: 'image')]
    private Collection $networks;

    #[ORM\OneToOne(targetEntity: CalendarSynchro::class, mappedBy: 'file')]
    private ?CalendarSynchro $calendarSynchro;

    #[ORM\OneToOne(targetEntity: SddRegie::class, mappedBy: 'file')]
    private ?SddRegie $sddRegieFile = null;

    #[ORM\OneToOne(targetEntity: SddRegie::class, mappedBy: 'bordereau')]
    private ?SddRegie $sddRegieBordereau = null;

    #[Pure]
    public function __construct()
    {
        $this->personImages = new ArrayCollection();
        $this->organizationLogos = new ArrayCollection();
        $this->organizationImages = new ArrayCollection();
        $this->accessPersons = new ArrayCollection();
        $this->events = new ArrayCollection();
        $this->activityLogos = new ArrayCollection();
        $this->activityImages = new ArrayCollection();
        $this->networks = new ArrayCollection();
    }

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

    public function getPerson(): ?Person
    {
        return $this->person;
    }

    public function setPerson(?Person $person): self
    {
        $this->person = $person;

        return $this;
    }

    public function getOrganization(): ?Organization
    {
        return $this->organization;
    }

    public function setOrganization(?Organization $organization): self
    {
        $this->organization = $organization;

        return $this;
    }

    public function getSlug(): ?string
    {
        return $this->slug;
    }

    public function setSlug(?string $slug): self
    {
        $this->slug = $slug;

        return $this;
    }

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

    public function setPath(?string $path): self
    {
        $this->path = $path;

        return $this;
    }

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

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

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

    public function setMimeType(?string $mimeType): self
    {
        $this->mimeType = $mimeType;

        return $this;
    }

    public function getConfig(): ?string
    {
        return $this->config;
    }

    public function setConfig(?string $config): self
    {
        $this->config = $config;

        return $this;
    }

    public function getPersonImages(): Collection
    {
        return $this->personImages;
    }

    public function addPersonImage(Person $person): self
    {
        if (!$this->personImages->contains($person)) {
            $this->personImages[] = $person;
            $person->setImage($this);
        }

        return $this;
    }

    public function removePersonImage(Person $person): self
    {
        // set the owning side to null (unless already changed)
        if ($this->personImages->removeElement($person) && $person->getImage() === $this) {
            $person->setImage(null);
        }

        return $this;
    }

    /**
     * TODO: voir si utilisé?
     */
    public function getAccessRoles(): Collection
    {
        return new ArrayCollection([]);
    }

    public function getVisibility(): FileVisibilityEnum
    {
        return $this->visibility;
    }

    public function setVisibility(FileVisibilityEnum $visibility): self
    {
        $this->visibility = $visibility;

        return $this;
    }

    public function getType(): FileTypeEnum
    {
        return $this->type;
    }

    public function setType(FileTypeEnum $type): self
    {
        $this->type = $type;

        return $this;
    }

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

    public function setSize(?int $size): self
    {
        $this->size = $size;

        return $this;
    }

    public function getIsTemporaryFile(): bool
    {
        return $this->isTemporaryFile;
    }

    public function setIsTemporaryFile(bool $isTemporaryFile): self
    {
        $this->isTemporaryFile = $isTemporaryFile;

        return $this;
    }

    public function getCreateDate(): \DateTime
    {
        return $this->createDate;
    }

    public function setCreateDate(\DateTime $createDate): self
    {
        $this->createDate = $createDate;

        return $this;
    }

    public function getCreatedBy(): ?int
    {
        return $this->createdBy;
    }

    public function setCreatedBy(?int $createdBy): self
    {
        $this->createdBy = $createdBy;

        return $this;
    }

    public function getUpdateDate(): \DateTime
    {
        return $this->updateDate;
    }

    public function setUpdateDate(\DateTime $updateDate): self
    {
        $this->updateDate = $updateDate;

        return $this;
    }

    public function getUpdatedBy(): ?int
    {
        return $this->updatedBy;
    }

    public function setUpdatedBy(?int $updatedBy): self
    {
        $this->updatedBy = $updatedBy;

        return $this;
    }

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

    public function setStatus(?FileStatusEnum $status): self
    {
        $this->status = $status;

        return $this;
    }

    public function getHost(): ?FileHostEnum
    {
        return $this->host;
    }

    public function setHost(?FileHostEnum $host): self
    {
        $this->host = $host;

        return $this;
    }

    public function getOrganizationLogos(): Collection
    {
        return $this->organizationLogos;
    }

    public function addOrganizationLogo(Organization $organization): self
    {
        if (!$this->organizationLogos->contains($organization)) {
            $this->organizationLogos[] = $organization;
            $organization->setLogo($this);
        }

        return $this;
    }

    public function removeOrganizationLogo(Organization $organization): self
    {
        // set the owning side to null (unless already changed)
        if ($this->organizationLogos->removeElement($organization) && $organization->getLogo() === $this) {
            $organization->setLogo(null);
        }

        return $this;
    }

    public function getOrganizationImages(): Collection
    {
        return $this->organizationImages;
    }

    public function addOrganizationImage(Organization $organization): self
    {
        if (!$this->organizationImages->contains($organization)) {
            $this->organizationImages[] = $organization;
            $organization->setImage($this);
        }

        return $this;
    }

    public function removeOrganizationImage(Organization $organization): self
    {
        // set the owning side to null (unless already changed)
        if ($this->organizationImages->removeElement($organization) && $organization->getImage() === $this) {
            $organization->setImage(null);
        }

        return $this;
    }

    public function getQrCode(): ?Parameters
    {
        return $this->qrCode;
    }

    public function setQrCode(?Parameters $qrCode): self
    {
        $this->qrCode = $qrCode;

        return $this;
    }

    public function getAvailabilityDate(): ?\DateTime
    {
        return $this->availabilityDate;
    }

    public function setAvailabilityDate(?\DateTime $availabilityDate): void
    {
        $this->availabilityDate = $availabilityDate;
    }

    /**
     * @return Collection<int, Person>
     */
    public function getAccessPersons(): Collection
    {
        return $this->accessPersons;
    }

    public function addAccessPerson(Person $accessPerson): self
    {
        if (!$this->accessPersons->contains($accessPerson)) {
            $this->accessPersons[] = $accessPerson;
        }

        return $this;
    }

    public function removeAccessPerson(Person $accessPerson): self
    {
        $this->accessPersons->removeElement($accessPerson);

        return $this;
    }

    /**
     * @return Collection<int, Event>
     */
    public function getEvents(): Collection
    {
        return $this->events;
    }

    public function addEvent(Event $event): self
    {
        if (!$this->events->contains($event)) {
            $this->events[] = $event;
            $event->setImage($this);
        }

        return $this;
    }

    public function removeEvent(Event $event): self
    {
        // set the owning side to null (unless already changed)
        if ($this->events->removeElement($event) && $event->getImage() === $this) {
            $event->setImage(null);
        }

        return $this;
    }

    /**
     * @return Collection<int, Activity>
     */
    public function getActivityLogos(): Collection
    {
        return $this->activityLogos;
    }

    public function addActivityLogo(Activity $activityLogo): self
    {
        if (!$this->activityLogos->contains($activityLogo)) {
            $this->activityLogos[] = $activityLogo;
            $activityLogo->setLogo($this);
        }

        return $this;
    }

    public function removeActivityLogo(Activity $activityLogo): self
    {
        // set the owning side to null (unless already changed)
        if ($this->activityLogos->removeElement($activityLogo) && $activityLogo->getLogo() === $this) {
            $activityLogo->setLogo(null);
        }

        return $this;
    }

    /**
     * @return Collection<int, Activity>
     */
    public function getActivityImages(): Collection
    {
        return $this->activityImages;
    }

    public function addActivityImage(Activity $activityImage): self
    {
        if (!$this->activityImages->contains($activityImage)) {
            $this->activityImages[] = $activityImage;
            $activityImage->setImageActivity($this);
        }

        return $this;
    }

    public function removeActivityImage(Activity $activityImage): self
    {
        // set the owning side to null (unless already changed)
        if ($this->activityImages->removeElement($activityImage) && $activityImage->getImageActivity() === $this) {
            $activityImage->setImageActivity(null);
        }

        return $this;
    }

    public function getEventReport(): ?EventReport
    {
        return $this->eventReport;
    }

    public function setEventReport(?EventReport $eventReport): self
    {
        $this->eventReport = $eventReport;

        return $this;
    }

    public function getDocumentWish(): ?DocumentWish
    {
        return $this->documentWish;
    }

    public function setDocumentWish(?DocumentWish $documentWish): self
    {
        $this->documentWish = $documentWish;

        return $this;
    }

    public function getOnlineRegistrationSetting(): ?OnlineRegistrationSettings
    {
        return $this->onlineRegistrationSetting;
    }

    public function setOnlineRegistrationSetting(?OnlineRegistrationSettings $onlineRegistrationSetting): self
    {
        $this->onlineRegistrationSetting = $onlineRegistrationSetting;

        return $this;
    }

    public function getOnlineRegistrationSettingNewEnrolments(): ?OnlineRegistrationSettings
    {
        return $this->onlineRegistrationSettingNewEnrolments;
    }

    public function setOnlineRegistrationSettingNewEnrolments(?OnlineRegistrationSettings $onlineRegistrationSettingNewEnrolments): self
    {
        $this->onlineRegistrationSettingNewEnrolments = $onlineRegistrationSettingNewEnrolments;

        return $this;
    }

    public function getWork(): ?Work
    {
        return $this->work;
    }

    public function setWork(?Work $work): self
    {
        $this->work = $work;

        return $this;
    }

    public function getTemplateSystem(): ?TemplateSystem
    {
        return $this->templateSystem;
    }

    public function setTemplateSystem(?TemplateSystem $templateSystem): self
    {
        $this->templateSystem = $templateSystem;

        return $this;
    }

    public function getNetworks(): Collection
    {
        return $this->networks;
    }

    public function addNetwork(Network $network): self
    {
        if (!$this->networks->contains($network)) {
            $this->networks[] = $network;
            $network->setImage($this);
        }

        return $this;
    }

    public function removeNetwork(Network $network): self
    {
        if ($this->networks->removeElement($network)) {
            //            $network->setImage(null);  // TODO: actuellement, pas nullable: conserver?
        }

        return $this;
    }

    public function getCalendarSynchro(): ?CalendarSynchro
    {
        return $this->calendarSynchro;
    }

    public function setCalendarSynchro(?CalendarSynchro $calendarSynchro): self
    {
        $this->calendarSynchro = $calendarSynchro;

        return $this;
    }

    public function getSddRegieFile(): ?SddRegie
    {
        return $this->sddRegieFile;
    }

    public function setSddRegieFile(?SddRegie $sddRegieFile): self
    {
        $this->sddRegieFile = $sddRegieFile;

        return $this;
    }

    public function getSddRegieBordereau(): ?SddRegie
    {
        return $this->sddRegieBordereau;
    }

    public function setSddRegieBordereau(?SddRegie $sddRegieBordereau): self
    {
        $this->sddRegieBordereau = $sddRegieBordereau;

        return $this;
    }
}
