<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\Common\Collections\ReadableCollection;
use Doctrine\Common\Collections\Selectable;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Represents a user group entity in the system.
 *
 * This class is used for managing user groups, including their relationships
 * with users, administrative users, and additional metadata such as colors and labels.
 *
 * Groups may be configured to have mutual exclusion properties based on an
 * exclusion key. This ensures that groups sharing the same key cannot coexist
 * in certain relationship contexts.
 *
 * Groups may be related to a UserJob. In that case, a cronjob task ensure that the members of the groups are
 * automatically synced with this group. Such groups are also automatically created by the cronjob.
 */
#[ORM\Entity]
#[ORM\Table(name: 'chill_main_user_group')]
// this discriminator key is required for automated denormalization
#[DiscriminatorMap('type', mapping: ['user_group' => UserGroup::class])]
class UserGroup
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
    private ?int $id = null;

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN, nullable: false, options: ['default' => true])]
    private bool $active = true;

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON, nullable: false, options: ['default' => '[]'])]
    private array $label = [];

    /**
     * @var Collection<int, User>&Selectable<int, User>
     */
    #[ORM\ManyToMany(targetEntity: User::class)]
    #[ORM\JoinTable(name: 'chill_main_user_group_user')]
    private Collection&Selectable $users;

    /**
     * @var Collection<int, User>&Selectable<int, User>
     */
    #[ORM\ManyToMany(targetEntity: User::class)]
    #[ORM\JoinTable(name: 'chill_main_user_group_user_admin')]
    private Collection&Selectable $adminUsers;

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#ffffffff'])]
    private string $backgroundColor = '#ffffffff';

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => '#000000ff'])]
    private string $foregroundColor = '#000000ff';

    /**
     * Groups with same exclude key are mutually exclusive: adding one in a many-to-one relationship
     * will exclude others.
     *
     * An empty string means "no exclusion"
     */
    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
    private string $excludeKey = '';

    #[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false, options: ['default' => ''])]
    #[Assert\Email]
    private string $email = '';

    /**
     * UserJob to which the group is related.
     */
    #[ORM\ManyToOne(targetEntity: UserJob::class)]
    #[ORM\JoinColumn(nullable: true)]
    private ?UserJob $userJob = null;

    public function __construct()
    {
        $this->adminUsers = new ArrayCollection();
        $this->users = new ArrayCollection();
    }

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

    public function setActive(bool $active): self
    {
        $this->active = $active;

        return $this;
    }

    public function addAdminUser(User $user): self
    {
        if (!$this->adminUsers->contains($user)) {
            $this->adminUsers[] = $user;
        }

        return $this;
    }

    public function removeAdminUser(User $user): self
    {
        $this->adminUsers->removeElement($user);

        return $this;
    }

    public function addUser(User $user): self
    {
        if (!$this->users->contains($user)) {
            $this->users[] = $user;
        }

        return $this;
    }

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

        return $this;
    }

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

    public function getLabel(): array
    {
        return $this->label;
    }

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

    /**
     * @return Selectable<int, User>&Collection<int, User>
     */
    public function getAdminUsers(): Collection&Selectable
    {
        return $this->adminUsers;
    }

    public function getForegroundColor(): string
    {
        return $this->foregroundColor;
    }

    public function getExcludeKey(): string
    {
        return $this->excludeKey;
    }

    public function getBackgroundColor(): string
    {
        return $this->backgroundColor;
    }

    public function setForegroundColor(string $foregroundColor): self
    {
        $this->foregroundColor = $foregroundColor;

        return $this;
    }

    public function setBackgroundColor(string $backgroundColor): self
    {
        $this->backgroundColor = $backgroundColor;

        return $this;
    }

    public function setExcludeKey(string $excludeKey): self
    {
        $this->excludeKey = $excludeKey;

        return $this;
    }

    public function setLabel(array $label): self
    {
        $this->label = $label;

        return $this;
    }

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

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

        return $this;
    }

    public function hasEmail(): bool
    {
        return '' !== $this->email;
    }

    public function hasUserJob(): bool
    {
        return null !== $this->userJob;
    }

    public function getUserJob(): ?UserJob
    {
        return $this->userJob;
    }

    public function setUserJob(?UserJob $userJob): void
    {
        $this->userJob = $userJob;
    }

    /**
     * Checks if the current object is an instance of the UserGroup class.
     *
     * In use in twig template, to discriminate when there an object can be polymorphic.
     *
     * @return bool returns true if the current object is an instance of UserGroup, false otherwise
     */
    public function isUserGroup(): bool
    {
        return true;
    }

    public function contains(User $user): bool
    {
        return $this->users->contains($user);
    }

    public function getUserListByLabelAscending(): ReadableCollection
    {
        $criteria = Criteria::create();
        $criteria->orderBy(['label' => Order::Ascending]);

        return $this->getUsers()->matching($criteria);
    }

    public function getAdminUserListByLabelAscending(): ReadableCollection
    {
        $criteria = Criteria::create();
        $criteria->orderBy(['label' => Order::Ascending]);

        return $this->getAdminUsers()->matching($criteria);
    }
}
