<?php

/*
 * kohinos_cooperatic
 * Copyright (C) 2019-2020  ADML63
 * Copyright (C) 2020- Cooperatic
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
namespace App\Entity;

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

/**
 * 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"},
 *     errorPath="email",
 *     message="Cet email est déjà utilisé !"
 * )
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * @Groups({"user:read"})
     */
    protected $id;

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

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

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

    /**
     * @var null|string
     *
     * @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"})
     * @ORM\JoinTable(name="user_prestataire",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="prestataire_id", referencedColumnName="id")}
     * )
     */
    protected $prestataires;

    /**
     * @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="EXTRA_LAZY")
     * @ORM\JoinTable(name="user_usergroup",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
     * )
     * @Groups({"user"})
     */
    protected $groups;
    
    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Usergroup", fetch="EAGER")
     * @ORM\JoinTable(name="user_possiblegroup",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
     * )
     * @Groups({"user"})
     */
    protected $possiblegroups;

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

    /**
     * @ORM\ManyToMany(targetEntity=Prestataire::class, mappedBy="caissiers")
     */
    private $caissierprestataires;

    /**
     * @ORM\ManyToMany(targetEntity=Comptoir::class, mappedBy="caissiers")
     */
    private $caissiercomptoirs;

    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->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->createApiKey();
        $this->createEmailToken();
        $this->caissierprestataires = new ArrayCollection();
        $this->caissiercomptoirs = new ArrayCollection();
    }

    /**
    * 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), '+/', '-_'), '=');
    }


    public function getCommonName(): ?string
    {
        return $this->getFirstname()." ".$this->getLastname();
    }

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

    /**
     * @param null|string $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->getRole() == 'ROLE_ADMIN') || $role == 'ROLE_ADMIN') {
                $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->getRole() == 'ROLE_SUPER_ADMIN') || $role == 'ROLE_SUPER_ADMIN') {
                $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 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()
    {
        if (empty(trim($this->getFullname()))) {
            return $this->__toString();
        }
        return $this->getFullname();
    }

    /**
     * @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 getPage(): Collection
    {
        return $this->page;
    }

    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($alertemailflux): self
    {
        $this->alertemailflux = $alertemailflux;
        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;
    }

    /**
     * @return Collection|Prestataire[]
     */
    public function getCaissierprestataires(): Collection
    {
        return $this->caissierprestataires;
    }

    public function addCaissierprestataire(Prestataire $caissierprestataire): self
    {
        if (!$this->caissierprestataires->contains($caissierprestataire)) {
            $this->caissierprestataires[] = $caissierprestataire;
            $caissierprestataire->addCaissier($this);
        }

        return $this;
    }

    public function removeCaissierprestataire(Prestataire $caissierprestataire): self
    {
        if ($this->caissierprestataires->contains($caissierprestataire)) {
            $this->caissierprestataires->removeElement($caissierprestataire);
            $caissierprestataire->removeCaissier($this);
        }

        return $this;
    }

    /**
     * @return Collection|Comptoir[]
     */
    public function getCaissiercomptoirs(): Collection
    {
        return $this->caissiercomptoirs;
    }

    public function addCaissiercomptoir(Comptoir $caissiercomptoir): self
    {
        if (!$this->caissiercomptoirs->contains($caissiercomptoir)) {
            $this->caissiercomptoirs[] = $caissiercomptoir;
            $caissiercomptoir->addCaissier($this);
        }

        return $this;
    }

    public function removeCaissiercomptoir(Comptoir $caissiercomptoir): self
    {
        if ($this->caissiercomptoirs->contains($caissiercomptoir)) {
            $this->caissiercomptoirs->removeElement($caissiercomptoir);
            $caissiercomptoir->removeCaissier($this);
        }

        return $this;
    }
}
