<?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;
    public $lang;
    private $_user;

    const ERROR_IS_MODIFY_PASSWORD = 'ERROR_IS_MODIFY_PASSWORD';

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'adminName' => '登录帐号',
            'password' => '登录密码',
            'verifyCode' => '验证码',
            'lang' => '语言标识',
        ];
    }
    /**
     * @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, 'Member does not exist');
                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 = Admin::findOneAsArray('ADMIN_NAME=:ADMIN_NAME', ['ADMIN_NAME' => $this->adminName]);
        if ($admin['FAIL_NUMS'] > 0) {
            Admin::updateAllCounters([
                'FAIL_NUMS' => 1,
            ], 'ADMIN_NAME=:ADMIN_NAME', ['ADMIN_NAME' => $this->adminName]);
        } else {
            Admin::updateAll(['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);
        LoggerTool::error(sprintf('tmp_log_fail_nums_incr, adminName: %s', $this->adminName));
    }

    /**
     * 更新成功次数
     */
    private function _updateSuccessTimes(){
        $admin = Admin::findOneAsArray('ADMIN_NAME=:ADMIN_NAME', ['ADMIN_NAME' => $this->adminName]);
        if ($admin['LOGIN_NUMS'] > 0) {
            Admin::updateAllCounters([
                'LOGIN_NUMS' => 1,
            ], 'ADMIN_NAME=:ADMIN_NAME', ['ADMIN_NAME' => $this->adminName]);
        } else {
            Admin::updateAll(['LOGIN_NUMS' => 1], 'ADMIN_NAME=:ADMIN_NAME', ['ADMIN_NAME' => $this->adminName]);
        }
    }

    /**
     * 更新系统语言类型
     * @param $lang
     * @return void
     */
    private function _updateLang($lang) {
        $lang = Tool::langConvert($lang);
        Admin::updateAll([
            'LANG' => $lang,
        ], '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){
                throw new Exception('The account does not exist'); // 账号不存在
            }
            if(!$this->_user['IS_ENABLE']){
                $this->_updateFailTimes($transaction,'账号已经被锁定，无法登录');
                throw new Exception('The account has been locked and cannot be logged in'); // 账号已经被锁定，无法登录
            }
            if (!$this->_user->validatePassword($this->password)) {
                $this->_updateFailTimes($transaction,'用户名或者密码错误');
                throw new Exception('Incorrect user name or password'); // 用户名或者密码错误
            }
            //验证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('The login IP does not match the IP bound to this account'.$bindIp); // 登录IP与此账号绑定的IP不符
            }

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

            $this->_updateSuccessTimes();

            // 如果用户没有设置语言类型,则选择前端传递的语言类型,并存储到表中
            if (!$this->_user['LANG']) {
                $this->_updateLang($this->lang);
            }

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

            // 把用户的登录时间存在操作时间里
            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;
    }

}
