<?php

declare(strict_types=1);

namespace App\Entity;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use App\Repository\UserRepository;
use App\State\CurrentUserProvider;
use App\State\UserPasswordHasherProcessor;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Serializer\Attribute\SerializedName;

#[ApiResource(
    operations: [
        new Get(
            uriTemplate: '/me',
            normalizationContext: ['groups' => ['user:read']],
            security: "is_granted('ROLE_USER')",
            provider: CurrentUserProvider::class
        ),
        new GetCollection(
            normalizationContext: ['groups' => ['user:read', 'employee:read', 'site:read']],
            security: "is_granted('ROLE_ADMIN')"
        ),
        new Get(
            uriTemplate: '/users/{id}',
            normalizationContext: ['groups' => ['user:read', 'employee:read', 'site:read']],
            security: "is_granted('ROLE_ADMIN')"
        ),
        new Post(
            denormalizationContext: ['groups' => ['user:write']],
            normalizationContext: ['groups' => ['user:read']],
            security: "is_granted('ROLE_ADMIN')",
            processor: UserPasswordHasherProcessor::class
        ),
        new Patch(
            uriTemplate: '/users/{id}',
            paginationEnabled: false,
            normalizationContext: ['groups' => ['user:read']],
            denormalizationContext: ['groups' => ['user:write']],
            security: "is_granted('ROLE_ADMIN')",
            processor: UserPasswordHasherProcessor::class
        ),
    ]
)]
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: 'users')]
#[ORM\UniqueConstraint(name: 'uniq_users_username', fields: ['username'])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    #[Groups(['user:read'])]
    private ?int $id = null;

    #[ORM\Column(type: 'string', length: 180)]
    #[Groups(['user:read', 'user:write'])]
    private string $username = '';

    #[ORM\Column(type: 'json')]
    #[Groups(['user:write'])]
    private array $roles = [];

    #[ORM\Column(type: 'string')]
    private string $password = '';

    #[Groups(['user:write'])]
    private string $plainPassword = '';

    #[ApiProperty(readableLink: true)]
    #[ORM\OneToOne(targetEntity: Employee::class)]
    #[ORM\JoinColumn(nullable: true, unique: true)]
    #[Groups(['user:read', 'user:write'])]
    private ?Employee $employee = null;

    #[ORM\Column(type: 'boolean', options: ['default' => false])]
    #[Groups(['user:read', 'user:write'])]
    #[SerializedName('isLocked')]
    private bool $isLocked = false;

    #[ORM\Column(type: 'boolean', options: ['default' => false])]
    #[Groups(['user:write'])]
    #[SerializedName('hasLeaveRecapAccess')]
    private bool $hasLeaveRecapAccess = false;

    /**
     * @var Collection<int, UserSiteRole>
     */
    #[ORM\OneToMany(mappedBy: 'user', targetEntity: UserSiteRole::class, orphanRemoval: true)]
    private Collection $siteRoles;

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

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

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

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

        return $this;
    }

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

    /**
     * @return list<string>
     */
    #[Groups(['user:read'])]
    public function getRoles(): array
    {
        $roles   = $this->roles;
        $roles[] = 'ROLE_USER';

        return array_values(array_unique($roles));
    }

    /**
     * @param list<string> $roles
     */
    public function setRoles(array $roles): self
    {
        $this->roles = $roles;

        return $this;
    }

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

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

        return $this;
    }

    public function getPlainPassword(): string
    {
        return $this->plainPassword;
    }

    public function setPlainPassword(string $plainPassword): self
    {
        $this->plainPassword = $plainPassword;

        return $this;
    }

    public function getEmployee(): ?Employee
    {
        return $this->employee;
    }

    public function setEmployee(?Employee $employee): self
    {
        $this->employee = $employee;

        return $this;
    }

    /**
     * @return Collection<int, UserSiteRole>
     */
    public function getSiteRoles(): Collection
    {
        return $this->siteRoles;
    }

    public function addSiteRole(UserSiteRole $siteRole): self
    {
        if (!$this->siteRoles->contains($siteRole)) {
            $this->siteRoles->add($siteRole);
            $siteRole->setUser($this);
        }

        return $this;
    }

    public function removeSiteRole(UserSiteRole $siteRole): self
    {
        if ($this->siteRoles->removeElement($siteRole)) {
            if ($siteRole->getUser() === $this) {
                $siteRole->setUser(null);
            }
        }

        return $this;
    }

    #[Groups(['user:read'])]
    #[SerializedName('isLocked')]
    public function isLocked(): bool
    {
        return $this->isLocked;
    }

    public function setIsLocked(bool $isLocked): self
    {
        $this->isLocked = $isLocked;

        return $this;
    }

    #[Groups(['user:read'])]
    #[SerializedName('hasLeaveRecapAccess')]
    public function hasLeaveRecapAccess(): bool
    {
        return $this->hasLeaveRecapAccess;
    }

    public function setHasLeaveRecapAccess(bool $hasLeaveRecapAccess): self
    {
        $this->hasLeaveRecapAccess = $hasLeaveRecapAccess;

        return $this;
    }

    #[Groups(['user:read'])]
    public function getIsDriver(): bool
    {
        return $this->employee?->getIsDriver() ?? false;
    }

    public function eraseCredentials(): void {}
}
