<?php

namespace App\Entity;

use App\Entity\Comment;
use App\Entity\Document;
use Doctrine\ORM\Mapping as ORM;
use App\Repository\UserRepository;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;

use function Symfony\Component\String\u;

/**
 * @ORM\Entity(repositoryClass=UserRepository::class)
 * @UniqueEntity(fields={"email"}, message="There is already an account with this email")
 * @Gedmo\Loggable
 */
class User implements UserInterface, PasswordAuthenticatedUserInterface, \Serializable
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, unique=true)
     * @Gedmo\Versioned
     */
    private $email;

    /**
     * @ORM\Column(type="json")
     * @Gedmo\Versioned
     */
    private $roles = [];

    /**
     * @var string The hashed password
     * @ORM\Column(type="string")
     */
    private $password;

    /**
     * @ORM\Column(type="boolean", options={"default":"0"})
     */
    private $isVerified = false;

    /**
     * @ORM\OneToMany(targetEntity=Document::class, mappedBy="creator")
     */
    private $documents;

    /**
     * @ORM\ManyToOne(targetEntity=Group::class, inversedBy="users")
     */
    private $mainGroup;

    /**
     * @ORM\Column(type="boolean", options={"default":"0"})
     */
    private $adminMode = false;

    /**
     * @ORM\Column(type="string", length=255)
     * @Gedmo\Versioned
     */
    private $firstname;

    /**
     * @ORM\Column(type="string", length=255)
     * @Gedmo\Versioned
     */
    private $lastname;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $phone;

    /**
     * @ORM\Column(type="datetime_immutable")
     * @Gedmo\Timestampable(on="create")
     */
    private $createdAt;

    /**
     * @ORM\ManyToOne(targetEntity=Rank::class, inversedBy="users")
     */
    private $mainRank;

    /**
     * @ORM\OneToMany(targetEntity=Comment::class, mappedBy="creator")
     */
    private $comments;

    /**
     * @ORM\Column(type="boolean", options={"default":"0"})
     * @Gedmo\Versioned
     */
    private $isDesactivated = false;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     * @Gedmo\Versioned
     */
    private $locale;

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

    /**
     * @ORM\OneToMany(targetEntity=Notification::class, mappedBy="sender")
     */
    private $notificationsSent;

    /**
     * @ORM\OneToMany(targetEntity=Notification::class, mappedBy="receiver", orphanRemoval=true)
     */
    private $notifications;

    /**
     * @ORM\ManyToMany(targetEntity=SubGroup::class, inversedBy="users")
     */
    private $subGroups;

    public function __construct()
    {
        $this->documents = new ArrayCollection();
        $this->adminMode = false;
        $this->comments = new ArrayCollection();
        $this->sanctions = new ArrayCollection();
        $this->notificationsSent = new ArrayCollection();
        $this->notifications = new ArrayCollection();
        $this->subGroups = new ArrayCollection();
    }

    /** @see \Serializable::serialize() */
    public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->email,
            $this->password,
        ));
    }

    /** @see \Serializable::unserialize() */
    public function unserialize($serialized)
    {
        list (
            $this->id,
            $this->email,
            $this->password,
        ) = unserialize($serialized);
    }

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

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

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUserIdentifier(): string
    {
        return (string) $this->email;
    }

    /**
     * @deprecated since Symfony 5.3, use getUserIdentifier instead
     */
    public function getUsername(): string
    {
        return (string) $this->email;
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        $roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @see PasswordAuthenticatedUserInterface
     */
    public function getPassword(): string
    {
        return $this->password;
    }

    public function setPassword(string $password): self
    {
        $this->password = $password;

        return $this;
    }

    /**
     * Returning a salt is only needed, if you are not using a modern
     * hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
     *
     * @see UserInterface
     */
    public function getSalt(): ?string
    {
        return null;
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }

    public function isVerified(): bool
    {
        return $this->isVerified;
    }

    public function setIsVerified(bool $isVerified): self
    {
        $this->isVerified = $isVerified;

        return $this;
    }

    /**
     * @return Collection|Document[]
     */
    public function getDocuments(): Collection
    {
        return $this->documents;
    }

    public function addDocument(Document $document): self
    {
        if (!$this->documents->contains($document)) {
            $this->documents[] = $document;
            $document->setCreator($this);
        }

        return $this;
    }

    public function removeDocument(Document $document): self
    {
        if ($this->documents->removeElement($document)) {
            // set the owning side to null (unless already changed)
            if ($document->getCreator() === $this) {
                $document->setCreator(null);
            }
        }

        return $this;
    }

    public function getMainGroup(): ?Group
    {
        return $this->mainGroup;
    }

    public function setMainGroup(?Group $mainGroup): self
    {
        $this->mainGroup = $mainGroup;

        return $this;
    }

    public function getAdminMode(): ?bool
    {
        return $this->adminMode;
    }

    public function setAdminMode(bool $adminMode): self
    {
        if (!$this->hasRole('ROLE_ADMIN')) {
            $adminMode = false;
        }
        $this->adminMode = $adminMode;

        return $this;
    }

    public function getFirstname(): ?string
    {
        return $this->firstname;
    }

    public function setFirstname(string $firstname): self
    {
        $this->firstname = $firstname;

        return $this;
    }

    public function getLastname(): ?string
    {
        return $this->lastname;
    }

    public function setLastname(string $lastname): self
    {
        $this->lastname = $lastname;

        return $this;
    }

    public function getFullName(): string
    {
        return sprintf('%s %s', u($this->firstname)->title(true), u($this->lastname)->upper());
    }

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

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

        return $this;
    }

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

    public function setCreatedAt(\DateTimeImmutable $createdAt): self
    {
        $this->createdAt = $createdAt;

        return $this;
    }

    public function getMainRank(): ?Rank
    {
        return $this->mainRank;
    }

    public function setMainRank(?Rank $mainRank): self
    {
        $this->mainRank = $mainRank;

        $this->setMainGroup(($mainRank === null) ? null : $mainRank->getMainGroup());

        return $this;
    }

    /**
     * @return Collection|Comment[]
     */
    public function getComments(): Collection
    {
        return $this->comments;
    }

    public function addComment(Comment $comment): self
    {
        if (!$this->comments->contains($comment)) {
            $this->comments[] = $comment;
            $comment->setCreator($this);
        }

        return $this;
    }

    public function removeComment(Comment $comment): self
    {
        if ($this->comments->removeElement($comment)) {
            // set the owning side to null (unless already changed)
            if ($comment->getCreator() === $this) {
                $comment->setCreator(null);
            }
        }

        return $this;
    }

    /**
     * Custom function to check if user has permisssion
     *
     * @param String $permission
     * @return boolean
     */
    public function hasPermission(string $permission): bool
    {
        if ($this->getMainGroup() == null) {
            return false;
        }
        if ($this->getMainRank() == null) {
            return false;
        }
        if ($this->getAdminMode() == true) {
            return true;
        }
        if (in_array(strtolower($permission), array_map('strtolower', $this->getMainRank()->getPermissions()))) {
            return true;
        }
        return false;
    }

    /**
     * Custom function to check if user has role
     *
     * @param String $permission
     * @return boolean
     */
    public function hasRole(string $role): bool
    {
        if (in_array(strtolower($role), array_map('strtolower', $this->getRoles()))) {
            return true;
        }
        return false;
    }

    public function getIsDesactivated(): ?bool
    {
        return $this->isDesactivated;
    }

    public function setIsDesactivated(bool $isDesactivated): self
    {
        $this->isDesactivated = $isDesactivated;

        return $this;
    }

    public function getLocale(): string
    {
        return ($this->locale != null) ? $this->locale : 'en';
    }

    public function setLocale(?string $locale): self
    {
        $this->locale = $locale;

        return $this;
    }

    /**
     * @return Collection|Sanction[]
     */
    public function getSanctions(): Collection
    {
        return $this->sanctions;
    }

    public function addSanction(Sanction $sanction): self
    {
        if (!$this->sanctions->contains($sanction)) {
            $this->sanctions[] = $sanction;
            $sanction->setUser($this);
        }

        return $this;
    }

    public function removeSanction(Sanction $sanction): self
    {
        if ($this->sanctions->removeElement($sanction)) {
            // set the owning side to null (unless already changed)
            if ($sanction->getUser() === $this) {
                $sanction->setUser(null);
            }
        }

        return $this;
    }


    /**
     * @return Collection|Notification[]
     */
    public function getNotificationsSent(): Collection
    {
        return $this->notificationsSent;
    }

    public function addNotificationsSent(Notification $notificationsSent): self
    {
        if (!$this->notificationsSent->contains($notificationsSent)) {
            $this->notificationsSent[] = $notificationsSent;
            $notificationsSent->setSender($this);
        }

        return $this;
    }

    public function removeNotificationsSent(Notification $notificationsSent): self
    {
        if ($this->notificationsSent->removeElement($notificationsSent)) {
            // set the owning side to null (unless already changed)
            if ($notificationsSent->getSender() === $this) {
                $notificationsSent->setSender(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|Notification[]
     */
    public function getNotifications(): Collection
    {
        return $this->notifications;
    }

    public function addNotification(Notification $notification): self
    {
        if (!$this->notifications->contains($notification)) {
            $this->notifications[] = $notification;
            $notification->setReceiver($this);
        }

        return $this;
    }

    public function removeNotification(Notification $notification): self
    {
        if ($this->notifications->removeElement($notification)) {
            // set the owning side to null (unless already changed)
            if ($notification->getReceiver() === $this) {
                $notification->setReceiver(null);
            }
        }

        return $this;
    }

    /**
     * @return Collection|subGroup[]
     */
    public function getSubGroups(): Collection
    {
        return $this->subGroups;
    }

    public function addSubGroup(subGroup $subGroup): self
    {
        if (!$this->subGroups->contains($subGroup)) {
            if ($subGroup->getMainGroup != $this->mainGroup) {
                return null;
            }
            $this->subGroups[] = $subGroup;
        }

        return $this;
    }

    public function removeSubGroup(subGroup $subGroup): self
    {
        $this->subGroups->removeElement($subGroup);

        return $this;
    }
}
