<?php

/*
 * This file is part of the Serenity package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Serenity\AuthBundle\Domain\User\Entity;

use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Serenity\AuthBundle\Domain\User\Event\UserCreatedEvent;
use Serenity\CoreBundle\Shared\Aggregate\AggregateRoot;
use Serenity\CoreBundle\Shared\Aggregate\AggregateRootInterface;
use Serenity\CoreBundle\Shared\Common\IdentifierInterface;
use Serenity\CoreBundle\Shared\Common\IdentifierTrait;
use Serenity\CoreBundle\Shared\Common\TimestampTrait;
use Serenity\CoreBundle\Shared\Common\UserInterface;
use Serenity\CoreBundle\Shared\Feature\SerenitySaasInterface;
use Serenity\CoreBundle\Shared\Feature\SerenitySaasTrait;
use Serenity\CoreBundle\Shared\Feature\SoftDeletableTrait;
use Serenity\CoreBundle\Shared\ValueObject\Email;
use Serenity\CoreBundle\Shared\ValueObject\Password;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Serializer\Attribute\Ignore;
use Symfony\Component\Validator\Constraints as Assert;

#[ORM\MappedSuperclass]
class AbstractUser implements IdentifierInterface, PasswordAuthenticatedUserInterface, UserInterface, AggregateRootInterface, SerenitySaasInterface
{
    use AggregateRoot;
    use IdentifierTrait {
        IdentifierTrait::__construct as private identifierConstruct;
    }
    use SerenitySaasTrait;
    use SoftDeletableTrait;
    use TimestampTrait;

    #[ORM\Embedded(class: Email::class, columnPrefix: false)]
    protected Email $email;

    #[Ignore]
    #[ORM\Embedded(class: Password::class, columnPrefix: false)]
    protected Password $password;

    /**
     * @var string[]
     */
    #[ORM\Column(type: 'json_document', options: ['jsonb' => true])]
    protected array $roles;

    #[ORM\Column(type: Types::BOOLEAN)]
    protected bool $verified = false;

    #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
    protected ?\DateTime $lastLinkRequestedAt = null;

    #[ORM\Column(type: Types::INTEGER)]
    protected int $lastLinkRequestedAtCount = 0;

    #[ORM\Column(type: Types::STRING, nullable: true)]
    protected ?string $lastLoginIp = null;

    #[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
    protected ?\DateTimeInterface $lastLoginAt = null;

    #[Assert\Valid]
    #[ORM\OneToOne(targetEntity: Profile::class, cascade: ['ALL'])]
    protected ?Profile $profile = null;

    /**
     * @param non-empty-string $email
     * @param string[]         $roles
     */
    public function __construct(string $email, string $password, array $roles = [], bool $test = false)
    {
        $this->identifierConstruct();
        $this->email = new Email($email);
        $this->password = new Password($password, $test);
        $this->roles = $roles;
        $this->recordEvent(
            new UserCreatedEvent($this->getId(), $email, $this->password->plainPassword, new \DateTime())
        );
    }

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

    /**
     * @param non-empty-string $email
     */
    public function setEmail(string $email): Email
    {
        $this->email = new Email($email);

        return $this->email;
    }

    /**
     * Returns the identifier for this user (e.g. username or email address).
     *
     * @return non-empty-string
     */
    public function getUserIdentifier(): string
    {
        return $this->email->value;
    }

    public function setPassword(string $plainPassword): Password
    {
        $this->password = new Password($plainPassword);

        return $this->password;
    }

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

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

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

        return array_unique($roles);
    }

    public function setVerified(bool $verified): void
    {
        $this->verified = $verified;
    }

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

    public function getLastLinkRequestedAt(): ?\DateTime
    {
        return $this->lastLinkRequestedAt;
    }

    public function setLastLinkRequestedAt(?\DateTime $lastLinkRequestedAt): void
    {
        $this->lastLinkRequestedAt = $lastLinkRequestedAt;
    }

    public function getLastLinkRequestedAtCount(): int
    {
        return $this->lastLinkRequestedAtCount;
    }

    public function setLastLinkRequestedAtCount(int $lastLinkRequestedAtCount): void
    {
        $this->lastLinkRequestedAtCount = $lastLinkRequestedAtCount;
    }

    public function setLastLoginIp(?string $lastLoginIp): void
    {
        $this->lastLoginIp = $lastLoginIp;
    }

    public function setLastLoginAt(?\DateTimeInterface $lastLoginAt): void
    {
        $this->lastLoginAt = $lastLoginAt;
    }

    public function getLastLoginIp(): ?string
    {
        return $this->lastLoginIp;
    }

    public function getLastLoginAt(): ?\DateTimeInterface
    {
        return $this->lastLoginAt;
    }

    public function getProfile(): ?Profile
    {
        return $this->profile;
    }

    public function setProfile(?Profile $profile): void
    {
        $this->profile = $profile;
    }

    public function eraseCredentials(): void
    {
    }

    public function getSalt(): ?string
    {
        return null;
    }
}
