<?php

namespace App\Entity;

use App\Utils\ConvertHelper;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\OrderBy;
use App\Repository\EventRepository;
use App\Entity\Interface\ICachableImage;
use App\Entity\Trait\TimestampableTrait;
use App\Entity\Trait\ForceUpdatableTrait;
use App\Entity\Interface\CachedImageColumn;
use Doctrine\Common\Collections\Collection;
use App\Entity\Interface\IVideoCachableImage;
use App\Entity\Interface\CachedImageDimensions;
use Symfony\Component\HttpFoundation\File\File;
use App\Entity\Interface\CachedVideoImageColumn;
use Doctrine\Common\Collections\ArrayCollection;
use App\Entity\Listener\ImageCacheEntityListener;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

#[ORM\Table(name: 'event')]
#[ORM\Entity(repositoryClass: EventRepository::class)]
#[ORM\HasLifecycleCallbacks]
#[Vich\Uploadable]
class Event implements ICachableImage, IVideoCachableImage
{
    use TimestampableTrait;
    use ForceUpdatableTrait;

    const t_root = "admin.event";

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

    #[ORM\ManyToOne]
    #[ORM\JoinColumn(name: 'type_id', nullable: false)]
    private ?TypeEvent $type = null;

    #[ORM\Column(length: 255)]
    #[Assert\Length(min: 5, minMessage: self::t_root . '.fields.@title.errors.min-length')] // "Le nom du partenaire doit faire au moins 5 caractères")]
    #[Assert\Length(max: 150, maxMessage: self::t_root . '.fields.@title.errors.max-length')]
    private ?string $title = null;

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

    // NOTE: This is not a mapped field of entity metadata, just a simple property.
    #[Vich\UploadableField(mapping: 'events', fileNameProperty: 'imgPresentationFileName')] //, size: 'logoSize')]
    private ?File $imgPresentationFile = null;

    #[Assert\Url(protocols: ['https'], message: self::t_root . '.fields.@video-presentation.errors.url-invalid')]
    #[ORM\Column(name: "video_presentation", length: 255, nullable: true)]
    private ?string $videoPresentation = null;

    #[ORM\Column(length: 255)]
    private ?string $subject = null;

    #[ORM\OneToMany(mappedBy: 'event', cascade: ['persist', 'remove'], targetEntity: EventPerformer::class, orphanRemoval: true)]
    #[OrderBy(["ordre" => "ASC"])]
    #[Assert\Count(min: 1, minMessage: self::t_root . '.fields.@performers.errors.min-count')]
    #[Assert\Valid()]
    private Collection $performers;

    #[ORM\OneToMany(mappedBy: 'event', cascade: ['persist', 'remove'], targetEntity: EventDate::class)]
    #[OrderBy(["date" => "ASC"])]
    #[Assert\Valid()]
    #[Assert\Count(min: 1, minMessage: self::t_root . '.fields.@dates.errors.min-count')]
    private Collection $dates;

    #[ORM\OneToMany(mappedBy: 'event', cascade: ['persist', 'remove'], targetEntity: EventMedia::class, orphanRemoval: true)]
    #[Assert\Valid()]
    private Collection $medias;

    #[ORM\OneToMany(mappedBy: 'event', cascade: ['persist', 'remove'], targetEntity: EventSponsor::class, orphanRemoval: true)]
    #[Assert\Valid()]
    #[Assert\Count(min: 1, minMessage: self::t_root . '.fields.@sponsors.errors.min-count')]
    private Collection $sponsors;

    #[ORM\Column(options: ["default" => true])]
    private ?bool $active = null;

    #[ORM\Column(type: 'event_status_enum')]
    private ?EventStatus $status = EventStatus::UNKNOWN;

    #[ORM\Column(options: ["default" => false])]
    private ?bool $force_no_media = null;

    #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
    private ?\DateTime $first_date = null;

    #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
    private ?\DateTime $last_date = null;

    public function __construct()
    {
        $this->performers = new ArrayCollection();
        $this->dates = new ArrayCollection();
        $this->medias = new ArrayCollection();
        $this->sponsors = new ArrayCollection();
    }

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

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

    public function setType(?TypeEvent $type): static
    {
        $this->type = $type;

        return $this;
    }

    public function getTitle(): ?string
    {
        return $this->title;
    }

    public function setTitle(string $title): static
    {
        $this->title = $title;

        return $this;
    }

    public function getImgPresentationFileName(): ?string
    {
        return $this->imgPresentationFileName;
    }

    public function setImgPresentationFileName(?string $imgPresentationFileName): static
    {
        $this->imgPresentationFileName = $imgPresentationFileName;

        return $this;
    }
   

    /**
     * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
     * of 'UploadedFile' is injected into this setter to trigger the update. If this
     * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
     * must be able to accept an instance of 'File' as the bundle will inject one here
     * during Doctrine hydration.
     *
     * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile|null $imageFile
     */
    public function setImgPresentationFile(?File $imgPresentationFile = null): static
    {
        $this->imgPresentationFile = $imgPresentationFile;

        if (null !== $imgPresentationFile) {
            // It is required that at least one field changes if you are using doctrine
            // otherwise the event listeners won't be called and the file is lost
            $this->updatedAt = new \DateTime();
            // $this->imgPresentationCached = [];
        }

        return $this;
    }

    public function getImgPresentationFile(): ?File
    {
        return $this->imgPresentationFile;
    }
    
    public function getCachedImages() : array {

        $col = new CachedImageColumn();
        $col->field_name = "imgPresentationFile";
        $col->filters = [ "home_event_filter_jpeg", "home_event_filter_webp", "admin_event_list_filter", "calendar_event_filter_jpeg", "calendar_event_filter_webp" ];

        // home_event_filter_jpeg : fixed / 1300 x 900
        // home_event_filter_webp : fixed / 1300 x 900
        // admin_event_list_filter : fixed / 400 x 280

        $col->max_dimensions = new CachedImageDimensions();
        $col->max_dimensions->min_width = 1300;
        $col->max_dimensions->min_height = 900;

        return [ "imgPresentationFile" => $col ];
    }


    public function getVideoPresentation(): ?string
    {
        return $this->videoPresentation;
    }

    public function setVideoPresentation(?string $videoPresentation): static
    {
        $this->videoPresentation = $videoPresentation;
        $this->imgPresentationFileName = null;

        return $this;
    }

    public function getCachedVideoImages() : array {

        $col = new CachedVideoImageColumn();
        $col->field_name = "videoPresentation";
        $col->field_imageFile = "imgPresentationFile";
        $col->field_imageFileName = "imgPresentationFileName";

        return [ "videoPresentation" => $col ];
    }

    public function getSubject(): ?string
    {
        return $this->subject;
    }

    public function setSubject(string $subject): static
    {
        $this->subject = $subject;

        return $this;
    }

    /**
     * @return Collection<int, EventPerformer>
     */
    public function getPerformers(): Collection
    {
        return $this->performers;
    }

    public function getPerformersJSON(): string
    {
        $res = ConvertHelper::coll2json($this->performers);
        return $res;
    }

    public function addPerformer(EventPerformer $performer): static
    {
        if (!$this->performers->contains($performer)) {
            $this->performers->add($performer);
            $performer->setEvent($this);
        }

        return $this;
    }

    public function removePerformer(EventPerformer $performer): static
    {
        if ($this->performers->removeElement($performer)) {
            // set the owning side to null (unless already changed)
            if ($performer->getEvent() === $this) {
                $performer->setEvent(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection<int, EventDate>
     */
    public function getDates(): Collection
    {
        return $this->dates;
    }

    public function getDatesJSON(): string
    {
        return ConvertHelper::coll2json($this->dates);
    }

    public function addDate(EventDate $date): static
    {
        if (!$this->dates->contains($date)) {
            $this->dates->add($date);
            $date->setEvent($this);

            $this->refreshFirstAndLastDates();
        }

        return $this;
    }

    public function refreshFirstAndLastDates() {
        if ($this->getDates()->count() > 0) {
            // dump($this->getDates());

            $last_date = new \DateTime('2000-01-01');
            $first_date = new \DateTime('2050-01-01');
    
            foreach ($this->getDates() as $d) {
                if ($d->getDate() > $last_date) {
                    $last_date = $d->getDate();
                }

                if ($d->getDate() < $first_date) {
                    $first_date = $d->getDate();
                }
            }

            // $this->first_date = $first_date;
            // $this->last_date  = $last_date;

            $this->setFirstDate($first_date);
            $this->setLastDate($last_date);

            // dump($this->first_date);
            // dump($this->last_date);

            // dd("xxxx");
        } else {
            $this->setFirstDate(null);
            $this->setLastDate(null);
        }
    }

    public function removeDate(EventDate $date): static
    {
        if ($this->dates->removeElement($date)) {
            // set the owning side to null (unless already changed)
            if ($date->getEvent() === $this) {
                $date->setEvent(null);
            }
        }

        $this->refreshFirstAndLastDates();

        return $this;
    }

    /**
     * @return Collection<int, EventMedia>
     */
    public function getMedias(): Collection
    {
        return $this->medias;
    }

    public function addMedia(EventMedia $media): static
    {
        if (!$this->medias->contains($media)) {
            $this->medias->add($media);
            $media->setEvent($this);
        }

        return $this;
    }

    public function removeMedia(EventMedia $media): static
    {
        if ($this->medias->removeElement($media)) {
            // set the owning side to null (unless already changed)
            if ($media->getEvent() === $this) {
                $media->setEvent(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection<int, EventSponsor>
     */
    public function getSponsors(): Collection
    {
        return $this->sponsors;
    }

    public function getSponsorsJSON(): string
    {
        return ConvertHelper::coll2json($this->sponsors);
    }

    public function addSponsor(EventSponsor $sponsor): static
    {
        if (!$this->sponsors->contains($sponsor)) {
            $this->sponsors->add($sponsor);
            $sponsor->setEvent($this);
        }

        return $this;
    }

    public function removeSponsor(EventSponsor $sponsor): static
    {
        if ($this->sponsors->removeElement($sponsor)) {
            // set the owning side to null (unless already changed)
            if ($sponsor->getEvent() === $this) {
                $sponsor->setEvent(null);
            }
        }

        return $this;
    }

    public function getMainSponsor(): ?EventSponsor
    {
        foreach ($this->getSponsors() as $sponsor) {
            if ($sponsor->isMain()) return $sponsor;
        }
        return null;
    }

    public function isActive(): ?bool
    {
        return $this->active;
    }

    public function setActive(bool $active): static
    {
        $this->active = $active;

        return $this;
    }

    public function __serialize() : array
    {
        return [
            "id"           =>           $this->id,
            "title"        =>           $this->title,
            "type"         => [ "id" => $this->type->getId() ],
            "active"       =>           $this->active,
            "performers"   =>           serialize($this->getPerformers()->toArray()),
            "sponsors"     =>           serialize($this->getSponsors()->toArray())
        ];
    }

    public function __unserialize(array $data)
    {
        $this->id       = $data["id"];
        $this->title    = $data["title"];
        $this->type     = null; // ignore  
        $this->active   = $data["active"];        
        $this->performers = unserialize($data["performers"]);           
        $this->sponsors   = unserialize($data["sponsors"]);           
    }

    public function __toString(): string
    {
        return "".$this->getTitle();
    }

    public function getStatus(): ?EventStatus
    {
        $computed_status = EventStatus::UNKNOWN;
        
        foreach ($this->getDates() as $d) {
            // dump($d);
            $computed_status = EventStatus::combineWithDateStatus($computed_status, $d->getStatus());
        }

        return $computed_status; 
    }

    public function setStatus(EventStatus|string $status): static
    {
        if ($status instanceof EventStatus) {
            $this->status = $status;
        } else {
            $this->status = EventStatus::from($status);
        }
        return $this;
    }

    #[ORM\PrePersist]
    #[ORM\PreUpdate]
    public function preUpdateStatus(): void
    {
        $this->status = $this->getStatus();
        // dump("preUpdateStatus = " . $this->status->value);
    }

    #[ORM\PrePersist]
    #[ORM\PreUpdate]
    public function preUpdateFirstDate(): void
    {
        $this->refreshFirstAndLastDates();
        dump("preUpdateFirstDate");
    }

    public function isForceNoMedia(): ?bool
    {
        return $this->force_no_media;
    }

    public function setForceNoMedia(bool $force_no_media): static
    {
        $this->force_no_media = $force_no_media;

        return $this;
    }

    #[ORM\PrePersist]
    public function prePersistForceNoMedia(): void
    {
        if ($this->isForceNoMedia() == null) {
            $this->setForceNoMedia(false);
        }
    }
    public function getRealRoleForUser(User $user): string
    {
        if (in_array("ROLE_ADMIN", $user->getRoles())) return "ROLE_EDITOR";
        foreach ($this->getSponsors() as $sponsor) {
            $read_role = $sponsor->getSponsor()->getRealRoleForUser($user);
            if ($read_role == "ROLE_EDITOR") return $read_role;
        }
        return "ROLE_USER";
    }


    #[Assert\Callback]
    public function validate(ExecutionContextInterface $context, $payload)
    {
        if ( !$this->imgPresentationFileName && !$this->imgPresentationFile && !$this->videoPresentation ) {
            $context->buildViolation(self::t_root . ".validate.errors.image-and-video-missing")
                ->addViolation();
        }
    }

    private function setFirstDate(\DateTime|null $first_date): static
    {
        $this->first_date = $first_date;
        return $this;
    }

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

    private function setLastDate(\DateTime|null $last_date): static
    {
        $this->last_date = $last_date;
        return $this;
    }

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

}
