<?php

/**
 * 群组相关
 * @author solu
 */
class GroupInfo extends Model {
    protected $tableName = 'group_info';
    protected $dbKey = 'dw_chat';

    const OFFICIAL_ID = 10000; // 官方群id
    const MAX_ADMIN_COUNT = 6; // 最多管理员数量
    const REDIS_GROUP_ID_HASH = 'globals:group_id_hash';

    /**
     * 加入群组
     * @author solu
     * @param $userId
     * @param $groupId
     * @param $group
     * @return bool
     * @throws Exception
     */
    public function joinGroup($userId, $groupId, $group = []) {
        $where = ['group_id' => $groupId];
        !$group && $group = $this->objTable->getRow($where);

        if (!$group) {
            throw new Exception('group not exists', CODE_PARAM_ERROR);
        }

        $objUserGroup = new TableHelper('user_group', 'dw_chat');
        $row = $objUserGroup->getRow(['user_id' => $userId, 'group_id' => $groupId, 'state' => UserGroup::STATE_IN_GROUP]);
        if (!$row) {
            $data = [
                'user_id' => $userId,
                'group_id' => $groupId,
                'join_time' => NOW,
                'state' => UserGroup::STATE_IN_GROUP,
            ];

            if (!$objUserGroup->replaceObject($data)) {
                throw new Exception('join fail', CODE_DB_ERROR);
            }

            $memberNum = $group['member_num'] + 1;
            $where['member_num'] = $group['member_num'];
            if (!$this->objTable->updateObject(['member_num' => $memberNum], $where)) {
                throw new Exception('update group info fail', CODE_DB_ERROR);
            }
            
            // 给自己发消息订阅新群组
            ThirdApi::pushPersonEvent($userId, [
                'type' => 'join_group',
                'group_id' => $groupId,
            ]);

            $user_info = User::getUserInfoById($userId);
            // 给群组发消息有人加入了
            ThirdApi::pushGroupEvent($groupId, [
                'type' => 'join',
                'group_id' => $groupId,
                'user_info' => $user_info,
            ]);
        }

        $objSession = new Session();
        $where = [
            'user_id' => $userId,
            'session_id' => $groupId,
        ];

        // 有可能删除了会话
        if (!$objSession->objTable->getCount($where)) {
            $sessData = $where;
            $sessData['is_group'] = 1;
            $objSession->objTable->replaceObject($sessData);
        }

        return true;
    }

    /**
     * 加入群组
     * @author solu
     * @param $userId
     * @param $groupId
     * @param $group
     * @param $memberNum
     * @return bool
     * @throws Exception
     */
    public function appendToGroup($userIds, $groupId, $memberNum = 0) {
        $userGroupDatas = [];
        $sessDatas = [];

        foreach ($userIds as $userId) {
            $userGroupDatas[] = [
                'user_id' => $userId,
                'group_id' => $groupId,
                'join_time' => NOW,
                'state' => UserGroup::STATE_IN_GROUP,
            ];

            $sessDatas[] = [
                'user_id' => $userId,
                'session_id' => $groupId,
                'is_group' => 1,
            ];
        }
        $objUserGroup = new UserGroup();
        $objUserGroup->objTable->replaceObjects2($userGroupDatas);

        $objSession = new Session();
        $objSession->objTable->replaceObjects2($sessDatas);

        $where = ['group_id' => $groupId];
        $newData = [
            'member_num' => count($userIds) + $memberNum,
            'group_name' => $groupId,
        ];
        $this->objTable->updateObject($newData, $where);
    }

    /**
     * 离开群组
     * @author solu
     * @param $userId
     * @param $groupId
     * @param $group
     * @return bool
     * @throws Exception
     */
    public function leaveGroup($userId, $groupId, $group = []) {
        $groupId = (int) $groupId;
        $where = ['group_id' => $groupId];
        !$group && $group = $this->objTable->getRow($where);

        if (!$group) {
            throw new Exception('group not exists', CODE_PARAM_ERROR);
        }

        $objUserGroup = new UserGroup();
        if (!$objUserGroup->inGroup($groupId, $userId)) {
            throw new Exception('not in group', CODE_PARAM_ERROR);
        }

        $groupUserIds = $objUserGroup->getUserIdListSortByAdminAndJoinTime($groupId);
        $isCreator = $this->isCreator($groupId, $userId);

        $objSession = new Session();
        $objUserGroup->objTable->autoCommit(false);

        $ugWhere = [
            'user_id' => $userId,
            'group_id' => $groupId,
        ];
        if (!$objUserGroup->objTable->updateObject(['state' => UserGroup::STATE_LEAVE, 'is_admin' => 0], $ugWhere)) {
            throw new Exception('leave fail', CODE_DB_ERROR);
        }

        $creatorField = '';
        if (count($groupUserIds) == 1) { // 最后一个人离开，去掉群主，群解散
            $creatorField = ' , creator=0';
        } elseif ($isCreator) { // 群主离开, 群主顺移给下一位
            $newCreator = 0;
            for ($i = 0; $i < count($groupUserIds); $i++) {
                if ($groupUserIds[$i] == $userId) {
                    continue;
                }
                $newCreator = $groupUserIds[$i];
                break;
            }
            $creatorField = " , creator={$newCreator}";

            // 新群主设置成管理员
            $objUserGroup->setData($groupId, $newCreator, ['is_admin' => 1]);
        }

        $sql = "UPDATE group_info SET member_num = member_num - 1 {$creatorField} WHERE group_id = {$groupId}";
        $this->objDb->update($sql);

        $sessWhere = [
            'user_id' => $userId,
            'session_id' => $groupId,
        ];
        if (!$objSession->objTable->delObject($sessWhere)) {
            throw new Exception('delete session error', CODE_DB_ERROR);
        }

        $objUserGroup->objTable->commit();

        // 给自己发消息订阅新群组
        ThirdApi::pushPersonEvent($userId, [
            'type' => 'leave_group',
            'group_id' => $groupId,
        ]);

        $user_info = User::getUserInfoById($userId);

        // 给群组发消息有人离开了
        ThirdApi::pushGroupEvent($groupId, [
            'type' => 'leave',
            'group_id' => $groupId,
            'user_id' => $userId,
            'user_info' => $user_info,
        ]);

        return true;
    }

    /**
     * 更新信息
     * @author solu
     * @param $userId
     * @param $groupId
     * @param array $data
     * @return bool|int
     * @throws Exception
     */
    public function setData($userId, $groupId, array $data) {
        if (!(new UserGroup())->isAdmin($groupId, $userId)) {
            throw new Exception('no permission', CODE_NO_PERMITION);
        }

        $data['update_time'] = NOW;
        return $this->objTable->updateObject($data, ['group_id' => $groupId]);
    }

    /**
     * 分享连接
     * @author solu
     * @param string $name
     * @return string
     */
    public static function genInviteUrl($name) {
        if (!$name) {
            return '';
        }

        return sprintf('%s/s/%s', URL_SELF, $name);
    }

    /**
     * 是否群主
     * @author solu
     * @param $groupId
     * @param $userId
     * @return bool
     */
    public function isCreator($groupId, $userId) {
        $creator = $this->objTable->getOne(['group_id' => $groupId], ['_field' => 'creator']);

        return intval($creator) == $userId;
    }

    /**
     * 设置群管理
     * @author solu
     * @param $groupId
     * @param $creator
     * @param $userIds
     * @return bool
     * @throws Exception
     */
    public function addAdmin($groupId, $creator, $userIds) {
        if (!$this->isCreator($groupId, $creator)) {
            throw new Exception('no permission', CODE_NO_PERMITION);
        }

        $objUserGroup = new UserGroup();
        $c = $objUserGroup->objTable->getCount(['group_id' => $groupId, 'is_admin' => 1]);
        $c += count($userIds);
        if ($c > self::MAX_ADMIN_COUNT) {
            throw new Exception('too many admins', CODE_NORMAL_ERROR);
        }

        if (!$objUserGroup->setData($groupId, $userIds, ['is_admin' => 1])) {
            throw new Exception('set admin fail', CODE_NORMAL_ERROR);
        }

        return true;
    }

    /**
     * 移除管理员
     * @author solu
     * @param $groupId
     * @param $creator
     * @param $userId
     * @return bool
     * @throws Exception
     */
    public function removeAdmin($groupId, $creator, $userId) {
        if (!$this->isCreator($groupId, $creator)) {
            throw new Exception('no permission', CODE_NO_PERMITION);
        }

        if ($userId == $creator) {
            throw new Exception('can not remove creator', CODE_NORMAL_ERROR);
        }

        $objUserGroup = new UserGroup();
        if (!$objUserGroup->setData($groupId, $userId, ['is_admin' => 0])) {
            throw new Exception('set admin fail', CODE_NORMAL_ERROR);
        }

        return true;
    }

    /**
     * 搜索群用户信息
     * @author solu
     * @param $groupId
     * @param $keyword
     * @return array
     */
    public function memberSearch($groupId, $keyword) {
        $objUserGroup = new UserGroup();
        $userIds = $objUserGroup->objTable->getCol(['group_id' => $groupId], ['_field' => 'user_id']);

        if (!$userIds) {
            return [];
        }

        $objUserInfo = new TableHelper('user_info', 'dw_chat');
        // $keyword 是外部传来的参数，会有注入攻击
        $keyword = $objUserInfo->escape($keyword);
        $userInfo = $objUserInfo->getAll(['user_id' => $userIds], [
            '_where' => "(user_name like '{$keyword}%' || nick_name like '{$keyword}%')",
            '_field' => 'user_id, user_name, nick_name, cover_photo',
            '_sortKey' => 'last_login_time DESC',
//            '_debug' => true,
        ]);
        coverReplaceArrImage($userInfo, 'cover_photo');

        return $userInfo;
    }

    /**
     * 获取用户名
     * @author solu
     * @param $groupId
     * @return string
     */
    public static function getGroupNameById($groupId) {
        $objRedis = dwRedis::init();
        $userName = $objRedis->hGet(self::REDIS_GROUP_ID_HASH, $groupId);
        if (!$userName) {
            $obj = new GroupInfo();
            $userName = $obj->objTable->getOne(['group_id' => $groupId], ['_field' => 'group_title']);
            if ($userName) {
                self::setGroupNameById($groupId, $userName, $objRedis);
            }
        }

        return $userName;
    }

    public static function setGroupNameById($groupId, $user_name, $objRedis = null) {
        !$objRedis && $objRedis = dwRedis::init();
        $objRedis->hSet(self::REDIS_GROUP_ID_HASH, $groupId, $user_name);
    }

    /**
     * 转移群主
     * @author solu
     * @param $groupId
     * @param $old
     * @param $new
     * @return bool
     * @throws Exception
     */
    public function changeCreator($groupId, $old, $new) {
        if (!$this->isCreator($groupId, $old)) {
            throw new Exception('no permission', CODE_NO_PERMITION);
        }

        if ($old == $new) {
            throw new Exception('same creator', CODE_NORMAL_ERROR);
        }

        $this->objTable->updateObject(['creator' => $new], ['group_id' => $groupId]);
        (new UserGroup())->setData($groupId, $old, ['is_admin' => 0]);

        return true;
    }

    /**
     * 获取客服id
     * @author solu
     * @param $userIds
     * @return array
     */
    public function getGroupServerFromUserIds($userIds) {
        $items = $this->objTable->getAll(['server_id' => $userIds], ['_field' => 'group_id, server_id']);
        return arrayFormatKey($items, 'server_id', 'group_id');
    }

    public function checkPermission($group_id, $user_id) {
        $_field = 'is_public';
        $objGroupInfo = new GroupInfo();
        $is_public = (int) $objGroupInfo->objTable->getOne(['group_id' => $group_id], compact('_field'));
        if (!$is_public) {
            // 私有群要判断用户是否存在
            if ($user_id) {
                $objUserGroup = new UserGroup();
                $state = 1;
                $count = $objUserGroup->objTable->getCount(compact('group_id', 'user_id', 'state'));
                if (!$count) {
                    Response::error(CODE_NO_PERMITION, 'the group is private');
                }
            } else {
                Response::error(CODE_NO_PERMITION, 'the group is private');
            }
        }
    }

    public function getGroupIdFromTgGroupId($tgGroupId) {
        $groupId = $this->objTable->getOne(['tg_group_id' => $tgGroupId], ['_field' => 'group_id']);
        return intval($groupId);
    }
}