<?php
namespace backendApi\modules\v1\models;

use common\components\Model;
use common\helpers\LoggerTool;
use common\helpers\Tool;
use common\libs\LoginIpChecker;
use Yii;
use yii\base\Exception;
use yii\captcha\Captcha;
use common\libs\logging\login\AdminLogin as AdminLoginLogger;

/**
 * Login form
 */
class LoginForm extends Model {

    public $adminName;
    public $password;
    public $verifyCode;
    private $_user;

    const ERROR_IS_MODIFY_PASSWORD = 'ERROR_IS_MODIFY_PASSWORD';

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'adminName' => '登录帐号',
            'password' => '登录密码',
            'verifyCode' => '验证码',
        ];
    }
    /**
     * @inheritdoc
     */
    public function rules() {
        return [
            // username and password are both required
            [['adminName', 'password', 'verifyCode'], 'required'],
            // rememberMe must be a boolean value
            ['verifyCode', 'captcha', 'captchaAction'=>'/v1/site/captcha'],
            // password is validated by validatePassword()
            //['password', 'validatePassword'],
        ];
    }

    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     *
     * @param string $attribute the attribute currently being validated
     * @param array $params the additional name-value pairs given in the rule
     */
    public function validatePassword($attribute, $params) {
        if (!$this->hasErrors()) {
            $this->getUser();
            if(!$this->_user){
                $this->addError($attribute, '会员不存在');
                return false;
            }
            if (!$this->_user->validatePassword($this->password)) {
                $this->addError($attribute, '用户名或者密码错误');
                return false;
            }
            //验证IP
            $bindIp = trim($this->_user['BIND_IP']);
            if(!empty($bindIp) && !(new LoginIpChecker(Yii::$app->request->getUserIP(), $bindIp))->validate()){
                $this->addError($attribute, '登录IP与此账号绑定的IP不符');
                return false;
            }
            return true;
        }
        return false;
    }

    /**
     * 更新失败次数
     * @param $transaction
     * @param $returnResult
     * @throws \Exception
     */
    private function _updateFailTimes($transaction,$returnResult){
        Admin::updateAllCounters([
            'FAIL_NUMS' => 1,
        ], 'ADMIN_NAME=:ADMIN_NAME', ['ADMIN_NAME' => $this->adminName]);
        $transaction->commit();
        if(isset($this->_user)){
            AdminLoginLogger::fail($this->_user,$returnResult, $this->password);
        }
        // 失败写入缓存锁
        Yii::$app->redis->incr('FAIL_NUMS:' . $this->adminName);
    }

    /**
     * 更新成功次数
     */
    private function _updateSuccessTimes(){
        Admin::updateAllCounters([
            'LOGIN_NUMS' => 1,
        ], 'ADMIN_NAME=:ADMIN_NAME', ['ADMIN_NAME' => $this->adminName]);
    }
    /**
     * 登录
     * @return array|bool
     * @throws \yii\base\Exception
     * @throws \yii\db\Exception
     */
    public function login(){
        if(!$this->validate()){
            return false;
        }
        $transaction = \Yii::$app->db->beginTransaction();
        try{
            $this->getUser();
            if(!$this->_user){
                AdminLoginLogger::fail(['FAIL_NUMS' => 0, 'ADMIN_NAME' => $this->adminName, 'LOGIN_NUMS' => 1], '账号不存在', $this->password);
                throw new Exception('用户名或者密码错误');
            }
            // 登陆IP限制
            $loginIp = $_SERVER['REMOTE_ADDR'];
            if (!Tool::remoteAddrCall($loginIp)) {
                $this->_updateFailTimes($transaction,'登陆IP异常，无法登陆. ' . $loginIp);
                throw new Exception('用户名或者密码错误');
            }
            // 失败次数到达上限次数
            $loginFailNums = Yii::$app->redis->get('FAIL_NUMS:' . $this->adminName) ?? 0;
            if ($loginFailNums >= 3) {
                $this->_updateFailTimes($transaction,'账号登陆失败次数过多，无法登录. ' . $loginFailNums);
                throw new Exception('用户名或者密码错误');
            }

            if(!$this->_user['IS_ENABLE']){
                $this->_updateFailTimes($transaction,'账号已经被锁定，无法登录');
                throw new Exception('用户名或者密码错误');
            }
            if (!$this->_user->validatePassword($this->password)) {
                $this->_updateFailTimes($transaction,'用户名或者密码错误');
                throw new Exception('用户名或者密码错误');
            }
            //验证IP
            $bindIp = trim($this->_user['BIND_IP']);
            if(!empty($bindIp) && !(new LoginIpChecker(Yii::$app->request->getUserIP(), $bindIp))->validate()){
                $this->_updateFailTimes($transaction,'登录IP与此账号绑定的IP不符');
                throw new Exception('用户名或者密码错误');
            }

            //需要修改密码
            if($this->_user['IS_MODIFY_PASSWORD'] == 1){
                throw new Exception(self::ERROR_IS_MODIFY_PASSWORD);
            }

            $this->_updateSuccessTimes();
            $transaction->commit();
            AdminLoginLogger::success($this->_user, $this->password);

            // 把用户的登录时间存在操作时间里
            Yii::$app->tokenRedis->hset('admin:timeOut', $this->_user->getId(), time());

            return Yii::$app->user->loginWithUAndP($this->_user);
        }catch(\Exception $e){
            $transaction->rollBack();
            $this->setError($e->getMessage());
            //AdminLoginLogger::fail($this->_user, $e->getMessage());
            return false;
        }
    }

    /**
     * Finds user by [[username]]
     *
     * @return User|null
     */
    public function getUser() {
        if ($this->_user === null) {
            $this->_user = User::findByUsername(strtolower($this->adminName));
        }
        return $this->_user;
    }

}
