<?php
declare (strict_types=1);

namespace App\Entity\Person;

use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\ApiResource;
use App\Entity\Access\Access;
use App\Entity\AccessWish\DocumentWish;
use App\Entity\Core\BankAccount;
use App\Entity\Core\ContactPoint;
use App\Entity\Core\Country;
use App\Entity\Core\File;
use App\Repository\Person\PersonRepository;

//use DH\Auditor\Provider\Doctrine\Auditing\Annotation\Auditable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JetBrains\PhpStorm\Pure;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * Personne physique ou morale
 */
#[ApiResource(
    operations: [
        new Get(
            security: 'is_granted(\'ROLE_USERS_VIEW\')'
        )
    ]
)]
//#[Auditable]
#[ORM\Entity(repositoryClass: PersonRepository::class)]
class Person implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\Column]
    #[ORM\GeneratedValue]
    private ?int $id = null;

    #[ORM\Column(length: 180, unique: true, nullable: true)]
    private ?string $username = null;

    /** @var string[]  */
    private array $roles = [];

    #[ORM\Column(nullable: true)]
    private ?string $password = null;

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

    #[ORM\Column(length: 255, nullable: true)]
    #[Groups(["access_people_ref", "access_address"])]
    private ?string $givenName = null;

    #[ORM\ManyToMany(targetEntity: ContactPoint::class, mappedBy: 'person')]
    private Collection $contactPoints;

    #[ORM\ManyToMany(targetEntity: BankAccount::class, inversedBy: 'person', cascade: ['persist'], orphanRemoval: true)]
    #[ORM\JoinColumn(name: 'person_id', referencedColumnName: 'id')]
    #[ORM\InverseJoinColumn(name: 'bankAccount_id', referencedColumnName: 'id')]
    private Collection $bankAccount;

    #[ORM\OneToMany(mappedBy: 'person', targetEntity: PersonAddressPostal::class, orphanRemoval: true)]
    #[Groups("access_address")]
    private Collection $personAddressPostal;

    #[ORM\Column(nullable: true)]
    #[Assert\Choice(callback: ['\\App\\Enum\\Person\\GenderEnum', 'toArray'], message: 'invalid-gender')]
    private ?string $gender = null;

    #[ORM\ManyToOne(inversedBy: 'personImages')]
    #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
    private ?File $image = null;

    #[ORM\Column(options: ['default' => true])]
    private bool $isPhysical = true;

    #[ORM\ManyToOne]
    private Country $nationality;

    #[ORM\OneToMany(mappedBy: 'person', targetEntity: Access::class, orphanRemoval: true)]
    private Collection $access;

    #[ORM\OneToMany(mappedBy: 'person', targetEntity: File::class, orphanRemoval: true)]
    private Collection $files;

    #[ORM\OneToMany(mappedBy: 'person', targetEntity: DisciplineOtherEstablishment::class, cascade: ['persist'], orphanRemoval: true)]
    private Collection $disciplineotherestablishments;

    #[ORM\OneToMany(mappedBy: 'person', targetEntity: Qualification::class, cascade: ['persist'], orphanRemoval: true)]
    private Collection $qualifications;

    #[ORM\OneToMany(mappedBy: 'person', targetEntity: SchoolingInEstablishment::class, cascade: ['persist'], orphanRemoval: true)]
    private Collection $schoolingEstablisments;

    #[ORM\OneToMany(mappedBy: 'person', targetEntity: TeacherSchoolingHistory::class, cascade: ['persist'], orphanRemoval: true)]
    private Collection $teacherSchoolingHistories;

    #[ORM\ManyToMany(targetEntity: File::class, mappedBy: 'accessPersons', cascade: ['persist'])]
    #[ORM\OrderBy(['id' => 'DESC'])]
    private Collection $personFiles;

    #[ORM\OneToMany(mappedBy: 'personOwner', targetEntity: DocumentWish::class, cascade: ['persist'], orphanRemoval: true)]
    private Collection $documentWishes;

    #[Pure]
    public function __construct()
    {
        $this->contactPoints = new ArrayCollection();
        $this->personAddressPostal = new ArrayCollection();
        $this->bankAccount = new ArrayCollection();
        $this->access = new ArrayCollection();
        $this->files = new ArrayCollection();
        $this->disciplineotherestablishments = new ArrayCollection();
        $this->qualifications = new ArrayCollection();
        $this->schoolingEstablisments = new ArrayCollection();
        $this->teacherSchoolingHistories = new ArrayCollection();
        $this->personFiles = new ArrayCollection();
        $this->documentWishes = new ArrayCollection();
    }

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

    public function getUsername(): ?string
    {
        return (string)$this->username;
    }

    public function getUserIdentifier(): string
    {
        return (string)$this->username;
    }

    public function setUsername(?string $username): self
    {
        $this->username = $username;
        return $this;
    }

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

    /**
     * @param string[] $roles
     * @return $this
     */
    public function setRoles(array $roles): self
    {
        $this->roles = $roles;
        return $this;
    }

    public function getPassword(): ?string
    {
        return (string)$this->password;
    }

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

    public function getSalt(): ?string
    {
        return null;
        // not needed when using the "bcrypt" algorithm in security.yaml
    }

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

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

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

    public function getGivenName(): ?string
    {
        return $this->givenName;
    }

    public function setGivenName(?string $givenName): self
    {
        $this->givenName = $givenName;
        return $this;
    }

    public function getFullName(): ?string
    {
        if (!$this->getName() || !$this->getGivenName()) {
            return null;
        }
        return "{$this->getName()} {$this->getGivenName()}";
    }

    public function setGender(?string $gender): self
    {
        $this->gender = $gender;
        return $this;
    }

    public function getGender(): ?string
    {
        return $this->gender;
    }

    /**
     * @return Collection
     */
    public function getContactPoints(): Collection
    {
        return $this->contactPoints;
    }

    public function addContactPoint(ContactPoint $contactPoint): self
    {
        if (!$this->contactPoints->contains($contactPoint)) {
            $this->contactPoints[] = $contactPoint;
            $contactPoint->addPerson($this);
        }
        return $this;
    }

    public function removeContactPoint(ContactPoint $contactPoint): self
    {
        if ($this->contactPoints->removeElement($contactPoint)) {
            $contactPoint->removePerson($this);
        }
        return $this;
    }

    public function setImage(?File $image): self
    {
        $this->image = $image;
        return $this;
    }

    public function getImage(): ?File
    {
        return $this->image;
    }

    public function getPersonAddressPostal(): Collection
    {
        return $this->personAddressPostal;
    }

    public function addPersonAddressPostal(PersonAddressPostal $personAddressPostal): self
    {
        if (!$this->personAddressPostal->contains($personAddressPostal)) {
            $this->personAddressPostal[] = $personAddressPostal;
            $personAddressPostal->setPerson($this);
        }
        return $this;
    }

    public function removePersonAddressPostal(PersonAddressPostal $personAddressPostal): self
    {
        if ($this->personAddressPostal->removeElement($personAddressPostal)) {
            // set the owning side to null (unless already changed)
            if ($personAddressPostal->getPerson() === $this) {
                $personAddressPostal->setPerson(null);
            }
        }
        return $this;
    }

    public function getIsPhysical(): ?bool
    {
        return $this->isPhysical;
    }

    public function setAdminAccess(bool $isPhysical): self
    {
        $this->isPhysical = $isPhysical;
        return $this;
    }

    public function setIsPhysical(bool $isPhysical): self
    {
        $this->isPhysical = $isPhysical;
        return $this;
    }

    /**
     * @return Collection<int, BankAccount>
     */
    public function getBankAccount(): Collection
    {
        return $this->bankAccount;
    }

    public function addBankAccount(BankAccount $bankAccount): self
    {
        if (!$this->bankAccount->contains($bankAccount)) {
            $this->bankAccount[] = $bankAccount;
        }
        return $this;
    }

    public function removeBankAccount(BankAccount $bankAccount): self
    {
        $this->bankAccount->removeElement($bankAccount);
        return $this;
    }

    public function getNationality(): ?Country
    {
        return $this->nationality;
    }

    public function setNationality(?Country $nationality): self
    {
        $this->nationality = $nationality;
        return $this;
    }

    /**
     * @return Collection<int, Access>
     */
    public function getAccess(): Collection
    {
        return $this->access;
    }

    public function addAccess(Access $access): self
    {
        if (!$this->access->contains($access)) {
            $this->access[] = $access;
            $access->setPerson($this);
        }
        return $this;
    }

    public function removeAccess(Access $access): self
    {
        if ($this->access->removeElement($access)) {
            // set the owning side to null (unless already changed)
            if ($access->getPerson() === $this) {
                $access->setPerson(null);
            }
        }
        return $this;
    }

    /**
     * @return Collection<int, File>
     */
    public function getFiles(): Collection
    {
        return $this->files;
    }

    public function addFile(File $file): self
    {
        if (!$this->files->contains($file)) {
            $this->files[] = $file;
            $file->setPerson($this);
        }
        return $this;
    }

    public function removeFile(File $file): self
    {
        if ($this->files->removeElement($file)) {
            // set the owning side to null (unless already changed)
            if ($file->getPerson() === $this) {
                $file->setPerson(null);
            }
        }
        return $this;
    }

    /**
     * @return Collection<int, DisciplineOtherEstablishment>
     */
    public function getDisciplineotherestablishments(): Collection
    {
        return $this->disciplineotherestablishments;
    }

    public function addDisciplineotherestablishment(DisciplineOtherEstablishment $disciplineotherestablishment): self
    {
        if (!$this->disciplineotherestablishments->contains($disciplineotherestablishment)) {
            $this->disciplineotherestablishments[] = $disciplineotherestablishment;
            $disciplineotherestablishment->setPerson($this);
        }
        return $this;
    }

    public function removeDisciplineotherestablishment(DisciplineOtherEstablishment $disciplineotherestablishment): self
    {
        if ($this->disciplineotherestablishments->removeElement($disciplineotherestablishment)) {
            // set the owning side to null (unless already changed)
            if ($disciplineotherestablishment->getPerson() === $this) {
                $disciplineotherestablishment->setPerson(null);
            }
        }
        return $this;
    }

    /**
     * @return Collection<int, Qualification>
     */
    public function getQualifications(): Collection
    {
        return $this->qualifications;
    }

    public function addQualification(Qualification $qualification): self
    {
        if (!$this->qualifications->contains($qualification)) {
            $this->qualifications[] = $qualification;
            $qualification->setPerson($this);
        }
        return $this;
    }

    public function removeQualification(Qualification $qualification): self
    {
        if ($this->qualifications->removeElement($qualification)) {
            // set the owning side to null (unless already changed)
            if ($qualification->getPerson() === $this) {
                $qualification->setPerson(null);
            }
        }
        return $this;
    }

    /**
     * @return Collection<int, SchoolingInEstablishment>
     */
    public function getSchoolingEstablisments(): Collection
    {
        return $this->schoolingEstablisments;
    }

    public function addSchoolingEstablisment(SchoolingInEstablishment $schoolingEstablisment): self
    {
        if (!$this->schoolingEstablisments->contains($schoolingEstablisment)) {
            $this->schoolingEstablisments[] = $schoolingEstablisment;
            $schoolingEstablisment->setPerson($this);
        }
        return $this;
    }

    public function removeSchoolingEstablisment(SchoolingInEstablishment $schoolingEstablisment): self
    {
        if ($this->schoolingEstablisments->removeElement($schoolingEstablisment)) {
            // set the owning side to null (unless already changed)
            if ($schoolingEstablisment->getPerson() === $this) {
                $schoolingEstablisment->setPerson(null);
            }
        }
        return $this;
    }

    /**
     * @return Collection<int, TeacherSchoolingHistory>
     */
    public function getTeacherSchoolingHistories(): Collection
    {
        return $this->teacherSchoolingHistories;
    }

    public function addTeacherSchoolingHistory(TeacherSchoolingHistory $teacherSchoolingHistory): self
    {
        if (!$this->teacherSchoolingHistories->contains($teacherSchoolingHistory)) {
            $this->teacherSchoolingHistories[] = $teacherSchoolingHistory;
            $teacherSchoolingHistory->setPerson($this);
        }
        return $this;
    }

    public function removeTeacherSchoolingHistory(TeacherSchoolingHistory $teacherSchoolingHistory): self
    {
        if ($this->teacherSchoolingHistories->removeElement($teacherSchoolingHistory)) {
            // set the owning side to null (unless already changed)
            if ($teacherSchoolingHistory->getPerson() === $this) {
                $teacherSchoolingHistory->setPerson(null);
            }
        }
        return $this;
    }

    /**
     * @return Collection<int, File>
     */
    public function getPersonFiles(): Collection
    {
        return $this->personFiles;
    }

    public function addPersonFile(File $personFile): self
    {
        if (!$this->personFiles->contains($personFile)) {
            $this->personFiles[] = $personFile;
            $personFile->addAccessPerson($this);
        }
        return $this;
    }

    public function removePersonFile(File $personFile): self
    {
        if ($this->personFiles->removeElement($personFile)) {
            $personFile->removeAccessPerson($this);
        }
        return $this;
    }

    /**
     * @return Collection<int, DocumentWish>
     */
    public function getDocumentWishes(): Collection
    {
        return $this->documentWishes;
    }

    public function addDocumentWish(DocumentWish $documentWish): self
    {
        if (!$this->documentWishes->contains($documentWish)) {
            $this->documentWishes[] = $documentWish;
            $documentWish->setPersonOwner($this);
        }
        return $this;
    }

    public function removeDocumentWish(DocumentWish $documentWish): self
    {
        if ($this->documentWishes->removeElement($documentWish)) {
            // set the owning side to null (unless already changed)
            if ($documentWish->getPersonOwner() === $this) {
                $documentWish->setPersonOwner(null);
            }
        }
        return $this;
    }
}
