<?php

/**
 * 会话信息
 * @author benzhan
 */
class Session extends Model {
    protected $tableName = 'session';
    protected $dbKey = 'dw_chat';

    const MSG_TYPE_TEXT = 0;
    const MSG_TYPE_IMAGE = 1;
    const MSG_TYPE_VIDEO = 2;
    const MSG_TYPE_AUDIO = 3;
    const MSG_TYPE_REDPACK = 4;

    const LAST_MSG_NUM_HASH = 'globals:last_msg_num_hash';
    const LAST_MSG_HASH = 'globals:last_msg_hash';

    /**
     * 填充会话列表
     * @param string $user_id 用户id
     * @param array $list 会话列表
     * @param int   $currentGroupId 当前群id
     *
     * @return mixed
     */
    public function fillSession($user_id, $list, $currentGroupId) {
        $group_ids = [];
        $user_ids = [];
        foreach ($list as $item) {
            if ($item['is_group']) {
                $group_ids[] = $item['session_id'];
            } else {
                // 把自己的用户名换掉
                $user_ids[] = str_replace([$user_id, '-'], ['', ''], $item['session_id']);
            }
        }

        $objUserInfo = new TableHelper('user_info', 'dw_chat');
        $_field = 'user_id, user_name, nick_name, cover_photo';
        $users = $objUserInfo->getAll(['user_id' => $user_ids], compact('_field'));
        $users = arrayFormatKey($users, 'user_id');

        $objGroupInfo = new TableHelper('group_info', 'dw_chat');
        $_field = 'group_id, group_name, group_title, cover_photo';
        $groups = $objGroupInfo->getAll(['group_id' => $group_ids], compact('_field'));
        $groups = arrayFormatKey($groups, 'group_id');

        $userGroups = [];
        if ($currentGroupId) {
            $objUserGroup = new UserGroup();
            $userGroups = $objUserGroup->objTable->getAll(['user_id' => $user_ids, 'group_id' => $currentGroupId], ['_field' => 'user_id, group_id']);
            $userGroups = arrayFormatKey($userGroups, 'user_id', 'group_id');
        }

        $objRedis = dwRedis::init(Eos::REDIS_SERV);
        foreach ($list as $i => $item) {
            if ($item['is_group']) {
                $group = $groups[$item['session_id']];
                $item['name'] = $group['group_title'] ?: $group['group_name'];
                $item['cover_photo'] = $group['cover_photo'];
            } else {
                // 把自己的用户名换掉
                $key = str_replace([$user_id, '-'], ['', ''], $item['session_id']);
                $user = $users[$key];
                $item['name'] = $user['nick_name'];
                $item['cover_photo'] = $user['cover_photo'];
                $item['in_group'] = $userGroups[$key] ? 1 : 0; // 已经在群
            }

            $lastNum = self::getLastMsgNum($item['session_id']);
            $item['unread'] = max($lastNum - $item['read_num'], 0);
            $item['cover_photo'] = coverReplaceImage($item['cover_photo']);
            $item['last_msg'] = self::getLastMsg($item['session_id'], $objRedis);

            $list[$i] = $item;
        }

        return $list;
    }


    public function getMsgList($session_id, $read_hash, $load_type, $is_group) {
        $where2 = [];
        if ($is_group) {
            $_field = '`hash`, `from`, `msg`, `msg_type`, `state`, create_time_int, msg_num';
            $objMsg = new TableHelper('group_msg', 'dw_chat');

            $where2['group_id'] = $session_id;
        } else {
            $_field = '`hash`, `from`, `to`, `msg`, `msg_type`, `state`, create_time_int, msg_num';
            $objMsg = new TableHelper('person_msg', 'dw_chat');

            $where2['session_id'] = $session_id;
        }

        if ($read_hash) {
            $currentRow = $objMsg->getRow(['hash' => $read_hash], compact('_field'));
            $create_time_int = (int) $currentRow['create_time_int'];
        } else {
            $create_time_int = 0;
        }


        $_limit = 100;
        $maxNum = 1000;
        $need_clear = false;
        if ($load_type == 0) {
            // 正常加载
            if ($create_time_int > 0) {
                $_limit = $maxNum;

                // 需要判断是否存在过多未读消息
                $_where = "create_time_int > {$create_time_int}";
                $num = $objMsg->getCount($where2, compact('_where'));
                if ($num > $maxNum) {
                    // 这种情况抛弃create_time_int...，消息太多了，只拿最新的1000条记录
                    $create_time_int = 0;
                    $need_clear = true;
                }
            }

            $create_time_int && $_where = "create_time_int > {$create_time_int}";
            $_sortKey = "create_time_int DESC";
            $keyWord2 = compact('_where', '_sortKey', '_limit', '_field');
            $list = $objMsg->getAll($where2, $keyWord2);
        } else {
            // 加载历史记录
            $_where = "create_time_int < {$create_time_int}";
            $_sortKey = "create_time_int DESC";
            $keyWord3 = compact('_where', '_sortKey', '_limit', '_field');
            $list = $objMsg->getAll($where2, $keyWord3);
        }

        $list = array_reverse($list);
        $list = $this->appendExtInfo($list);

        $user_ids = array_column($list, 'from');
        if (!$is_group) {
            $to_ids = array_column($list, 'to');
            $user_ids = array_merge($user_ids, $to_ids);
        }

        $user_ids = array_unique($user_ids);
        $userMap = $this->getUserMap($user_ids);

        return compact('userMap', 'list', 'need_clear');
    }

    /**
     * 额外信息
     * @author solu
     * @param $list
     * @return mixed
     */
    private function appendExtInfo($list) {
        $userId = User::getUserId();
        $objRedpackLog = new RedpackLog();

        $trxIds = array_map(function($v) {
            $msg = Utils::decodeRC4($v['msg']);
            $data = json_decode($msg, true);
            return $data['trxId'];
        }, array_filter($list, function($v) {
            return $v['msg_type'] == self::MSG_TYPE_REDPACK;
        }));
        $redpack = [];
        if ($trxIds) {
            $objRedpack = new Redpack();
            $redpack = $objRedpack->objTable->getAll(['transfer_trx_id' => $trxIds], ['_field' => 'transfer_trx_id, status']);
            $redpack = arrayFormatKey($redpack, 'transfer_trx_id', 'status');
        }

        foreach ($list as $k => $v) {
            if ($v['msg_type'] == self::MSG_TYPE_REDPACK) {
                $msg = Utils::decodeRC4($v['msg']);
                $data = json_decode($msg, true);
                $trxId = $data['trxId'];

                $v['ext']['grabbed'] = $objRedpackLog->userGrabbed($userId, $trxId);
                $v['ext']['redpack_status'] = intval($redpack[$trxId]);
            }

            $list[$k] = $v;
        }

        return $list;
    }

    public function getUserMap($user_ids) {
        $objUserInfo = new TableHelper('user_info', 'dw_chat');
        $datas = $objUserInfo->getAll(['user_id' => $user_ids], ['_field' => 'user_id, user_name, nick_name, cover_photo']);
        coverReplaceArrImage($datas, 'cover_photo');
        return arrayFormatKey($datas, 'user_id');
    }

    /**
     * 修改状态
     * @param $session_id
     * @param $newData
     * @param $ext;
     */
    public function updateState($session_id, $newData, $ext = []) {
        $where = compact('session_id');
        $where = array_merge($where, $ext);
        $newData['update_time'] = NOW;
        $newData['update_time_int'] = microtime(true) * 1000;

        $this->objTable->updateObject($newData, $where);
    }

    /**
     * 检测私聊session
     * @author solu
     * @param $from
     * @param $sessionId
     * @return array
     * @throws Exception
     */
    public function checkPersonSession($from, $sessionId) {
        $uids = explode('-', $sessionId);
        $uids = array_filter($uids, function ($v) {return $v > 0;});

//        if (count($uids) != 2 || !$this->objTable->getRow(['user_id' => $from, 'session_id' => $sessionId])) {
//            throw new Exception('session_id error', CODE_PARAM_ERROR);
//        }

        if (!in_array($from, $uids)) {
            throw new Exception('user not in session', CODE_NO_PERMITION);
        }

        $to = 0;
        foreach ($uids as $uid) {
            if ($uid != $from) {
                $to = $uid;
                break;
            }
        }

        return [$from, $to];
    }

    /**
     * 检测群session
     * @author solu
     * @param $from
     * @param $sessionId
     * @return bool
     * @throws Exception
     */
    public function checkGroupSession($from, $sessionId) {
        if (!$this->objTable->getRow(['user_id' => $from, 'session_id' => $sessionId])) {
            throw new Exception('user not in session', CODE_NO_PERMITION);
        }

        return true;
    }

    public static function getPersonSessionId($from, $to) {
        if ($from > $to) {
            return "{$to}-{$from}";
        } else {
            return "{$from}-{$to}";
        }
    }

    private function initPersonSession($from, $to) {
        $session_id = self::getPersonSessionId($from, $to);
        $num = $this->objTable->getCount(compact('session_id'));
        if ($num < 2) { // 单方面删除会话的情况
            // 插入双方的session
            $datas = [[
                    'user_id' => $from,
                    'session_id' => $session_id,
                    'is_group' => 0,
                ], [
                    'user_id' => $to,
                    'session_id' => $session_id,
                    'is_group' => 0,
                ]
            ];

            // 第一次初始化，需要初始化
            $this->objTable->addObjectsIfNoExist($datas);
        }
    }

    /**
     * 发送私聊消息
     * @author solu
     * @param $from
     * @param $sessionId
     * @param $msg_type
     * @param $msg
     * @param $noEvent
     * @return array
     * @throws Exception
     */
    public function sendPersonMsg($from, $sessionId, $msg_type, $msg, $noEvent = false) {
        list($from, $to) = $this->checkPersonSession($from, $sessionId);

        $this->initPersonSession($from, $to);

        $t = self::getMS();
        0 == $msg_type && $msg = htmlentities($msg);
        $lastNum = self::incrLastMsgNum($sessionId);
        $data = [
            'session_id' => $sessionId,
            'from' => intval($from),
            'to' => intval($to),
            'msg_type' => $msg_type,
            'msg' => $msg,
            'create_time' => NOW,
            'create_time_int' => $t,
            'msg_num' => $lastNum,
        ];

        $data['hash'] = self::_genHash($data);
        $objPersonMsg = new TableHelper('person_msg', 'dw_chat');
        if (!$objPersonMsg->addObject($data)) {
            throw new Exception('send message error', CODE_NORMAL_ERROR);
        }

        // 更新发送人已读序号
        $this->updateState($sessionId, ['read_num' => $lastNum], ['user_id' => $from]);

        $name = User::getUserNameById($from);
        $content = self::_msgHandle($msg, $msg_type);
        $eventData = [
            'type' => 'msg',
            'from' => $from,
            'to' => strval($to),
            'name' => $name,
            'content' => $content,
            'hash' => $data['hash'],
            'timestamp' => $t,
        ];
        !$noEvent && ThirdApi::pushPersonEvent($to, $eventData);

        self::setLastMsg($sessionId, $msg_type, $content, $from, $name);
        $this->updateState($sessionId, []);

        $eventData['content'] = $msg;
        return $eventData;
    }

    /**
     * 发送群聊消息
     * @author solu
     * @param $from
     * @param $groupId
     * @param $msg_type
     * @param $msg
     * @param $noEvent
     * @return array
     * @throws Exception
     */
    public function sendGroupMsg($from, $groupId, $msg_type, $msg, $noEvent = false) {
        $userMap = null;
        if (!$this->objTable->getRow(['user_id' => $from, 'session_id' => $groupId])) {
            // 聊天就自动加入群
            $objGroup = new GroupInfo();
            $objGroup->joinGroup($from, $groupId);

            // 第一次发言，需要返回用户信息
            $userMap = $this->getUserMap($from);
        } else if ((new UserGroup())->isBlock($groupId, $from)) {
            throw new Exception('Banned', CODE_NORMAL_ERROR);
        }

        $t = self::getMS();
        0 == $msg_type && $msg = htmlentities($msg);
        $lastNum = self::incrLastMsgNum($groupId);
        $data = [
            'group_id' => intval($groupId),
            'from' => $from,
            'msg_type' => $msg_type,
            'msg' => $msg,
            'create_time' => NOW,
            'create_time_int' => $t,
            'msg_num' => $lastNum,
        ];
        $data['hash'] = self::_genHash($data);
        $objGroupMsg = new TableHelper('group_msg', 'dw_chat');
        if (!$objGroupMsg->addObject($data)) {
            throw new Exception('send message error', CODE_NORMAL_ERROR);
        }

        // 更新发送人已读序号
        $this->updateState($groupId, ['read_num' => $lastNum], ['user_id' => $from]);

        $content = self::_msgHandle($msg, $msg_type);
        $name = GroupInfo::getGroupNameById($groupId);
        $eventData = [
            'type' => 'msg',
            'msg_type' => $msg_type,
            'from' => $from,
            'name' => $name,
            'content' => $content,
            'hash' => $data['hash'],
            'timestamp' => $t,
        ];
        !$noEvent && ThirdApi::pushGroupEvent($groupId, $eventData);

        self::setLastMsg($groupId, $msg_type, $content, $from, $name);

        $eventData['content'] = $msg;
        $eventData['userMap'] = $userMap;
        return $eventData;
    }

    private static function _genHash($data) {
        return md5(json_encode($data));
    }

    public static function _msgHandle($content, $msg_type, $len = 16) {
        if ($msg_type > 0) { // 只处理文本
            return $content;
        }
        $source = Utils::decodeRC4($content);
        !$source && $source = $content;
        if (mb_strlen($source) > $len) {
            $source = mb_substr($source, 0, $len);
        }

        return Utils::encodeRC4($source);
    }

    public static function getMS() {
        return intval(microtime(true) * 1000);
    }

    /**
     * 自增session num
     * @author solu
     * @param $sessionId
     * @return int
     */
    public static function incrLastMsgNum($sessionId) {
        $objRedis = dwRedis::init(Eos::REDIS_SERV);

        // 存在原子性问题
        if ($objRedis->hExists(self::LAST_MSG_NUM_HASH, $sessionId)) {
            return $objRedis->hIncrBy(self::LAST_MSG_NUM_HASH, $sessionId, 1);
        }

        if (is_numeric($sessionId)) { // 群组
            $objGroupMsg = new GroupMsg();
            $c = $objGroupMsg->objTable->getCount(['group_id' => $sessionId]);
        } else {
            $objPersonMsg = new PersonMsg();
            $c = $objPersonMsg->objTable->getCount(['session_id' => $sessionId]);
        }
        $c += 1;
        return $objRedis->hIncrBy(self::LAST_MSG_NUM_HASH, $sessionId, $c);
    }

    /**
     * 获取session最新num
     * @author solu
     * @param $sessionId
     * @param null $objRedis
     * @return int
     */
    public static function getLastMsgNum($sessionId, $objRedis = null) {
        !$objRedis && $objRedis = dwRedis::init(Eos::REDIS_SERV);

        return $objRedis->hGet(self::LAST_MSG_NUM_HASH, $sessionId) ?: 0;
    }

    public static function setLastMsg($sessionId, $msgType, $content, $from, $name) {
        $objRedis = dwRedis::init(Eos::REDIS_SERV);
        $msgType != self::MSG_TYPE_TEXT && $content = '';
        $data = [
            'msg_type' => $msgType,
            'content' => $content,
            'from' => $from,
            'name' => $name,
        ];

        $objRedis->hSet(self::LAST_MSG_HASH, $sessionId, json_encode($data));
    }

    public static function getLastMsg($sessionId, $objRedis = null) {
        !$objRedis && $objRedis = dwRedis::init(Eos::REDIS_SERV);

        if ($objRedis->hExists(self::LAST_MSG_HASH, $sessionId)) {
            $json = $objRedis->hGet(self::LAST_MSG_HASH, $sessionId);
            return json_decode($json, true);
        }

        $isGroup = is_numeric($sessionId);
        if ($isGroup) { // 群组
            $objGroupMsg = new GroupMsg();
            $row = $objGroupMsg->objTable->getRow(['group_id' => $sessionId]);
        } else {
            $objPersonMsg = new PersonMsg();
            $row = $objPersonMsg->objTable->getRow(['session_id' => $sessionId]);
        }

        $data = null;
        if ($row) {
            $msg_type = $row['msg_type'];
            $content = '';
            $from = $row['from'];
            if ($msg_type == self::MSG_TYPE_TEXT) {
                $content = self::_msgHandle($row['msg'], $msg_type);
            }

            if ($isGroup) {
                $name = GroupInfo::getGroupNameById($row['group_id']);
            } else {
                $name = User::getUserNameById($from);
            }

            $data = compact('msg_type', 'content', 'from', 'name');
            $objRedis->hSet(self::LAST_MSG_HASH, $sessionId, json_encode($data));
        }

        return $data;
    }
}