<?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\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Ferienpass\CoreBundle\Entity\Offer\OfferInterface;
use Ferienpass\CoreBundle\Repository\HostRepository;
use Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\String\Slugger\SluggerInterface;
use Symfony\Component\Uid\Uuid;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\Entity(repositoryClass: HostRepository::class)]
#[UniqueEntity('alias')]
class Host
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer', options: ['unsigned' => true])]
    #[Groups(['list:export:admin', 'list:export:default'])]
    private ?int $id = null;

    #[ORM\Column(type: UuidType::NAME)]
    private Uuid $uuid;

    #[ORM\Column(type: 'datetime_immutable', options: ['default' => 'CURRENT_TIMESTAMP'])]
    #[Groups(['list:export:admin', 'list:export:default'])]
    private \DateTimeInterface $createdAt;

    #[ORM\Column(type: 'string', length: 255, nullable: false, options: ['default' => ''])]
    #[Assert\NotBlank(message: 'notBlank')]
    #[Groups(['list:export:admin', 'list:export:default', 'api:offer:anon'])]
    private ?string $name = null;

    #[ORM\Column(type: 'string', length: 255, unique: true)]
    private ?string $alias = null;

    #[ORM\Column(type: 'string', length: 64, nullable: true)]
    #[PhoneNumber(defaultRegion: 'DE')]
    #[Groups(['list:export:admin', 'list:export:default'])]
    private ?string $phone = null;

    #[ORM\Column(type: 'string', length: 64, nullable: true)]
    #[PhoneNumber(defaultRegion: 'DE')]
    #[Groups(['list:export:admin', 'list:export:default'])]
    private ?string $fax = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    #[PhoneNumber(type: PhoneNumber::MOBILE, defaultRegion: 'DE')]
    #[Groups(['list:export:admin', 'list:export:default'])]
    private ?string $mobile = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    #[Assert\Email]
    #[Groups(['list:export:admin', 'list:export:default'])]
    private ?string $email = null;

    #[ORM\Column(type: 'string', length: 255, nullable: true)]
    #[Assert\Url]
    #[Groups(['list:export:admin', 'list:export:default'])]
    private ?string $website = null;

    #[ORM\JoinTable(name: 'HostAddresses')]
    #[ORM\JoinColumn(name: 'host_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
    #[ORM\InverseJoinColumn(name: 'address_id', referencedColumnName: 'id', unique: true)]
    #[ORM\ManyToMany(targetEntity: PostalAddress::class, cascade: ['persist'])]
    private Collection $postalAddresses;

    #[ORM\Column(type: 'text', nullable: true)]
    #[Groups(['list:export:admin', 'list:export:default'])]
    private ?string $text = null;

    #[ORM\Column(name: 'logo', type: 'binary_string', length: 16, nullable: true)]
    private ?string $logoOld = null;

    #[ORM\ManyToOne(targetEntity: DbafsLogo::class, inversedBy: 'hosts')]
    #[ORM\JoinColumn(name: 'logo_id', referencedColumnName: 'uuid')]
    private ?DbafsLogo $logo = null;

    #[ORM\Column(type: 'string', length: 1, nullable: true)]
    private ?string $active = null;

    #[ORM\OneToMany(mappedBy: 'host', targetEntity: HostMemberAssociation::class, cascade: ['persist', 'remove'])]
    private Collection $memberAssociations;

    // No join column here: this is the inverse side, the join table is defined on
    // Offer::$hosts. A JoinColumn on an inverse side is dead metadata and doctrine/orm
    // rejects it outright from 3.0 on.
    #[ORM\ManyToMany(targetEntity: OfferInterface::class, mappedBy: 'hosts', fetch: 'EXTRA_LAZY')]
    private Collection $offers;

    #[ORM\OneToMany(mappedBy: 'host', targetEntity: HostActivity::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
    #[ORM\OrderBy(['createdAt' => 'DESC'])]
    private Collection $activity;

    #[ORM\OneToMany(mappedBy: 'host', targetEntity: ConversationItem::class, fetch: 'EXTRA_LAZY')]
    #[ORM\OrderBy(['createdAt' => 'ASC'])]
    private Collection $conversation;

    public function __construct()
    {
        $this->uuid = Uuid::v7();

        $this->postalAddresses = new ArrayCollection();
        $this->memberAssociations = new ArrayCollection();
        $this->offers = new ArrayCollection();
        $this->activity = new ArrayCollection();
        $this->conversation = new ArrayCollection();
        $this->createdAt = new \DateTimeImmutable();
    }

    public function hasMember(User $user): bool
    {
        return !$this->memberAssociations
            ->matching(Criteria::create()->where(Criteria::expr()->eq('user', $user)))
            ->isEmpty();
    }

    public function addMember(User $user): self
    {
        if ($this->hasMember($user)) {
            return $this;
        }

        return $this->addMemberAssociation(new HostMemberAssociation($user, $this));
    }

    public function addMemberAssociation(HostMemberAssociation $memberAssociation): self
    {
        $this->memberAssociations[] = $memberAssociation;

        return $this;
    }

    public function removeMemberAssociation(HostMemberAssociation $memberAssociation): void
    {
        $this->memberAssociations->removeElement($memberAssociation);
    }

    public function getMemberAssociations(): Collection
    {
        return $this->memberAssociations;
    }

    /**
     * @return User[]
     */
    public function getUsers(): array
    {
        return array_filter($this->memberAssociations->map(static fn (HostMemberAssociation $a) => $a->getUser())->toArray());
    }

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

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

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

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

    public function getAlias(): ?string
    {
        return $this->alias;
    }

    public function getPhone(): ?string
    {
        return $this->phone;
    }

    public function getFax(): ?string
    {
        return $this->fax;
    }

    public function getMobile(): ?string
    {
        return $this->mobile;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function getWebsite(): ?string
    {
        return $this->website;
    }

    public function getPostalAddresses(): Collection
    {
        return $this->postalAddresses;
    }

    public function addPostalAddress(PostalAddress $address): void
    {
        $this->postalAddresses->add($address);
    }

    public function removePostalAddress(PostalAddress $address): void
    {
        $this->postalAddresses->removeElement($address);
    }

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

    public function getLogoOld(): ?string
    {
        return $this->logoOld;
    }

    public function getLogo(): ?DbafsLogo
    {
        return $this->logo;
    }

    public function setLogo(?DbafsLogo $logo): void
    {
        $this->logo = $logo;
    }

    public function getActive(): ?string
    {
        return $this->active;
    }

    public function setName(?string $name): void
    {
        $this->name = $name;
    }

    public function setAlias(?string $alias): void
    {
        $this->alias = $alias;
    }

    public function setPhone(?string $phone): void
    {
        $this->phone = $phone;
    }

    public function setFax(?string $fax): void
    {
        $this->fax = $fax;
    }

    public function setMobile(?string $mobile): void
    {
        $this->mobile = $mobile;
    }

    public function setEmail(?string $email): void
    {
        $this->email = $email;
    }

    public function setWebsite(?string $website): void
    {
        $this->website = $website;
    }

    public function setText(?string $text): void
    {
        $this->text = $text;
    }

    public function setLogoOld(?string $logoOld): void
    {
        $this->logoOld = $logoOld;
    }

    public function setActive(?string $active): void
    {
        $this->active = $active;
    }

    public function getOffers(): Collection
    {
        return $this->offers;
    }

    public function addOffer(OfferInterface $offer): void
    {
        $this->offers->add($offer);
    }

    public function getActivity(): Collection
    {
        return $this->activity;
    }

    public function getConversation(): Collection
    {
        return $this->conversation;
    }

    public function generateAlias(SluggerInterface $slugger): void
    {
        $this->alias = (string) $slugger->slug($this->getName() ?? uniqid())->lower();
    }
}
