1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 
<?php

declare(strict_types=1);

namespace Wtf\Auth\Repository;

use Wtf\Root;

class User extends Root implements RepositoryInterface
{
    /**
     * {@inheritdoc}
     */
    public function getLoginFields(): array
    {
        return ['email', 'login', 'username'];
    }

    /**
     * Get password field, eg: 'password'.
     *
     * @return string
     */
    public function getPasswordField(): string
    {
        return 'password';
    }

    /**
     * Get forgot password code field, eg: 'forgot'.
     *
     * @return string
     */
    public function getForgotField(): string
    {
        return 'forgot';
    }

    /**
     * {@inheritdoc}
     */
    public function login(string $login, string $password): ?Root
    {
        $user = $this->getByLogin($login);
        if (null === $user) {
            return null;
        }

        if (!\password_verify($password, $user->get($this->getPasswordField()))) {
            return null;
        }

        return $user;
    }

    /**
     * {@inheritdoc}
     */
    public function getByLogin(string $login): ?Root
    {
        $entity = $this->entity($this->config('auth.entity', 'user'));
        foreach ($this->getLoginFields() as $field) {
            try {
                if ($entity->has([$field => $login])) {
                    return $entity->load($login, $field);
                }
            } catch (\Throwable $t) {
                //If field does not exist, exception will be thrown,
                //but for that case it's not a problem,
                //so just ignore it and go to the next field
            }
        }

        return null;
    }

    /**
     * {@inheritdoc}
     */
    public function forgot(string $login): string
    {
        $user = $this->getByLogin($login);
        if (null === $user) {
            return '';
        }

        $user->set($this->getForgotField(), \md5($user->getId().\random_int(PHP_INT_MIN, PHP_INT_MAX)))->save(false);

        return $user->get($this->getForgotField());
    }

    /**
     * {@inheritdoc}
     */
    public function reset(string $code, string $new_password): bool
    {
        $user = $this->entity($this->config('auth.entity', 'user'));
        if (!$user->has([$this->getForgotField() => $code])) {
            return false;
        }

        $user->load($code, $this->getForgotField())
             ->setData([
             $this->getForgotField() => null,
             $this->getPasswordField() => \password_hash($new_password, PASSWORD_DEFAULT),
         ])->save(false);

        return true;
    }
}