<?php

namespace App\Entity;


use App\Entity\IFace\IEntity;
use App\Utils\Utils;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Entity
 * @ORM\Table(name="user")
 */
class EntUser implements IEntity, UserInterface
{
    // User id of system
    const SYSTEM_USER_ID = 0;

    // Costs of password encryption
    const CRYPT_COST = 12;

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", nullable=false, unique=true)
     */
    protected $email;

    /**
     * @ORM\Column(type="integer", nullable=false, options={"unsigned" = true})
     */
    protected $user_type_id;

    /**
     * @ORM\Column(type="string", length=50, nullable=false)
     */
    protected $firstname;

    /**
     * @ORM\Column(type="string", length=50, nullable=false)
     */
    protected $lastname;

    /**
     * @ORM\Column(type="string", length=60, nullable=false)
     */
    protected $password;

    /**
     * @ORM\Column(type="boolean", nullable=false)
     */
    protected $active;

    /**
     * @ORM\Column(type="boolean", nullable=false)
     */
    protected $visible;

    /**
     * @ORM\Column(type="datetime", nullable=false)
     */
    protected $creation_date;

    /**
     * EntUser constructor.
     * @param ObjectManager $em
     * @param $email
     * @param $firstName
     * @param $lastName
     * @param $password
     * @param $userTypeId
     * @param $userTypeIdCreatingUser
     * @param EntUser|null $editingUser
     * @throws \Exception
     */
    public function __construct(ObjectManager $em, $email, $firstName, $lastName, $password, $userTypeId, $userTypeIdCreatingUser, EntUser $editingUser = null)
    {
        // Check email
        if (!Utils::validateEmail($em, $email)) {
            throw new Exception('invalid or existent email given: '.$email);
        }

        $this->setUserTypeId($userTypeIdCreatingUser, $userTypeId, $editingUser);

        // Check password length
        if (!Utils::isValidPasswordLength($password)) {
            throw new Exception('invalid password length');
        }

        $this->email = $email;
        $this->user_type_id = $userTypeId;
        $this->firstname = $firstName;
        $this->lastname = $lastName;
        $this->password = password_hash($password, PASSWORD_BCRYPT, ["cost" => self::CRYPT_COST]);
        $this->active = true;
        $this->visible = true;
        $this->creation_date = new \DateTime();
    }

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return mixed
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * @return mixed
     */
    public function getUserTypeId()
    {
        return $this->user_type_id;
    }

    /**
     * @return mixed
     */
    public function getFirstname()
    {
        return $this->firstname;
    }

    /**
     * @return mixed
     */
    public function getLastname()
    {
        return $this->lastname;
    }

    /**
     * @return mixed
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * @return bool
     */
    public function isVisible(): bool
    {
        return $this->visible;
    }

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

    /**
     * @return mixed
     */
    public function getCreationDate()
    {
        return $this->creation_date;
    }

    /**
     * Returns username as part of UserInterface needed in Symfony
     * @return string
     */
    public function getUsername()
    {
        return $this->email;
    }

    /**
     * Returns roles as part of UserInterface needed in Symfony
     * @return array
     */
    public function getRoles()
    {
        return EntUserType::getUserRoles($this->user_type_id);
    }

    /**
     * Returns if user has admin rights
     * @return bool
     */
    public function isAdmin()
    {
        return in_array(EntUserType::USER_ROLE_ADMIN, $this->getRoles());
    }

    /**
     * Erases credentials as part of UserInterface needed in Symfony
     */
    public function eraseCredentials() {}

    /**
     * Checks password as part of UserInterface needed in Symfony
     * @param $password
     * @return bool
     */
    public function checkPassword($password)
    {
        return $this->active && password_verify($password, $this->password);
    }

    /**
     * Returns salt as part of UserInterface needed in Symfony
     * @return null|string|void
     */
    public function getSalt() {}

    /**
     * @param $userTypeIdCreatingUser
     * @param $userTypeId
     * @param EntUser|null $editingUser
     */
    public function setUserTypeId($userTypeIdCreatingUser, $userTypeId, EntUser $editingUser = null): void
    {
        // Check user type id
        if (!EntUserType::isValidUserTypeId($userTypeId)) {
            throw new Exception('invalid user type id');
        }

        // Compare creating user type with this user type
        $isEditingUser = is_null($editingUser) ? false : $editingUser->getId() == $this->id;
        if (!EntUserType::isHigherUserType($userTypeIdCreatingUser, $userTypeId) && !$isEditingUser) {
            throw new Exception('creating user has not enough rights to create this user type');
        }
        $this->user_type_id = $userTypeId;

    }

    /**
     * Sets email with check on validity and existence
     * @param ObjectManager $em
     * @param $email
     */
    public function setEmail(ObjectManager $em, $email)
    {
        if ($email != $this->email) {
            if (!Utils::validateEmail($em, $email)) {
                throw new Exception('email is invalid or already exists: '.$email);
            }
            $this->email = $email;
        }
    }

    /**
     * @param bool|string $password
     */
    public function setPassword($password): void
    {
        $this->password = $password;
    }

    /**
     * @param bool $active
     */
    public function setActive(bool $active): void
    {
        $this->active = $active;
    }

    /**
     * @param mixed $lastname
     */
    public function setLastname($lastname): void
    {
        $this->lastname = $lastname;
    }

    /**
     * @param string $firstname
     * @return EntUser
     */
    public function setFirstname(string $firstname): self
    {
        $this->firstname = $firstname;

        return $this;
    }

    /**
     * @param bool $visible
     */
    public function setVisible(bool $visible): void
    {
        $this->visible = $visible;
    }

    /**
     * Returns whether this user is on higher user role level than given user to compare with
     * @param EntUser $compareUser
     * @return bool
     */
    public function isHigherUser(EntUser $compareUser)
    {
        return EntUserType::isHigherUserType($this->user_type_id, $compareUser->getUserTypeId());
    }


    /**
     * Sets client data
     * @param ObjectManager $em
     * @param $clientData
     */
    public function setClientData(ObjectManager $em, $clientData)
    {
        $this->setFirstname($clientData->firstname);
        $this->setLastname($clientData->lastname);
        $this->setEmail($em, $clientData->email);
    }

    /**
     * Client mapper
     * @param ObjectManager $em
     * @param bool $fullMapping
     * @return array
     */
    public function clientMapper(ObjectManager $em, $fullMapping = false)
    {
        /** @var EntUserType $userType */
        $userType = $em->getRepository('App:EntUserType')->find($this->user_type_id);

        return [
            'id' => $this->id,
            'email' => $this->email,
            'firstname' => $this->firstname,
            'lastname' => $this->lastname,
            'active' => $this->active,
            'visible' => $this->visible,
            'v_translated_role' => EntUserType::getTranslatedRole($this->user_type_id),
            'v_user_type' => $userType->clientMapper($em, $fullMapping),
        ];
    }

}
