<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\GroupInterface;
use FOS\UserBundle\Model\UserInterface;
use Ramsey\Uuid\Doctrine\UuidGenerator;
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * ApiResource(
 *     attributes={"security"="is_granted('ROLE_SONATA_USER_GERER_VIEW')"},
 *     collectionOperations={
 *         "get"={"security"="is_granted('ROLE_SONATA_USER_GERER_LIST')"},
 *         "post"={"security"="is_granted('ROLE_SONATA_USER_GERER_EDIT')"}
 *     },
 *     itemOperations={
 *         "get"={"security"="is_granted('ROLE_SONATA_USER_GERER_VIEW')"},
 *         "put"={"security"="is_granted('ROLE_SONATA_USER_GERER_EDIT')"},
 *     },
 *     normalizationContext={"groups"={"read"}},
 *     denormalizationContext={"groups"={"write"}}
 * ).
 *
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @ORM\Table(name="user")
 * @UniqueEntity(
 *     fields       =   "email",
 *     message      =   "Cet email est déjà utilisé !"
 * )
 */
class User extends BaseUser
{
    /**
     * @var \Ramsey\Uuid\UuidInterface
     *
     * @ORM\Id
     * @ORM\Column(type="uuid", unique=true)
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class=UuidGenerator::class)
     * @Groups({"user:read", "read"})
     */
    protected $id;

    /**
     * @Groups({"user"})
     */
    protected $email;

    /**
     * @Groups({"user:write"})
     */
    protected $plainPassword;

    /**
     * @Groups({"user"})
     */
    protected $username;

    /**
     * @var string|null
     *
     * @ORM\Column(name="etat", type="string", length=10, nullable=true)
     * @Groups({"user"})
     */
    protected $etat;

    /**
     * @ORM\Column(type="string", length=15, nullable=true)
     * @Groups({"user"})
     */
    protected $mobile;

    /**
     * @var ArrayCollection|Document[]
     *
     * @ORM\OneToMany(targetEntity="Document", mappedBy="user", cascade={"persist"})
     */
    private $documents;

    /**
     * @var ArrayCollection|Faq[]
     *
     * @ORM\OneToMany(targetEntity="Faq", mappedBy="user", cascade={"persist"})
     */
    private $faqs;

    /**
     * @var ArrayCollection|Import[]
     *
     * @ORM\OneToMany(targetEntity="Import", mappedBy="user", cascade={"persist"})
     */
    private $imports;

    /**
     * @ORM\OneToMany(targetEntity="Flux", mappedBy="operateur", cascade={"persist"})
     * @ORM\OrderBy({"createdAt" = "DESC"})
     */
    protected $flux;

    /**
     * @ORM\OneToMany(targetEntity="EmailToken", mappedBy="user", cascade={"persist", "remove"})
     * @ORM\OrderBy({"expiredAt" = "DESC"})
     */
    private $emailTokens;

    /**
     * @ORM\OneToOne(targetEntity="Adherent", inversedBy="user", cascade={"all"}, fetch="EXTRA_LAZY")
     * @ORM\JoinColumn(name="adherent_id", referencedColumnName="id", nullable=true)
     */
    protected $adherent;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Prestataire", mappedBy="users", cascade={"persist"}, fetch="EAGER")
     * @ORM\JoinTable(name="user_prestataire",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="prestataire_id", referencedColumnName="id")}
     * )
     */
    protected $prestataires;

    /**
     * @ORM\OneToMany(targetEntity=Caissier::class, mappedBy="user", orphanRemoval=true)
     */
    private $caissiers;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Groupe", mappedBy="gestionnaires", cascade={"persist"}, fetch="EAGER")
     * @ORM\JoinTable(name="user_groupe",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="groupe_id", referencedColumnName="id")}
     * )
     */
    private $groupesgeres;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Comptoir", mappedBy="gestionnaires", cascade={"persist"}, fetch="EAGER")
     * @ORM\JoinTable(name="user_comptoir",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="comptoir_id", referencedColumnName="id")}
     * )
     */
    private $comptoirsgeres;

    /**
     * @ORM\Column(name="apiKey", type="string", length=255, nullable=true)
     * @Groups({"user:read"})
     */
    private $apiKey;

    /**
     * @var ArrayCollection|News[]
     * @ORM\OneToMany(targetEntity="App\Entity\News", mappedBy="user", cascade={"persist"})
     */
    private $news;

    /**
     * @var ArrayCollection|Page[]
     * @ORM\OneToMany(targetEntity="App\Entity\Page", mappedBy="user", cascade={"persist"})
     */
    private $pages;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Usergroup", cascade={"persist"}, fetch="EAGER")
     * @ORM\JoinTable(name="user_usergroup",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
     * )
     */
    protected $groups;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Usergroup", cascade={"persist"}, fetch="EAGER")
     * @ORM\JoinTable(name="user_possiblegroup",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
     * )
     */
    protected $possiblegroups;

    /**
     * Alerte email à chaque transaction concernant l'utilisateur.
     *
     * @var bool
     *
     * @ORM\Column(name="alertemailflux", type="boolean", options={"default" : true})
     */
    protected $alertemailflux = true;

    /**
     * Alerte email à chaque transaction concernant l'utilisateur.
     *
     * @var bool
     *
     * @ORM\Column(name="canValidateAchat", type="boolean", options={"default" : false})
     */
    public $canValidateAchat;

    /**
     * @ORM\Column(type="text", nullable=true)
     */
    private $comment;

    /**
     * This flag is set to false as soon as a user with an enabled prestataire logs in.
     *
     * @var bool
     * @ORM\Column(type="boolean", nullable=false, options={"default" : true})
     */
    private bool $beforeFirstLoginWithPrestaEnabled;

    public function __construct()
    {
        parent::__construct();
        $this->imports = new ArrayCollection();
        $this->faqs = new ArrayCollection();
        $this->documents = new ArrayCollection();
        $this->flux = new ArrayCollection();
        $this->emailTokens = new ArrayCollection();
        $this->prestataires = new ArrayCollection();
        $this->caissiers = new ArrayCollection();
        $this->groupesgeres = new ArrayCollection();
        $this->comptoirsgeres = new ArrayCollection();
        $this->faqs = new ArrayCollection();
        $this->news = new ArrayCollection();
        $this->pages = new ArrayCollection();
        $this->possiblegroups = new ArrayCollection();
        $this->alertemailflux = true;
        $this->canValidateAchat = false;
        $this->beforeFirstLoginWithPrestaEnabled = true;
        $this->createApiKey();
        $this->createEmailToken();
    }

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

    /**
     * Get apiKey.
     *
     * @return string
     */
    public function getApiKey()
    {
        return $this->apiKey;
    }

    public function isUser(?UserInterface $user = null): bool
    {
        return $user instanceof self && $user->id === $this->id;
    }

    public function createApiKey()
    {
        $bytes = random_bytes(64);
        $this->apiKey = rtrim(strtr(base64_encode($bytes), '+/', '-_'), '=');
    }

    /**
     * @return string|null
     */
    public function getEtat(): ?string
    {
        return $this->etat;
    }

    /**
     * @param string|null $etat
     *
     * @return Prestataire
     */
    public function setEtat(?string $etat): self
    {
        $this->etat = $etat;

        return $this;
    }

    /**
     * Get mobile number.
     *
     * @return [type] [description]
     */
    public function getMobile(): ?string
    {
        return $this->mobile;
    }

    /**
     * Set mobile number.
     *
     * @param string $mobile [description]
     */
    public function setMobile(?string $mobile): self
    {
        $this->mobile = $mobile;

        return $this;
    }

    public function isGranted($role)
    {
        return in_array($role, $this->getRoles());
    }

    public function isAdmin()
    {
        if ($this->isSuperAdmin()) {
            return true;
        }
        $isAdmin = false;
        foreach ($this->getRoles() as $role) {
            if ((is_object($role) && 'ROLE_ADMIN' == $role->getRole()) || 'ROLE_ADMIN' == $role) {
                $isAdmin = true;
                break;
            }
        }

        return $isAdmin;
    }

    public function isSuperAdmin()
    {
        return $this->getSuperAdmin();
    }

    public function getSuperAdmin()
    {
        $isSuperAdmin = false;
        foreach ($this->getRoles() as $role) {
            if ((is_object($role) && 'ROLE_SUPER_ADMIN' == $role->getRole()) || 'ROLE_SUPER_ADMIN' == $role) {
                $isSuperAdmin = true;
                break;
            }
        }

        return $isSuperAdmin;
    }

    /**
     * Get adherent.
     *
     * @return
     */
    public function getAdherent()
    {
        return $this->adherent;
    }

    /**
     * Set adherent.
     *
     * @return $this
     */
    public function setAdherent($adherent): self
    {
        $this->adherent = $adherent;

        return $this;
    }

    /**
     * Get prestataires.
     *
     * @return
     */
    public function getPrestataires()
    {
        return $this->prestataires;
    }

    /**
     * @param Prestataire $prestataire
     *
     * @return $this
     */
    public function addPrestataire(Prestataire $prestataire): self
    {
        if (!$this->prestataires->contains($prestataire)) {
            $this->prestataires[] = $prestataire;
        }

        return $this;
    }

    /**
     * @param Prestataire $prestataire
     *
     * @return $this
     */
    public function removePrestataire(Prestataire $prestataire): self
    {
        if ($this->prestataires->contains($prestataire)) {
            $this->prestataires->removeElement($prestataire);
        }

        return $this;
    }

    /**
     * Set prestataire.
     *
     * @return $this
     */
    public function setPrestataires($prestataires): self
    {
        $this->prestataires = $prestataires;

        return $this;
    }

    /**
     * @return Collection<int, Caissier>
     */
    public function getCaissiers(): Collection
    {
        return $this->caissiers;
    }

    public function addCaissier(Caissier $caissier): self
    {
        if (!$this->caissiers->contains($caissier)) {
            $this->caissiers[] = $caissier;
            $caissier->setUser($this);
        }

        return $this;
    }

    public function removeCaissier(Caissier $caissier): self
    {
        if ($this->caissiers->removeElement($caissier)) {
            // set the owning side to null (unless already changed)
            if ($caissier->getUser() === $this) {
                $caissier->setUser(null);
            }
        }

        return $this;
    }

    public function setCaissiers($caissiers): self
    {
        $this->caissiers = $caissiers;

        return $this;
    }

    /**
     * @return Flux[]|ArrayCollection
     */
    public function getFlux()
    {
        return $this->flux;
    }

    /**
     * @param Flux $flux
     *
     * @return $this
     */
    public function addFlux(Flux $flux): self
    {
        if (!$this->flux->contains($flux)) {
            $this->flux[] = $flux;
            $flux->setUser($this);
        }

        return $this;
    }

    /**
     * @param Flux $flux
     *
     * @return $this
     */
    public function removeFlux(Flux $flux): self
    {
        throw new \LogicException('User::removeFlux : Ce code ne devrait jamais être atteint !');
        // if ($this->flux->contains($flux)) {
        //     $this->flux->removeElement($flux);
        // }
        return $this;
    }

    /**
     * @return Groupesgere[]|ArrayCollection
     */
    public function getGroupesgeres()
    {
        return $this->groupesgeres;
    }

    /**
     * @param Groupesgere[]|ArrayCollection
     *
     * @return $this
     */
    public function setGroupesgeres($groupesgeres): self
    {
        $this->groupesgeres = $groupesgeres;

        return $this;
    }

    /**
     * @param Groupesgere $groupesgere
     *
     * @return $this
     */
    public function addGroupesgere(Groupe $groupesgere): self
    {
        if (!$this->groupesgeres->contains($groupesgere)) {
            $this->groupesgeres[] = $groupesgere;
            $groupesgere->addGestionnaire($this);
        }

        return $this;
    }

    /**
     * @param Groupesgere $groupesgere
     *
     * @return $this
     */
    public function removeGroupesgere(Groupe $groupesgere): self
    {
        if ($this->groupesgeres->contains($groupesgere)) {
            $this->groupesgeres->removeElement($groupesgere);
            $groupesgere->removeGestionnaire($this);
        }

        return $this;
    }

    /**
     * @return Comptoirsgere[]|ArrayCollection
     */
    public function getComptoirsgeres()
    {
        return $this->comptoirsgeres;
    }

    /**
     * @param Comptoirsgere[]|ArrayCollection
     *
     * @return $this
     */
    public function setComptoirsgeres($comptoirsgeres): self
    {
        $this->comptoirsgeres = $comptoirsgeres;

        return $this;
    }

    /**
     * @param Comptoirsgere $comptoirsgere
     *
     * @return $this
     */
    public function addComptoirsgere(Comptoir $comptoirsgere): self
    {
        if (!$this->comptoirsgeres->contains($comptoirsgere)) {
            $this->comptoirsgeres[] = $comptoirsgere;
            $comptoirsgere->addGestionnaire($this);
        }

        return $this;
    }

    /**
     * @param Comptoirsgere $comptoirsgere
     *
     * @return $this
     */
    public function removeComptoirsgere(Comptoir $comptoirsgere): self
    {
        if ($this->comptoirsgeres->contains($comptoirsgere)) {
            $this->comptoirsgeres->removeElement($comptoirsgere);
            $comptoirsgere->removeGestionnaire($this);
        }

        return $this;
    }

    public function createEmailToken()
    {
        $token = new EmailToken();
        $token->setUser($this);
        $this->emailTokens->add($token);

        return $token;
    }

    public function getFirstValidEmailToken()
    {
        foreach ($this->getEmailTokens() as $emailToken) {
            if ($emailToken->isValid()) {
                return $emailToken;
            }
        }

        return null;
    }

    public function getEmailTokens()
    {
        return $this->emailTokens;
    }

    public function getEmailToken($token)
    {
        foreach ($this->emailTokens as $emailToken) {
            if ($emailToken->getToken() == $token) {
                return $emailToken;
            }
        }
    }

    public function __toString()
    {
        if (empty(trim($this->getFullname()))) {
            return $this->getEmail();
        }

        return $this->getFullname();
    }

    public function getName()
    {
        return $this->__toString();
    }

    /**
     * @return Collection|News[]
     */
    public function getNews(): Collection
    {
        return $this->news;
    }

    public function addNews(News $news): self
    {
        if (!$this->news->contains($news)) {
            $this->news[] = $news;
            $news->setUser($this);
        }

        return $this;
    }

    public function removeNews(News $news): self
    {
        if ($this->news->contains($news)) {
            $this->news->removeElement($news);
            // set the owning side to null (unless already changed)
            if ($news->getUser() === $this) {
                $news->setUser(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|Page[]
     */
    public function getPages(): Collection
    {
        return $this->pages;
    }

    public function addPage(Page $page): self
    {
        if (!$this->page->contains($page)) {
            $this->page[] = $page;
            $page->setUser($this);
        }

        return $this;
    }

    public function removePage(Page $page): self
    {
        if ($this->page->contains($page)) {
            $this->page->removeElement($page);
            // set the owning side to null (unless already changed)
            if ($page->getUser() === $this) {
                $page->setUser(null);
            }
        }

        return $this;
    }

    /**
     * Get possiblegroups.
     *
     * @return
     */
    public function getPossiblegroups()
    {
        return $this->possiblegroups;
    }

    /**
     * Set possiblegroups.
     *
     * @return $this
     */
    public function setPossiblegroups($possiblegroups)
    {
        $this->possiblegroups = $possiblegroups;

        return $this;
    }

    /**
     * addPossibleGroup.
     *
     * @param GroupInterface $possiblegroups [description]
     */
    public function addPossibleGroup(GroupInterface $possiblegroups)
    {
        if (!$this->getPossiblegroups()->contains($possiblegroups)) {
            $this->getPossiblegroups()->add($possiblegroups);
        }

        return $this;
    }

    /**
     * removePossibleGroup.
     *
     * @param GroupInterface $possiblegroups [description]
     *
     * @return [type] [description]
     */
    public function removePossibleGroup(GroupInterface $possiblegroups)
    {
        if ($this->getPossiblegroups()->contains($possiblegroups)) {
            $this->getPossiblegroups()->removeElement($possiblegroups);
        }

        return $this;
    }

    /**
     * Get alertemailflux.
     *
     * @return
     */
    public function getAlertemailflux()
    {
        return $this->alertemailflux;
    }

    /**
     * Set alertemailflux.
     *
     * @return $this
     */
    public function setAlertemailflux(bool $alertemailflux): self
    {
        $this->alertemailflux = $alertemailflux;

        return $this;
    }

    /**
     * Get canValidateAchat.
     *
     * @return
     */
    public function getCanValidateAchat()
    {
        return $this->canValidateAchat;
    }

    /**
     * Set canValidateAchat.
     *
     * @return $this
     */
    public function setCanValidateAchat(bool $canValidateAchat): self
    {
        $this->canValidateAchat = $canValidateAchat;

        return $this;
    }

    /**
     * Quand on appelle setGroups sur le user, on réinitialise ses groupes avant ! Pour pouvoir se connecter sur un seul groupe, les groupes possibles sont dans possiblegroups !
     * {@inheritdoc}
     */
    public function setGroups($groups)
    {
        $this->groups = new ArrayCollection();
        foreach ($groups as $group) {
            $this->addGroup($group);
        }

        return $this;
    }

    public function getComment(): ?string
    {
        return $this->comment;
    }

    public function setComment(?string $comment): self
    {
        $this->comment = $comment;

        return $this;
    }

    public function getBeforeFirstLoginWithPrestaEnabled(): bool
    {
        return $this->beforeFirstLoginWithPrestaEnabled;
    }
    public function setBeforeFirstLoginWithPrestaEnabled($var)
    {
        $this->beforeFirstLoginWithPrestaEnabled = $var;
    }
}
