<?php
/**
 * @link      http://www.u-bo.com
 * @copyright 南京友博网络科技有限公司 
 * @license   http://www.u-bo.com/license/
 */

namespace common\models;

use Yii;
use yii\base\NotSupportedException;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
use qcbase\models\Auth;
use qcbase\models\UserInfo;
use qcbase\models\Department;
use yii\db\Query;
use yii\data\ActiveDataProvider;
use yii\base\ErrorException;
use common\components\Image;
use yii\helpers\ArrayHelper;
use home\models\UserPortlet;

/**
 * 用户模型模型
 * 
 * @property integer $user_id  (ID)
 * @property string $username  (用户名)
 * @property string $user_no  (用户工号)
 * @property string $password_hash  (加盐的密码)
 * @property string $password_reset_token  (重置密码token)
 * @property string $auth_key  (身份验证密钥 保证cookie安全)
 * @property string $realname  (真实姓名)
 * @property smallint $gender  (性别)
 * @property string $email  (邮箱)
 * @property string $avatar  (头像)
 * @property smallint $status  (状态)
 * @property smallint $sort  (排序)
 * @property string $last_login_ip  (最后登陆ip地址)
 * @property integer $last_login_time  (最后登陆时间)
 * @property integer $create_time  (创建时间)
 * 
 * @author legendjw http://blog.u-bo.com
 * @since 0.1
 */
class User extends ActiveRecord implements IdentityInterface
{
    const STATUS_ENABLE = 1; //启用
    const STATUS_DISABLE = 0; //禁用
    const GENDER_MALE = 1; //男
    const GENDER_FEMALE = 0; //女

    public $password;
    public $role;
    public $depts;
    private $_info;
    public $repassword; //重复密码字段
    public $old_password;

    /**
     * 初始化用户模型
     *
     * - 绑定添加默认组件操作到新增用户事件
     */
    public function init()
    {
        parent::init();

        $this->on(self::EVENT_AFTER_INSERT, [UserPortlet::className(), 'addDefaultPortlet']);
    }

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%user}}';
    }

    /**
     * @inheritdoc
     */
    public function scenarios()
    {
        $scenario = parent::scenarios();
        $scenario['insert'] = ['username', 'user_no', 'realname', 'role', 'depts', 'password', 'gender', 'status', 'email'];
        $scenario['update'] = ['username', 'user_no', 'realname', 'role', 'depts', 'password', 'gender', 'status', 'email'];
        $scenario['info'] = ['realname', 'gender', 'email', 'avatar'];
        $scenario['repwd'] = ['password', 'old_password', 'repassword'];
        return $scenario;
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['username', 'user_no', 'status', 'gender', 'email', 'role', 'depts', 'realname'], 'required'],
            ['password', 'required', 'on' => ['insert', 'repwd']],
            [['old_password'], 'required'],
            //[['password'], 'required'],
            [['old_password'], 'checkPwd'],
            [['repassword'], 'required'],
            [['password'], 'string', 'min' => 6],
            [['repassword'], 'compare', 'compareAttribute' => 'password', 'message' => '确认密码必须与新密码一致'],
            [['username', 'user_no'], 'unique'],
            [['gender', 'status', 'sort'], 'integer'],
            [['username'], 'string', 'max' => 20],
            [['realname'], 'string', 'max' => 40],
            [['email'], 'email'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'user_id' => 'ID',
            'username' => '用户名',
            'user_no' => '工号',
            'role' => '角色(可多选)',
            'depts' => '所属部门(可多选)',
            'password' => '新密码',
            'repassword' => '确认密码',
            'old_password' => '旧密码',
            'realname' => '真实姓名',
            'gender' => '性别',
            'email' => '邮箱',
            'status' => '状态',
            'phone' => '手机',
            'avatar' => '头像',
            'qq' => 'QQ',
            'remark' => '备注',
            'last_login_ip' => '最后登陆ip地址',
            'last_login_time' => '最后登陆时间',
            'create_time' => '创建时间',
        ];
    }

    /**
     * @inheritdoc
     */
    public function fields()
    {
        $fields = parent::fields();

        // remove fields that contain sensitive information
        unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);

        return $fields;
    }

    /**
     * 获取关联用户信息
     */
    public function getInfo()
    {
        return $this->hasOne(UserInfo::className(), ['user_id' => 'user_id']);
    }

    /**
     * info的setter方法
     */
    public function setInfo($value)
    {
        $this->info = $value;
    }

    /**
     * 获取关联部门
     */
    public function getDept()
    {
        return $this->hasMany(Department::className(), ['dept_id' => 'dept_id'])
                ->viaTable('{{%user_department}}', ['user_id' => 'user_id'])->asArray();
    }

    /**
     * 验证旧密码是否正确
     *
     * @param type $attribute
     */
    public function checkPwd()
    {
        if (!$this->validatePassword($this->old_password)) {
            $this->addError('old_password', '旧密码错误！');
        }
        if ($this->validatePassword($this->password)) {
            $this->addError('password', '新密码与旧密码完全相同！');
        }
    }

    /**
     * 公共搜索所需信息
     */
    public function search()
    {
        return [
            'username' => '用户名',
            'realname' => '真实姓名',
            'email' => '邮箱',
        ];
    }

    /**
     * 获取安全的字段
     */
    public static function getSafeFields()
    {
        $columns = User::getTableSchema()->columns;
        unset($columns['auth_key'], $columns['password_hash'], $columns['password_reset_token']);
        //$columns = array_keys(User::getTableSchema()->columns);
        $columns = array_keys($columns);
        return $columns;
    }

    /**
     * 返回性别列表 
     */
    public static function getGenderList()
    {
        return [
            self::GENDER_MALE => '男',
            self::GENDER_FEMALE => '女',
        ];
    }

    /**
     * 返回状态列表
     */
    public static function getStatusList()
    {
        return [
            self::STATUS_ENABLE => '启用',
            self::STATUS_DISABLE => '禁用',
        ];
    }

    /**
     * 获取下拉列表
     *
     * @param string $typeModule 使用模块
     */
    public static function getDropDownList($typeModule = '')
    {
        $rows = self::find()
            ->select('realname,username, user_id')
            ->orderBy(['user_id' => SORT_ASC])
            ->asArray()
            ->all();
        foreach ($rows as $row) {
            if ($typeModule == 'verify') {
                $reimAccess = 'reimbursement/reimbursement/verify';
                $appliAccess = 'reimbursement/application/verify';
                if (Auth::checkAccessById($reimAccess, $row['user_id']) || Auth::checkAccessById($appliAccess, $row['user_id'])) {
                    $list[$row['user_id']] = $row['realname']."[".$row['username']."]";
                }
            } else {
                $list[$row['user_id']] = $row['realname']."[".$row['username']."]";   
            }
        }
        return $list;
    }

    /**
     * 根据以","相隔的用户id获取部门名称
     *
     * @param type $deptStr
     * @return string
     */
    public static function getUserByDot($str)
    {
        $cache = Yii::$app->cache;
        if (empty($str)) {
            return '';
        }
        $ids = (is_array($str)) ? $str : explode(',', $str);

        $list = $cache->get('user_list');
        $user = [];

        foreach ($ids as $id) {
            if (array_key_exists($id, $list)) {
                $user[] = $list[$id];
            }
        }

        return $user;
    }

    /**
     * 获取用户列表
     * 
     * @return type
     */
    public static function getList()
    {
        //$cache = Yii::$app->cache;

        $list = self::getQuery()->all();
        $list = self::getExtraInfo($list);

        return $list;
    }

    /**
     * 取得分页用户
     *
     * @return array 返回一个数组，data里包含分页用户，pagination包含分页对象
     */
    public static function getListByPage($map = null, $pageSize = 15)
    {
        $query = self::getQuery($map);
        $provider = new ActiveDataProvider([
            'query' => $query,
            'pagination' => [
                'pageSize' => $pageSize,
            ],
        ]);
        $data = $provider->getModels();
        $data = self::getExtraInfo($data);
        $list['data'] = $data;
        $list['pagination'] = $provider->pagination;
        return $list;
    }

    /**
     * 按照部门取得用户
     *
     * @author HuLimin <http://blog.u-bo.com>
     *
     * @param int $deptId 部门id
     * @param bool $tree 部门是否获取数结构
     * @param string $typeModule 使用模块
     * @return array 
     */
    public static function getListByDepartment($where = [], $deptId = 0, $tree = false, $typeModule = '')
    {
        $query = new Query();
        $depts = Department::getList('*', '', [], null, 'dept_id', $tree);

        if ($deptId) {
            $temp = (array_key_exists($deptId, $depts)) ? [$deptId => $depts[$deptId]] : [];
            $depts = $temp;
        }
        foreach ($depts as &$dept) {
            $users = [];
            $user_ids = $query->select('user_id')->from('{{%user_department}}')->where(['in', 'dept_id', explode(',', $dept['child_ids'])])->column();
            $newMap = array_merge($where, [['in', 'u.user_id', $user_ids]]);

            $users = self::getQuery($newMap)->all();

            if (!empty($typeModule)) {
                switch ($typeModule) {
                    case 'reim':
                        $checkAccess = 'reimbursement/reimbursement/verify';
                        break;
                    case 'appli':
                        $checkAccess = 'reimbursement/application/verify';
                        break;
                }
                if (!empty($checkAccess)) {
                    foreach ($users as $key => $value) {
                        if (!Auth::checkAccessById($checkAccess, $value['user_id'])) {
                            unset($users[$key]);
                        }
                    }
                }
            }
            $dept['user'] = $users;
        }
        return $depts;
    }

    /**
     * 按照部门取得用户(部门可多选)
     *
     * @author Strief <http://blog.u-bo.com>
     *
     * @param string $deptId 部门ids(类似于'1,2,3')
     * @param bool $tree 部门是否获取数结构
     * @param string $typeModule 使用模块
     * @return array 
     */
    public static function getListByMultDept($where = [], $deptId = '', $tree = false, $typeModule = '')
    {
        $query = new Query();
        $depts = Department::getList('*', '', [], null, 'dept_id', $tree);

        if ($deptId != '') {
            $deptIds = explode(',', $deptId);
            foreach ($deptIds as $key => $value) {
                (array_key_exists((int)$value, $depts)) && $temp[(int)$value] = $depts[(int)$value];
            }
            $depts = $temp;
        }
        foreach ($depts as &$dept) {
            $users = [];
            $user_ids = $query->select('user_id')->from('{{%user_department}}')->where(['in', 'dept_id', explode(',', $dept['child_ids'])])->column();
            $newMap = array_merge($where, [['in', 'u.user_id', $user_ids]]);

            $users = self::getQuery($newMap)->all();

            if (!empty($typeModule)) {
                switch ($typeModule) {
                    case 'reim':
                        $checkAccess = 'reimbursement/reimbursement/verify';
                        break;
                    case 'appli':
                        $checkAccess = 'reimbursement/application/verify';
                        break;
                }
                if (!empty($checkAccess)) {
                    foreach ($users as $key => $value) {
                        if (!Auth::checkAccessById($checkAccess, $value['user_id'])) {
                            unset($users[$key]);
                        }
                    }
                }
            }
            
            $dept['user'] = $users;
        }
        return $depts;
    }

    /**
     * 按照角色取出所有人
     */
    public static function getListByRole($where = [])
    {
        $authManager = Yii::$app->authManager;
        $data = [];
        $roles = Auth::getRoleTree();
        $roles = ArrayHelper::index($roles, 'name');
        //\common\components\Tool::dump($roles);

        $query = new Query();
        foreach ($roles as $role) {
            $userIds = $query
                ->select('user_id')
                ->from($authManager->assignmentTable)
                ->where(['item_name' => $role['name']])
                ->column();
            $users = self::find()
                ->where(['user_id' => $userIds])
                ->indexBy('user_id')
                ->asArray()
                ->all();
            $data[$role['name']]['users'] = $users;
            $data[$role['name']]['child_users'] = [];
        }
        foreach($data as $role => $d) {
            if (!empty($roles[$role]['childs'])) {
                $childs = $roles[$role]['childs'];
                foreach ($childs as $child) {
                    $data[$role]['child_users'] = array_merge($data[$role]['child_users'], $data[$child]['users']);
                }
                if (!empty($data[$role]['child_users'])) {
                    $data[$role]['child_users'] = ArrayHelper::index($data[$role]['child_users'], 'user_id');
                }
            }
        }
        //\common\components\Tool::dump($data);
        return $data;
    }

    /**
     * 获取用户所属部门的所有用户
     *
     * @param int $userId 用户的id
     */
    public static function getScopeDeptUsers($userId)
    {
        $users = [];
        $cache = Yii::$app->cache;
        $user_list = $cache->get('user_list');
        $user_department_list = $cache->get('user_department_list');
//        \common\components\Tool::dump($user_department_list);die;
        if (isset($user_list[$userId])) {
            $user = $user_list[$userId];
            if (!empty($user['depts'])) {
                $depts = array_keys($user['depts']);
                foreach ($depts as $dept) {
                    if (isset($user_department_list[$dept]) && isset($user_department_list[$dept]['user'])) {
                        $users = array_merge($users, array_keys($user_department_list[$dept]['user']));
                    }
                }
            }
            else {
                throw new ErrorException('获取用户所属部门下用户错误，用户没有所属部门！');
            }
            return $users;
        }
        else{
            throw new ErrorException('获取用户所属部门下用户错误，没有此用户！');
        }
    }

    /**
     * 获取用户所属角色下的所有用户
     *
     * @param int $userId 用户的id
     */
    public static function getScopeRoleUsers($userId)
    {
        $users = [];
        $cache = Yii::$app->cache;
        $user_list = $cache->get('user_list');
        $user_role_list = $cache->get('user_role_list');
        if (isset($user_list[$userId])) {
            $user = $user_list[$userId];
            if (!empty($user['roles'])) {
                $roles = $user['roles'];
                foreach ($roles as $role) {
                    if (isset($user_role_list[$role]) && isset($user_role_list[$role]['child_users'])) {
                        $users = array_merge($users, array_keys($user_role_list[$role]['child_users']));
                    }
                }
            }
            else {
                throw new ErrorException('获取用户所属角色下用户错误，用户没有所属角色！');
            }
            return $users;
        }
        else{
            throw new ErrorException('获取用户所属角色下用户错误，没有此用户！');
        }
    }

    /**
     *列举部门和用户
     *
     * @author HuLimin <http://blog.u-bo.com>
     * 
     * @param type $where
     * @param int $deptId 部门id
     * @return type
     */
    public static function getDepartmentAndUser($where = [], $deptId = 0)
    {
        $query = new Query();
        $depts = Department::getList('*', $where, [], null, null, false);
        
        foreach ($depts as &$dept) {
            $userArr=[];
            $user_ids = $query->select('user_id')->from('{{%user_department}}')->where(['in', 'dept_id', explode(',', $dept['child_ids'])])->column();
            $user_ids2 = $query->select('user_id')->from('{{%user_department}}')->where(['dept_id' => $dept['dept_id']])->column();
//    
            $userArr = self::getQuery([['in', 'u.user_id', $user_ids]])->andWhere(['not in', 'u.user_id', [1]])->all();
            $dept['user_ids'] = implode(',', \yii\helpers\ArrayHelper::getColumn($userArr, 'user_id'));
            $dept['user'] = self::getQuery([['in', 'u.user_id', $user_ids2]])->andWhere(['not in', 'u.user_id', [1]])->all();
        }
        return $depts;
    }

    /**
     * 获取用户通用的查询对象
     *
     * @return type
     */
    private static function getQuery($map = null)
    {
        $query = (new \yii\db\Query())
            ->select('u.user_id,u.user_no,u.username,u.realname,u.gender,u.email,u.avatar,u.status,i.phone,i.qq,i.remark')
            ->from('{{%user}} u')
            ->leftJoin('{{%user_info}} i', 'i.user_id = u.user_id')
            //->leftJoin('{{%department}} d', 'd.dept_id = u.dept_id')
            ->where('u.status=:status', [':status' => self::STATUS_ENABLE])
            ->orderBy(['u.user_id' => SORT_ASC])
            ->indexBy('user_id');

        if ($map !== null && is_array($map)) {
            foreach ($map as $m) {
                $query->andWhere($m);
            }
        }
        return $query;
    }

    /**
     * 获取额外的信息
     * 
     * @return type
     */
    private static function getExtraInfo($list)
    {
        $list = self::addDept($list);
        $list = self::addRole($list);
        return $list;
    }

    /**
     * 获取用户的部门列表
     * 
     * @param type $list
     * @return type
     */
    private static function addDept($list)
    {
        $query = new \yii\db\Query();
        $deptList = Department::getList();
        foreach ($list as &$user) {
            $depts = [];
            $deptArr = $query->select('dept_id')->from('{{%user_department}}')->where(['user_id' => $user['user_id']])->column();
            foreach ($deptArr as $deptId) {
                array_key_exists($deptId, $deptList) && $depts[$deptId] = $deptList[$deptId]['name'];
            }
            $user['depts'] = $depts;
        }
        return $list;
    }

    /**
     * 获取用户的角色
     *
     * @param type $list
     * @return type
     */
    private static function addRole($list)
    {
        foreach ($list as &$user) {
            $user['roles'] = array_keys(\qcbase\models\Auth::getRolesByUser($user['user_id']));
        }
        return $list;
    }

    /**
     * 设置缓存
     */
    public static function setCache()
    {
        $cache = Yii::$app->cache;

        //设置用户列表缓存
        $userList = self::getList();
        $cache->set('user_list', $userList);

        //设置根据部门划分的用户缓存
        $userDeptList = self::getListByDepartment();
        $cache->set('user_department_list', $userDeptList);

        //设置根据角色划分的用户缓存
        $userRoleList = self::getListByRole();
        $cache->set('user_role_list', $userRoleList);
    }

    /**
     * @inheritdoc
     */
    public function beforeSave($insert)
    {
        if (parent::beforeSave($insert)) {
            if ($this->scenario == 'insert') {
                //生成hash加密密码
                $this->setPassword($this->password);
                //生成auth_key
                $this->generateAuthKey();
                $this->create_time = time();
            } elseif ($this->scenario == 'update') {

                !empty($this->password) && $this->setPassword($this->password);
            } elseif ($this->scenario == 'repwd') {

                !empty($this->password) && $this->setPassword($this->password);
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * @inheritdoc
     */
    public function afterSave($insert, $changedAttributes)
    {
        parent::afterSave($insert, $changedAttributes);

        //重新分配角色

        if (in_array($this->scenario, ['insert', 'update'])) {
            Auth::assignRole($this->user_id, $this->role, $insert);
            //修改部门
            $this->modifyDept($this->scenario, $this->user_id, $this->depts);
        }
        $infoModel = new UserInfo;
        if ($insert) {
            $infoModel->load(Yii::$app->request->post());
            $infoModel->user_id = $this->user_id;
            $infoModel->save();
        } else {
            $infoModel = UserInfo::findOne($this->user_id);
            $infoModel->load(Yii::$app->request->post());
            $infoModel->save();
        }

        //生成头像缩略图
        if (isset($changedAttributes['avatar']) && !empty($changedAttributes['avatar'])) {
            $web = Yii::getAlias('@web');
            $avatarPath = './avatar/avatar_' . md5($this->user_id) . '_small.jpg';
            Image::thumb2('.' . str_replace($web, '', $this->avatar), $avatarPath, '', 50, 50);
            Yii::$app->db->createCommand()->update(self::tableName(), ['avatar' => $web . trim($avatarPath, '.')], 'user_id = :user_id', [':user_id' => $this->user_id])->execute();
            Yii::$app->session->set('user_avatar', $avatarPath);
        }
        
        \qcbase\models\ActionLog::insertLog('home','修改',json_encode($changedAttributes));
    }

    /**
     * @inheritdoc
     */
    public function afterDelete()
    {
        parent::afterDelete();
        $infoModel = UserInfo::findOne($this->user_id);
        $infoModel->delete();
    }

    /**
     * 修改部门
     *
     * @param type $scenario
     * @param type $userId
     * @param type $depts
     */
    public function modifyDept($scenario, $userId, $depts)
    {
        $command = \Yii::$app->db->createCommand();
        $scenario == 'update' && $command->delete('{{%user_department}}', 'user_id = :user_id', [':user_id' => $userId])->execute();
        $data = [];
        foreach ($depts as $dept) {
            $data[] = [$userId, $dept];
        }
        $command->batchInsert('{{%user_department}}', ['user_id', 'dept_id'], $data)->execute();
    }

    /**
     * user组件afterLogin所绑定的事件,登录后更新与登录相关的字段
     *
     * @param class $event  UserEvent事件类
     */
    public static function updateLoginLog(\yii\base\Event $event)
    {
        if (!empty($event->identity)) {
            $identity = $event->identity;
            Yii::$app->db->createCommand()
                ->update('{{%user}}', [
                    'last_login_ip' => Yii::$app->getRequest()->getUserIP(),
                    'last_login_time' => time(),
                    ], 'user_id = :id', [':id' => $identity->user_id])
                ->execute();
            //判断是否是超级管理员
            $session = Yii::$app->session;
            $roles = Auth::getRolesByUser($identity->user_id);
            if (array_key_exists(Yii::$app->params['auth']['superAdminRole'], $roles)) {
                $session->set('isSuperAdmin', true);
            }
            $session->set('user_roles', array_keys($roles));
            $session->set('user_avatar', (!empty($identity->avatar)) ? $identity->avatar : Yii::getAlias('@web') . '/static/images/headpic.jpg');
        }
    }

    /**
     * @inheritdoc
     */
    public static function findIdentity($id)
    {
        return static::findOne(['user_id' => $id, 'status' => self::STATUS_ENABLE]);
    }

    /**
     * @inheritdoc
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
    }

    /**
     * 根据用户名查找用户
     *
     * @param string $username
     * @return static|null
     */
    public static function findByUsername($username)
    {
        return static::findOne(['username' => $username, 'status' => self::STATUS_ENABLE]);
    }

    /**
     * 根据用户ID查找用户
     *
     * @param string $id
     * @return static|null
     */
    public static function findById($id)
    {
        return static::findOne(['user_id' => $id]);
    }

    /**
     * 根据重置密码密钥查找用户
     *
     * @param string $token password reset token
     * @return static|null
     */
    public static function findByPasswordResetToken($token)
    {
        if (!static::isPasswordResetTokenValid($token)) {
            return null;
        }

        return static::findOne([
                'password_reset_token' => $token,
                'status' => self::STATUS_ENABLE,
        ]);
    }

    /**
     * 验证重置密钥是否合法
     *
     * @param string $token password reset token
     * @return boolean
     */
    public static function isPasswordResetTokenValid($token)
    {
        if (empty($token)) {
            return false;
        }
        $expire = Yii::$app->params['user.passwordResetTokenExpire'];
        $parts = explode('_', $token);
        $timestamp = (int) end($parts);
        return $timestamp + $expire >= time();
    }

    /**
     * @inheritdoc
     */
    public function getId()
    {
        return $this->getPrimaryKey();
    }

    /**
     * @inheritdoc
     */
    public function getAuthKey()
    {
        return $this->auth_key;
    }

    /**
     * @inheritdoc
     */
    public function validateAuthKey($authKey)
    {
        return $this->getAuthKey() === $authKey;
    }

    /**
     * Validates password  验证当前输入密码与数据库密码的比较
     *
     * @param string $password password to validate
     * @return boolean if password provided is valid for current user
     */
    public function validatePassword($password)
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }

    /**
     * Generates password hash from password and sets it to the model
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password_hash = Yii::$app->security->generatePasswordHash($password);
    }

    /**
     * Generates "remember me" authentication key
     */
    public function generateAuthKey()
    {
        $this->auth_key = Yii::$app->security->generateRandomString();
    }

    /**
     * Generates new password reset token
     */
    public function generatePasswordResetToken()
    {
        $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
    }

    /**
     * Removes password reset token
     */
    public function removePasswordResetToken()
    {
        $this->password_reset_token = null;
    }
}
