<?php
/**
 * Created by PhpStorm.
 * User: leo
 * Date: 2018/2/28
 * Time: 上午10:31
 */
namespace backendApi\modules\v1\components;

use backendApi\modules\v1\models\Admin;
use backendApi\modules\v1\models\AdminRole;
use backendApi\modules\v1\models\AdminToken;
use common\components\Request;
use common\helpers\Cache;
use common\helpers\Date;
use common\helpers\Form;
use common\helpers\Log;
use common\helpers\Tool;
use common\models\UserInfo;
use common\models\UserToken;
use Yii;
use yii\db\ActiveRecordInterface;
use yii\helpers\Json;
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 $_deviceInfo = null;

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

    /**
     * 首次以用户名和密码的方式登录
     * @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'],
                'adminName' => $identity['ADMIN_NAME'],
                'roleId' => $identity['ROLE_ID'],
                'accessTokenUpdatedAt' => Date::nowTime(),
                'ip' => $ip,
            ];

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

            $userToken = AdminToken::findOne(['ADMIN_ID' => $identity['ID']]);
            if(!$userToken){
                $userToken = new AdminToken();
                $userToken->ADMIN_ID = $identity['ID'];
                $userToken->CREATED_AT = Date::nowTime();
                $userToken->save();
            }
            $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'access');
            $this->updateToken($userToken, $appType = $this->_device, $typeToken = 'refresh');

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

        return !$this->getIsGuest();
    }

    public function afterLogin($identity, $cookieBased, $duration)
    {
        parent::afterLogin($identity, $cookieBased, $duration);
        // 记录管理员登录日志
        // Log::adminLogin();
    }

    /**
     * 已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,
                'adminName' => Yii::$app->tokenRedis->hget($token, 'ADMIN_NAME'),
                'roleId' => Yii::$app->tokenRedis->hget($token, 'ROLE_ID'),
                'accessTokenUpdatedAt' => Yii::$app->tokenRedis->hget($token, 'TOKEN_UPDATED_AT'),
                'ip' => Yii::$app->getRequest()->getUserIP(),
            ];
            return $userId;
        } 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 = AdminToken::findOne(['ADMIN_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 = AdminToken::findOne(['ADMIN_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 = AdminToken::findOne(['ADMIN_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['backAccessTokenExpiresIn'];
            } elseif ($typeToken === 'refresh'){
                $updateField = 'PRT_UPDATED_AT';
                $expiresIn = Yii::$app->params['backRefreshTokenExpiresIn'];
            } else {
                throw new HttpException(500, 'token字段错误', 500);
            }
        } elseif ($appType === Request::DEVICE_APP){
            if($typeToken === 'access'){
                $updateField = 'AAT_UPDATED_AT';
                $expiresIn = Yii::$app->params['backAccessTokenExpiresIn'];
            } elseif ($typeToken === 'refresh'){
                $updateField = 'ART_UPDATED_AT';
                $expiresIn = Yii::$app->params['backRefreshTokenExpiresIn'];
            } 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, 'ADMIN_NAME', $identity['ADMIN_NAME']);
        Yii::$app->tokenRedis->hset($token, 'ROLE_ID', $identity['ROLE_ID']);
        Yii::$app->tokenRedis->hset($token, 'TOKEN_UPDATED_AT', $userTokenModel->$updateField);
        Yii::$app->tokenRedis->expire($token, $expiresIn);

        $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 getAdminPermission(){
        $userInfo = $this->_userInfo;
        $adminRoles = Cache::getAdminRole();
        //$role = AdminRole::findOne(['ID'=>$userInfo['roleId']]);
        return $adminRoles[$userInfo['roleId']]['PERMISSION'];
    }

    /**
     * 校验管理员权限
     * @param $controller
     * @param string $action
     * @return bool
     */
    public function validateAdminAction($controller, $action = ''){
        // 查看控制器是否在白名单中，如果在白名单中则直接返回true
        $noCheckActions = Yii::$app->params['noCheckPermissionActions'];
        if(in_array($controller.'/'.$action, $noCheckActions)){
            return true;
        }
        $userInfo = $this->_userInfo;
        if($userInfo && isset($userInfo) && $userInfo['roleId'] === Yii::$app->params['superAdminRoleId']){
            return true;
        }
        // 获取管理员权限
        $permission = $this->getAdminPermission();
        if(!$permission){
            return false;
        }
        if($controller && $action){
            if(in_array($controller.'/'.$action, $permission)){
                return true;
            }
        }
//        elseif ($controller){
//            $pattern = '/'.$controller.'\//';
//            foreach($permission as $value){
//                if(preg_match($pattern, $value)){
//                    return true;
//                }
//            }
//        }
        return false;
    }

    /**
     * 查看是否有该控制器的权限
     * @param $controller
     * @return bool
     */
    public function validateAdminController($controller){
        $userInfo = $this->_userInfo;
        if($userInfo && isset($userInfo) && $userInfo['roleId'] === Yii::$app->params['superAdminRoleId']){
            return true;
        }
        $result = false;
        // 查看控制器是否在白名单中，如果在白名单中则直接返回true
        $noCheckActions = Yii::$app->params['noCheckPermissionActions'];
        foreach($noCheckActions as $action){
            if(preg_match('/^'.$controller.'\//', $action)){
                $result = true;
                break;
            }
        }
        // 查看会员的权限
        $permissions = $this->getAdminPermission();
        if($permissions){
            foreach($permissions as $permission){
                if(preg_match('/^'.$controller.'\//', $permission)){
                    $result = true;
                    break;
                }
            }
        }

        return $result;
    }

    /**
     * 不需要检查管理员权限的控制器
     * @param $controller
     * @return bool
     */
    public function noCheckAdminController($controller){
        $userInfo = $this->_userInfo;
        if($userInfo && isset($userInfo) && $userInfo['roleId'] === Yii::$app->params['superAdminRoleId']){
            return false;
        }
        $result = false;
        // 查看控制器是否在白名单中，如果在白名单中则直接返回true
        $noCheckActions = Yii::$app->params['noCheckPermissionActions'];
        foreach($noCheckActions as $action){
            if(preg_match('/^'.$controller.'\//', $action)){
                $result = true;
                break;
            }
        }
        // 查看会员的权限
        $permissions = $this->getAdminPermission();
        if($permissions){
            foreach($permissions as $permission){
                if(preg_match('/^'.$controller.'\//', $permission)){
                    $result = false;
                    break;
                }
            }
        }
        return $result;
    }
}