<?php

declare(strict_types=1);

namespace App\Entity\Message;

use App\Attribute\OrganizationDefaultValue;
use App\Entity\Access\Access;
use App\Entity\Core\File;
use App\Entity\Core\Tagg;
use App\Entity\Organization\Organization;
use App\Enum\Message\MessageStatusEnum;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Doctrine\UuidGenerator;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

/**
 * Classe de base de @see Mail, Sms, Email.
 */
#[ORM\Entity]
#[OrganizationDefaultValue(fieldName: 'organization')]
#[ORM\Table(name: 'Message')]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorColumn(name: 'discr', type: 'string')]
#[ORM\DiscriminatorMap(
    [
        'sms' => Sms::class,
        'mail' => Mail::class,
        'email' => Email::class,
    ]
)]
abstract class AbstractMessage
{
    /**
     * IMPORTANT! Cette propriété doit être déclarée avant l'id, pour prévenir un bug doctrine.
     *
     * @see https://github.com/doctrine/orm/issues/7215
     */
    #[ORM\Column(type: 'uuid', unique: true)]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\CustomIdGenerator(class: UuidGenerator::class)]
    protected ?UuidInterface $uuid = null;

    #[ORM\Id]
    #[ORM\Column]
    #[ORM\GeneratedValue]
    protected ?int $id = null;

    #[ORM\ManyToOne(inversedBy: 'emails')]
    #[ORM\JoinColumn(nullable: true)]
    private ?Access $author = null;

    #[ORM\ManyToOne]
    #[ORM\JoinColumn(nullable: true)]
    protected ?Organization $organization = null;

    #[ORM\Column(length: 50, enumType: MessageStatusEnum::class, options: ['default' => 'DRAFT'])]
    protected MessageStatusEnum $status = MessageStatusEnum::DRAFT;

    // @todo remplacer par le nom sendingDate
    #[ORM\Column(type: 'datetime', nullable: true)]
    protected ?\DateTimeInterface $dateSent = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    protected ?string $about = null;

    #[ORM\Column(type: 'text', nullable: true)]
    protected ?string $text = null;

    /** @var Collection<int, File> */
    #[ORM\ManyToMany(targetEntity: File::class, cascade: ['persist'], orphanRemoval: true)]
    #[ORM\JoinTable(name: 'messages_files')]
    #[ORM\JoinColumn(name: 'message_id', referencedColumnName: 'id', onDelete: 'cascade')]
    #[ORM\InverseJoinColumn(name: 'file_id', referencedColumnName: 'id', onDelete: 'cascade')]
    protected Collection $files;

    /** @var Collection<int, ReportMessage> */
    #[ORM\OneToMany(targetEntity: ReportMessage::class, mappedBy: 'message', cascade: ['persist'], orphanRemoval: true)]
    protected Collection $reportMessage;

    /** @var Collection<int, Tagg> */
    #[ORM\ManyToMany(targetEntity: Tagg::class, inversedBy: 'messages', cascade: ['persist'])]
    #[ORM\JoinTable(name: 'tag_message')]
    #[ORM\JoinColumn(name: 'message_id', referencedColumnName: 'id', onDelete: 'cascade')]
    #[ORM\InverseJoinColumn(name: 'tag_id', referencedColumnName: 'id', onDelete: 'cascade')]
    protected Collection $tags;

    public function __construct()
    {
        $this->uuid = Uuid::uuid4();
        $this->files = new ArrayCollection();
        $this->tags = new ArrayCollection();
        $this->reportMessage = new ArrayCollection();
    }

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

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

    public function getAuthor(): ?Access
    {
        return $this->author;
    }

    public function setAuthor(?Access $author): self
    {
        $this->author = $author;

        return $this;
    }

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

        return $this;
    }

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

    public function setAbout(string $about): self
    {
        $this->about = $about;

        return $this;
    }

    public function getAbout(): string
    {
        return $this->about;
    }

    public function setText(string $text): self
    {
        $this->text = $text;

        return $this;
    }

    public function getText(): string
    {
        return html_entity_decode($this->text);
    }

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

        return $this;
    }

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

    public function setDateSent(\DateTimeInterface $dateSent): self
    {
        $this->dateSent = $dateSent;

        return $this;
    }

    public function getDateSent(): ?\DateTimeInterface
    {
        return $this->dateSent;
    }

    /**
     * @return Collection<int, File>
     */
    public function getFiles(): Collection
    {
        return $this->files;
    }

    public function addFile(File $file): self
    {
        if (!$this->files->contains($file)) {
            $this->files[] = $file;
            //            $file->addXXXX($this);  // TODO: quel méthode?
        }

        return $this;
    }

    public function removeFile(File $file): self
    {
        if ($this->files->removeElement($file)) {
            //            $file->removeXXX($this);  // TODO: quel méthode?
        }

        return $this;
    }

    public function getReportMessage(): Collection
    {
        return $this->reportMessage;
    }

    public function addReportMessage(ReportMessage $reportMessage): self
    {
        if (!$this->reportMessage->contains($reportMessage)) {
            $this->reportMessage[] = $reportMessage;
            $reportMessage->setMessage($this);
        }

        return $this;
    }

    public function removeReportMessage(ReportMessage $reportMessage): self
    {
        if ($this->reportMessage->removeElement($reportMessage)) {
            $reportMessage->setMessage(null);
        }

        return $this;
    }

    public function getTags(): Collection
    {
        return $this->tags;
    }

    public function addTag(Tagg $tag): self
    {
        if (!$this->tags->contains($tag)) {
            $this->tags[] = $tag;
        }

        return $this;
    }

    public function removeTag(Tagg $tag): self
    {
        $this->tags->removeElement($tag);

        return $this;
    }
}
