<?php

namespace App\Entity;

use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Tree\Entity\Repository\MaterializedPathRepository;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;

#[Gedmo\Tree(type: 'materializedPath')]
#[ORM\Entity(repositoryClass: MaterializedPathRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_NAME', columns: ['name', 'parent_id'])]
#[UniqueEntity(fields: ['name', 'parent'], message: 'This organizational unit already exists.')]
class OrganizationalUnit extends AbstractEntity
{
    use NameableTrait;

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

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

    #[Gedmo\TreePath(separator: '/', appendId: false, startsWithSeparator: true, endsWithSeparator: false)]
    #[ORM\Column(length: 255, nullable: true)]
    private ?string $path = null;

    #[Gedmo\TreeLevel]
    #[ORM\Column(length: 255, nullable: true)]
    private ?int $level;

    #[Gedmo\TreePathSource]
    #[Gedmo\Slug(fields: ['name'])]
    #[ORM\Column(length: 255, nullable: true)]
    private ?string $slug = null;

    #[Gedmo\TreeParent]
    #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'organizationalUnits')]
    #[ORM\JoinColumn( onDelete: 'SET NULL')]
    private ?self $parent = null;

    /**
     * @var Collection<int, self>
     */
    #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
    private Collection $organizationalUnits;

    #[ORM\ManyToOne(targetEntity: NetworkSettings::class,  inversedBy: 'organizationalUnits', cascade: ['persist'])]
    #[ORM\JoinColumn(nullable: true)]
    private ?NetworkSettings $networkSettings = null;

    /**
     * @var Collection<int, User>
     */
    #[ORM\ManyToMany(targetEntity: User::class, mappedBy: 'allowedOrganizationalUnits')]
    private Collection $users;

    /**
     * @var Collection<int, Client>
     */
    #[ORM\OneToMany(mappedBy: 'organizationalUnit', targetEntity: Client::class)]
    private Collection $clients;

    #[ORM\Column(length: 255)]
    private ?string $type = null;

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

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

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

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

    /**
     * @var Collection<int, SoftwareProfile>
     */
    #[ORM\OneToMany(mappedBy: 'organizationalUnit', targetEntity: SoftwareProfile::class)]
    private Collection $softwareProfiles;

    #[ORM\ManyToOne(inversedBy: 'organizationalUnits')]
    private ?Subnet $subnet = null;

    #[ORM\ManyToOne(targetEntity: RemoteCalendar::class, cascade: ['persist'], inversedBy: 'organizationalUnits')]
    #[ORM\JoinColumn(nullable: true)]
    private ?RemoteCalendar $remoteCalendar = null;

    #[ORM\Column]
    private ?bool $remotePc = false;

    #[ORM\Column]
    private ?bool $reserved = false;

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

    public function __construct()
    {
        parent::__construct();
        $this->organizationalUnits = new ArrayCollection();
        $this->users = new ArrayCollection();
        $this->clients = new ArrayCollection();
        $this->softwareProfiles = new ArrayCollection();
    }

    public function getDescription(): ?string
    {
        return $this->description;
    }

    public function setDescription(?string $description): static
    {
        $this->description = $description;

        return $this;
    }

    public function getComments(): ?string
    {
        return $this->comments;
    }

    public function setComments(?string $comments): static
    {
        $this->comments = $comments;

        return $this;
    }

    public function getPath(): ?string
    {
        return $this->path;
    }

    public function setPath(?string $path): static
    {
        $this->path = $path;

        return $this;
    }

    public function getLevel(): ?int
    {
        return $this->level;
    }

    public function setLevel(?int $level): static
    {
        $this->level = $level;

        return $this;
    }

    public function getSlug(): ?string
    {
        return $this->slug;
    }

    public function setSlug(?string $slug): static
    {
        $this->slug = $slug;

        return $this;
    }

    public function getParent(): ?self
    {
        return $this->parent;
    }

    public function setParent(?self $parent): static
    {
        $this->parent = $parent;

        return $this;
    }

    /**
     * @return Collection<int, self>
     */
    public function getOrganizationalUnits(): Collection
    {
        return $this->organizationalUnits;
    }

    public function addOrganizationalUnit(self $organizationalUnit): static
    {
        if (!$this->organizationalUnits->contains($organizationalUnit)) {
            $this->organizationalUnits->add($organizationalUnit);
            $organizationalUnit->setParent($this);
        }

        return $this;
    }

    public function removeOrganizationalUnit(self $organizationalUnit): static
    {
        if ($this->organizationalUnits->removeElement($organizationalUnit)) {
            // set the owning side to null (unless already changed)
            if ($organizationalUnit->getParent() === $this) {
                $organizationalUnit->setParent(null);
            }
        }

        return $this;
    }

    public function getNetworkSettings(): ?NetworkSettings
    {
        return $this->networkSettings;
    }

    public function setNetworkSettings(?NetworkSettings $networkSettings): static
    {
        $this->networkSettings = $networkSettings;

        return $this;
    }

    /**
     * @return Collection<int, User>
     */
    public function getUsers(): Collection
    {
        return $this->users;
    }

    public function addUser(User $user): static
    {
        if (!$this->users->contains($user)) {
            $this->users->add($user);
            $user->addAllowedOrganizationalUnit($this);
        }

        return $this;
    }

    public function removeUser(User $user): static
    {
        if ($this->users->removeElement($user)) {
            $user->removeAllowedOrganizationalUnit($this);
        }

        return $this;
    }

    /**
     * @return Collection<int, Client>
     */
    public function getClients(): Collection
    {
        return $this->clients;
    }

    public function addClient(Client $client): static
    {
        if (!$this->clients->contains($client)) {
            $this->clients->add($client);
            $client->setOrganizationalUnit($this);
        }

        return $this;
    }

    public function removeClient(Client $client): static
    {
        if ($this->clients->removeElement($client)) {
            if ($client->getOrganizationalUnit() === $this) {
                $client->setOrganizationalUnit(null);
            }
        }

        return $this;
    }

    public function getType(): ?string
    {
        return $this->type;
    }

    public function setType(string $type): static
    {
        $this->type = $type;

        return $this;
    }

    public function getLocation(): ?string
    {
        return $this->location;
    }

    public function setLocation(?string $location): static
    {
        $this->location = $location;

        return $this;
    }

    public function isProjector(): ?bool
    {
        return $this->projector;
    }

    public function setProjector(?bool $projector): static
    {
        $this->projector = $projector;

        return $this;
    }

    public function isBoard(): ?bool
    {
        return $this->board;
    }

    public function setBoard(?bool $board): static
    {
        $this->board = $board;

        return $this;
    }

    public function getCapacity(): ?int
    {
        return $this->capacity;
    }

    public function setCapacity(?int $capacity): static
    {
        $this->capacity = $capacity;

        return $this;
    }

    public function updateNetworkSettingsRecursively(?NetworkSettings $networkSettings): void
    {
        $this->setNetworkSettings($networkSettings);

        /** @var Client $client */
        foreach ($this->getClients() as $client) {
            $client->setMenu($networkSettings->getMenu());
            $client->setHardwareProfile($networkSettings->getHardwareProfile());
        }

        foreach ($this->getOrganizationalUnits() as $childUnit) {
            $childUnit->updateNetworkSettingsRecursively($networkSettings);
        }
    }

    /**
     * @return Collection<int, SoftwareProfile>
     */
    public function getSoftwareProfiles(): Collection
    {
        return $this->softwareProfiles;
    }

    public function addSoftwareProfile(SoftwareProfile $softwareProfile): static
    {
        if (!$this->softwareProfiles->contains($softwareProfile)) {
            $this->softwareProfiles->add($softwareProfile);
            $softwareProfile->setOrganizationalUnit($this);
        }

        return $this;
    }

    public function removeSoftwareProfile(SoftwareProfile $softwareProfile): static
    {
        if ($this->softwareProfiles->removeElement($softwareProfile)) {
            // set the owning side to null (unless already changed)
            if ($softwareProfile->getOrganizationalUnit() === $this) {
                $softwareProfile->setOrganizationalUnit(null);
            }
        }

        return $this;
    }

    public function getSubnet(): ?Subnet
    {
        return $this->subnet;
    }

    public function setSubnet(?Subnet $subnet): static
    {
        $this->subnet = $subnet;

        return $this;
    }

    public function getRemoteCalendar(): ?RemoteCalendar
    {
        return $this->remoteCalendar;
    }

    public function setRemoteCalendar(?RemoteCalendar $remoteCalendar): static
    {
        $this->remoteCalendar = $remoteCalendar;

        return $this;
    }

    public function isRemotePc(): ?bool
    {
        return $this->remotePc;
    }

    public function setRemotePc(bool $remotePc): static
    {
        $this->remotePc = $remotePc;

        return $this;
    }

    public function isReserved(): ?bool
    {
        return $this->reserved;
    }

    public function setReserved(bool $reserved): static
    {
        $this->reserved = $reserved;

        return $this;
    }

    public function isExcludeParentChanges(): ?bool
    {
        return $this->excludeParentChanges ?? false;
    }

    public function setExcludeParentChanges(?bool $excludeParentChanges): static
    {
        $this->excludeParentChanges = $excludeParentChanges;

        return $this;
    }
}
