<?php
/**
 * Created by PhpStorm.
 * User: leo
 * Date: 2018/2/28
 * Time: 上午10:31
 */

namespace frontendApi\modules\v1\components;

use common\components\Redis;
use common\components\Request;
use common\helpers\Date;
use common\helpers\Form;
use common\models\Message;
use common\models\UserInfo;
use common\models\UserToken;
use common\helpers\Log;
use Yii;
use yii\db\ActiveRecordInterface;
use yii\web\HttpException;
use yii\web\IdentityInterface;
use yii\web\User;

class UserAuth extends User {
    private $_userId = null;
    private $_apiIdentity = null;
    private $_token = null;
    private $_userInfo = null;
    private $_device = null;
    private static $_isQuicklyLogin = false;

    /**
     * 初始化设备信息
     * @throws \yii\base\InvalidConfigException
     */
    public function init() {
        parent::init();
        $this->_device = Yii::$app->request->getDevice();
    }

    /**
     * 首次以用户名和密码的方式登录
     * @param IdentityInterface $identity
     * @return bool
     * @throws HttpException
     */
    public function loginWithUAndP(IdentityInterface $identity) {
        if ($this->beforeLogin($identity, false, 0)) {
            $id = $identity->getId();
            $ip = Yii::$app->getRequest()->getUserIP();

            $this->_userId = $identity['ID'];
            $this->_apiIdentity = $identity;
            $this->_userInfo = [
                'id' => $identity['ID'],
                'userName' => $identity['USER_NAME'],
                'accessTokenUpdatedAt' => Date::nowTime(),
                'ip' => $ip,
            ];

            UserInfo::updateAll(['LAST_LOGIN_IP' => $ip, 'LAST_LOGIN_AT' => Date::nowTime()], 'USER_ID=:USER_ID', [':USER_ID'=>$identity['ID']]);

            $userToken = UserToken::findOne(['USER_ID' => $identity['ID']]);
            if (!$userToken) {
                $userToken = new UserToken();
                $userToken->USER_ID = $identity['ID'];
                $userToken->CREATED_AT = Date::nowTime();
                if (!$userToken->save()) {
                    throw new HttpException(500, Form::formatErrorsForApi($userToken->getErrors()), 500);
                }
            }
            $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'access');
            $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'refresh');

            //// Log::adminLoginInfo();

            $this->afterLogin($identity, false, 0);
        }

        return !$this->getIsGuest();
    }

    /**
     * 登录成功之后
     * @param IdentityInterface $identity
     * @param bool $cookieBased
     * @param int $duration
     * @throws \yii\base\Exception
     */
    public function afterLogin($identity, $cookieBased, $duration) {
        // 拉取站内信
        Message::pullMsgByUser($identity['ID']);
        parent::afterLogin($identity, $cookieBased, $duration);
    }

    /**
     * 已AccessToken方式登录（即平时直接访问）
     * @param string $token
     * @param null $type
     * @return null|IdentityInterface
     */
    public function loginByAccessToken($token, $type = null) {
        /* @var $class IdentityInterface */
        $class = $this->identityClass;
        $userId = $this->_userId = $class::findIdentityByAccessToken($token, $type);
        if ($userId) {
            $this->_userInfo = [
                'id' => $userId,
                'userName' => Yii::$app->tokenRedis->hget($token, 'USER_NAME'),
                'accessTokenUpdatedAt' => Yii::$app->tokenRedis->hget($token, 'TOKEN_UPDATED_AT'),
                'ip' => Yii::$app->getRequest()->getUserIP(),
            ];
            return $userId;
        } else {
            return null;
        }
    }

    /**
     * 从后台登录前台
     * @param $userId
     * @return null
     * @throws HttpException
     */
    public function loginByBackend($userId) {
        if (UserInfo::find()->where(['USER_ID' => $userId])->exists()) {
            $userToken = UserToken::findOne(['USER_ID' => $userId]);
            if (!$userToken) {
                $userToken = new UserToken();
                $userToken->USER_ID = $userId;
                $userToken->CREATED_AT = Date::nowTime();
                if (!$userToken->save()) {
                    return null;
                }
            }
            self::$_isQuicklyLogin = true;
            $accessTokenResult = $this->updateToken($userToken, $appType = 'pc', $typeToken = 'access', $userId);
            $refreshTokenResult = $this->updateToken($userToken, $appType = 'pc', $typeToken = 'refresh', $userId);
            if ($accessTokenResult && $refreshTokenResult) {
                return $this->getToken();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 用refreshToken生成新的accessToken和refreshToken
     * @param $refreshToken
     * @return bool
     * @throws HttpException
     */
    public function refreshToken($refreshToken) {
        if (!$refreshToken) {
            return false;
        }
        $userId = Yii::$app->tokenRedis->hget($refreshToken, 'ID');
        if (!$userId) {
            return false;
        }
        $userToken = UserToken::findOne(['USER_ID' => $userId]);
        $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'access', $userId);
        $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'refresh', $userId);
        return true;
    }

    /**
     * 用refreshToken生成新的accessToken
     * @param $refreshToken
     * @return bool
     * @throws HttpException
     */
    public function refreshAccessToken($refreshToken) {
        if (!$refreshToken) {
            return false;
        }
        $userId = Yii::$app->tokenRedis->hget($refreshToken, 'ID');
        if (!$userId) {
            return false;
        }
        $userToken = UserToken::findOne(['USER_ID' => $userId]);
        return $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'access', $userId);
    }

    /**
     * 用refreshToken生成新的refreshToken
     * @param $refreshToken
     * @return bool
     * @throws HttpException
     */
    public function refreshRefreshToken($refreshToken) {
        if (!$refreshToken) {
            return false;
        }
        $userId = Yii::$app->tokenRedis->hget($refreshToken, 'ID');
        if (!$userId) {
            return false;
        }
        $userToken = UserToken::findOne(['USER_ID' => $userId]);
        return $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'refresh', $userId);
    }

    /**
     * 更新token 的具体方法
     * @param ActiveRecordInterface $userTokenModel
     * @param string $appType (pc|app)
     * @param string $typeToken
     * @param $userId
     * @return bool
     * @throws HttpException
     */
    public function updateToken(ActiveRecordInterface $userTokenModel, $appType = Request::DEVICE_PC, $typeToken = 'access', $userId = 0) {
        $tokenField = strtoupper($appType . '_' . $typeToken . '_TOKEN');
        $updateField = '';
        $expiresIn = 0;
        if ($appType === Request::DEVICE_PC) {
            if ($typeToken === 'access') {
                $updateField = 'PAT_UPDATED_AT';
                $expiresIn = Yii::$app->params['frontAccessTokenExpiresIn'];
            } elseif ($typeToken === 'refresh') {
                $updateField = 'PRT_UPDATED_AT';
                $expiresIn = Yii::$app->params['frontRefreshTokenExpiresIn'];
            } else {
                throw new HttpException(500, 'token字段错误', 500);
            }
        } elseif ($appType === Request::DEVICE_APP) {
            if ($typeToken === 'access') {
                $updateField = 'AAT_UPDATED_AT';
                $expiresIn = Yii::$app->params['frontAccessTokenExpiresIn'];
            } elseif ($typeToken === 'refresh') {
                $updateField = 'ART_UPDATED_AT';
                $expiresIn = Yii::$app->params['frontRefreshTokenExpiresIn'];
            } else {
                throw new HttpException(500, 'token字段错误', 500);
            }
        }
        // 老token
        $oldToken = $userTokenModel->$tokenField;
        // 生成 access_token
        /* @var $identityClass IdentityInterface */
        $identityClass = $this->identityClass;
        $generateTokenMethodName = 'generate' . ucfirst($typeToken) . 'Token';
        //$token = $identityClass::generateAccessToken();
        $token = call_user_func([$identityClass, $generateTokenMethodName], $appType);
        $userTokenModel->$tokenField = $token;
        $userTokenModel->$updateField = Date::nowTime();
        if (!$userTokenModel->save()) {
            throw new HttpException(500, 'token更新失败', 500);
        }
        // 查找TOKEN中是否有同一用户产生的垃圾token，有的话就清除
        Yii::$app->tokenRedis->del($oldToken);

        $identity = $this->_apiIdentity;
        if (!$this->_apiIdentity) {
            if (!$userId) {
                throw new HttpException(500, 'userId不能为空', 500);
            }
            $identity = $identityClass::findIdentity($userId);
        }
        // 把 accessToken 当做key存入redis中内容为会员的ID和用户名
        Yii::$app->tokenRedis->hset($token, 'ID', $identity['ID']);
        Yii::$app->tokenRedis->hset($token, 'USER_NAME', $identity['USER_NAME']);
        Yii::$app->tokenRedis->hset($token, 'TOKEN_UPDATED_AT', $userTokenModel->$updateField);
        Yii::$app->tokenRedis->expire($token, $expiresIn);

        // 标记为快速登录的会员
        if (self::$_isQuicklyLogin) {
            Yii::$app->redis->setex(Redis::key(\frontendApi\modules\v1\models\User::CACHE_IS_QUICKLY_LOGIN . $token), Yii::$app->params['frontAccessTokenExpiresIn'], 1);
        }

        $this->_token = array_merge($this->_token ? $this->_token : [], [
            $typeToken . 'Token' => $token,
            $typeToken . 'TokenExpiresIn' => $expiresIn,
            $typeToken . 'TokenUpdateAt' => $userTokenModel->$updateField,
        ]);
        return true;
    }

    /**
     * 获取管理员ID
     * @return int|null|string
     */
    public function getId() {
        return $this->_userId;
    }

    /**
     * 获取token
     * @return null
     */
    public function getToken() {
        return $this->_token;
    }

    /**
     * 获取管理员信息
     * @return null
     */
    public function getUserInfo() {
        return $this->_userInfo;
    }

    /**
     * 获取身份信息
     * @param bool $autoRenew
     * @return null|IdentityInterface
     */
    public function getIdentity($autoRenew = true) {
        if ($this->_apiIdentity) {
            return $this->_apiIdentity;
        } else {
            if ($this->_userId) {
                /* @var $class IdentityInterface */
                $class = $this->identityClass;
                return $class::findOne(['ID' => $this->_userId]);
            } else {
                return null;
            }
        }
    }

    /**
     * 获取权限
     * @return mixed
     */
    public function getUserPermission() {
        return [];
    }

    /**
     * 校验权限
     * @param $controller
     * @param string $action
     * @return bool
     */
    public function validateUserAction($controller, $action = '') {
        $isRecharge = \common\models\User::getEnCodeInfo($this->_userId)['IS_RECHARGE'];
        if($controller=='finance' &&  $action=='recharge' &&  $isRecharge==0){
            return false;
        }
        return true;
//        $userInfo = $this->_userInfo;
//        if($userInfo['roleId'] === Yii::$app->params['superAdminRoleId']){
//            return true;
//        }
//        // 查看控制器是否在白名单中，如果在白名单中则直接返回true
//        $noCheckActions = Yii::$app->params['noCheckPermissionActions'];
//        if(in_array($controller.'/'.$action, $noCheckActions)){
//            return true;
//        }
//        return true;
    }

    /**
     * 查看是否有该控制器的权限
     * @param $controller
     * @return bool
     */
    public function validateUserController($controller) {
        $isAtlas = \common\models\User::getEnCodeInfo($this->_userId)['IS_ATLAS'];
        if($controller=='atlas' &&  $isAtlas==0){
            return false;
        }
        return true;
//        if($userInfo['roleId'] === Yii::$app->params['superAdminRoleId']){
//            return true;
//        }
//        $result = true;
//        // 查看控制器是否在白名单中，如果在白名单中则直接返回true
//        $noCheckActions = Yii::$app->params['noCheckPermissionActions'];
//        foreach($noCheckActions as $action){
//            if(preg_match('/^'.$controller.'\//', $action)){
//                $result = true;
//                break;
//            }
//        }
//
//        return $result;
    }
}