<?php
/**
 * 好友功能
 * @date 2022-3-30
 * @author JZW
 **/

namespace Game\Logic\Friend;

use Framework\Define\ErrorDefine;
use Framework\Log\LogMark;
use Framework\MVC\ModelManager;
use Framework\Lib\Utils;
use Framework\Logic\TemplateHelp;
use Framework\Network\SendMessage;
use Game\Constant\ClientErrorCode;
use Game\Constant\ConstTemplate\TemplateConst;
use Game\Constant\ConstTemplate\TemplateIntimacy;
use Game\Constant\ConstTemplate\TemplateItem;
use Game\Constant\ConstTemplate\TemplateLevel;
use Game\Constant\EventTypeDefine;
use Game\Constant\GameConstantDefine;
use Game\Constant\GameErrorCode;
use Game\Constant\ModelTypeDefine;
use Game\Constant\TemplateDefine;
use Game\Data\AccountData;
use Game\Data\Friends\Friend;
use Game\Data\Friends\FriendIntimacyApplyData;
use Game\Data\Friends\FriendsData;
use Game\Data\Friends\FriendsLastGameData;
use Game\Data\NoticeData;
use Game\Data\ProficiencyData;
use Game\Data\RoleExtData;
use Game\Logic\AccountLogic;
use Game\Logic\EventConditionLogic;
use Game\Logic\GenerateAiLogic;
use Game\Logic\ItemLogic;
use Game\Logic\MailLogic;
use Game\Logic\NoticeLogic;
use Game\Logic\PaiweiLevelLogic;
use Game\Logic\RankLogic;
use Game\Logic\ShieldWordsLogic;
use Game\Logic\Task\FriendIntimacyRewardLogic;
use Game\Model\AccountModel;
use Game\Model\Friend\FriendBlackModel;
use Game\Model\Friend\FriendIntimacyApplyModel;
use Game\Model\Friend\FriendIntimacyMarkModel;
use Game\Model\Friend\FriendIntimacyModel;
use Game\Model\Friend\FriendLastGameModel;
use Game\Model\Friend\FriendModel;
use Game\Model\Friend\FriendRandomModel;
use Game\Model\Friend\FriendRequestModel;
use Game\Model\Friend\StrangerBlackModel;
use Game\Model\Rank\RankModel;
use Game\Protobuf\FriendRole;
use Game\Protobuf\FriendRoleExt;
use Game\Protobuf\GCFriendDeleteNotice;
use Game\Protobuf\GCSyncFriendInfo;
use Game\Protobuf\LastGame;
use Game\Protobuf\LastGamePlayer;
use Game\Protobuf\PacketId;

trait FriendLogic
{
    use TemplateHelp;
    use NoticeLogic;
    use EventConditionLogic;
    use AccountLogic;
    use GenerateAiLogic;
    use ItemLogic;
    use MailLogic;
    use PaiweiLevelLogic;
    use FriendIntimacyRewardLogic;
    use ShieldWordsLogic;
    use RankLogic;

    //申请好友
    public function apply(int $playerId, $respondentPlayerId, $source, &$code): bool
    {
        //检查是否是AI
        if ($this->checkIsAIPlayer($respondentPlayerId)) {
            $code = ClientErrorCode::ERROR_FRIEND_REJECT_REQUEST_ERROR;
            return false;
        }
        //检测被申请人是否允许添加好友
        /**
         * @var AccountModel $account
         */
        $account = ModelManager::getInstance()->getModel(ModelTypeDefine::ACCOUNT);
        if ($account->search($respondentPlayerId, $targetPlayerInfo)) {
            //1 拒绝申请
            if ((int)$targetPlayerInfo[RoleExtData::DB_FRIEND_REJECT]) {
                $code = ClientErrorCode::ERROR_FRIEND_REJECT_REQUEST_ERROR;
                return false;
            }
        } else {
            $code = ClientErrorCode::ERROR_FRIEND_REJECT_REQUEST_ERROR;
            return false;
        }
        //检查自己好友数量上限
        if (!$this->verifyMyFriendNum($code)) {
            return false;
        }

        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($respondentPlayerId);
        if ($friend->checkFriend()) {
            $code = ClientErrorCode::ERROR_FRIEND_EXISTS_ERROR;
            return false;
        }
        //检查是否被对方拉黑
        /**
         * @var FriendBlackModel $black
         */
        $black = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_BLACK);
        $black->setPlayerId($respondentPlayerId);
        $black->setRespondentPlayerId($playerId);
        if ($black->checkInBlack()) {
            //被拉黑,不给对方发消息
            $code = ClientErrorCode::ERROR_FRIEND_REJECT_REQUEST_ERROR;
            return false;
        }
        //发送申请通知,离线不通知
        if ($targetPlayerInfo[RoleExtData::DB_Status] != GameConstantDefine::PLAYER_STATUS_OFFLINE) {
            $this->applyNotice($playerId, $respondentPlayerId, NoticeData::Notice_Index_Apply_Friend, ["Source" => $source]);
        }

        /**
         * @var FriendRequestModel $request
         */
        $request = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_REQUEST);
        $request->setRespondentPlayerId($respondentPlayerId);
        $request->applyForFriends();
        $code = ErrorDefine::SUCCESS;
        return true;
    }

    //邀请游戏
    public function applyGame(int $playerId, $respondentPlayerId, $roomId, $gameType, $source, &$code, int $roomType): bool
    {
        //检查对方是否在线
        /**
         * @var AccountModel $account
         */
        $account = ModelManager::getInstance()->getModel(ModelTypeDefine::ACCOUNT);
        if ($account->search($respondentPlayerId, $playerInfo)) {
            //检查状态
            switch ($playerInfo[RoleExtData::DB_Status])
            {
                case GameConstantDefine::PLAYER_STATUS_OFFLINE:
                    $code = ClientErrorCode::ERROR_NOTICE_PLAYER_OFFLINE;
                    return false;
                case GameConstantDefine::PLAYER_STATUS_GAMING:
                case GameConstantDefine::PLAYER_STATUS_MATCH:
                    $code = ClientErrorCode::ERROR_FRIEND_STATUS_ERROR;
                    return false;
            }
        } else {
            $code = ClientErrorCode::ERROR_NOTICE_PLAYER_OFFLINE;
            return false;
        }

        //检查是否已被对方拉黑
        /**
         * @var FriendBlackModel $black
         */
        $black = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_BLACK);
        $black->setPlayerId($respondentPlayerId);
        $black->setRespondentPlayerId($playerId);
        if ($black->checkInBlack()) {
            //被拉黑,不给对方发消息
            $code = ClientErrorCode::CLIENT_SUCCESS;
            return true;
        }
        //检查对方段位
        if (in_array(
            $gameType,
            [GameConstantDefine::MATCH_MODE_SINGLE_PAI_WEI, GameConstantDefine::MATCH_MODE_TEAM_PAI_WEI]
        )) {
            $myLevel = $this->getPaiweiLevel()->levelId;
            $targetLevel = $this->getTargetPaiWeiLevel($respondentPlayerId)->levelId;
            //段位间隔
            $gapLevel = $this->getTerm(TemplateDefine::TYPE_CONST,
                                       TemplateConst::Const_Ranking_Room_Level_Gap,
                                       TemplateConst::ConstNum);
            if ($targetLevel > $myLevel + $gapLevel) {
                $code = ClientErrorCode::PVP_RANKING_INVITED_FRIEND_LEVEL_OVER_MATE;
                return true;
            }
            if ($targetLevel < $myLevel - $gapLevel) {
                $code = ClientErrorCode::PVP_RANKING_INVITED_FRIEND_LEVEL_LESS_MATE;
                return true;
            }
        }
        //检查对方消息数量
        $num = $this->getNoticeNum($respondentPlayerId, NoticeData::Notice_Index_Invited_Game);
        $configNum = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Friend_Online_Request_Num,
            TemplateConst::ConstNum
        );
        if ($num >= $configNum) {
            //超过上限 直接拒绝邀请
            $data = [
                "Time" => Utils::getServerTimestamp(),
                "TipsId" => NoticeData::Tips_Refuse_Invited_Game_Language_Id,
            ];
            $this->tipsNotice($playerId, NoticeData::Notice_Index_Tips, $data);
            $code = ClientErrorCode::CLIENT_SUCCESS;
            return true;
        }
        $param = [
            "Source" => $source,
            "GameType" => $gameType,
            "RoomId" => $roomId,
            "RoomType" => $roomType,
        ];
        $code = ClientErrorCode::CLIENT_SUCCESS;
        return $this->applyNotice($playerId, $respondentPlayerId, NoticeData::Notice_Index_Invited_Game, $param);
    }

    //同意申请
    public function agree(int $playerId, int $respondentPlayerId, int &$code): bool
    {
        /**
         * @var FriendRequestModel $request
         */
        $request = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_REQUEST);
        $request->setRespondentPlayerId($respondentPlayerId);
        if (!$request->delApply()) {
            return false;
        }

        if (!$this->checkFriendNum($respondentPlayerId, $code)) {
            return false;
        }
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($respondentPlayerId);
        if (!$friend->addFriend()) {
            $code = ClientErrorCode::ERROR_FRIEND_ADD_ERROR;
            return false;
        }
        $code = ClientErrorCode::CLIENT_SUCCESS;
        //同步好友信息
        $this->syncFriendInfo($playerId, $respondentPlayerId);
        //通知对方
        $this->syncFriendInfo($respondentPlayerId, $playerId, true);
        return true;
    }

    //检测我的好友数量是否已到上限
    private function verifyMyFriendNum(&$code): bool
    {
        $tpl_FriendNum = $this->getTerm(TemplateDefine::TYPE_CONST, TemplateConst::Const_Friend_Number, "Const_num");
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        if ($friend->getMineNumber() >= $tpl_FriendNum) {
            $code = ClientErrorCode::ERROR_FRIEND_LIST_MAX_ERROR;
            return false;
        }
        return true;
    }

    //检测我的和好友数量是否已到上限
    private function checkFriendNum($respondentPlayerId, &$code): bool
    {
        $tpl_FriendNum = $this->getTerm(TemplateDefine::TYPE_CONST, TemplateConst::Const_Friend_Number, "Const_num");
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        if ($friend->getMineNumber() >= $tpl_FriendNum) {
            $code = ClientErrorCode::ERROR_FRIEND_LIST_MAX_ERROR;
            return false;
        }
        $tmp = $friend->playerId;
        $friend->setPlayerId($respondentPlayerId);
        if ($friend->getMineNumber() >= $tpl_FriendNum) {
            $code = ClientErrorCode::ERROR_Other_FRIEND_LIST_MAX_ERROR;
            return false;
        }
        $friend->setPlayerId($tmp);
        return true;
    }

    //拒绝申请
    public function refuse($respondentPlayerId, &$code = 0)
    {
        $request = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_REQUEST);
        $request->setRespondentPlayerId($respondentPlayerId);
        if ($request->delApply()) {
            $code = 0;
            return false;
        }
        $code = ErrorDefine::SUCCESS;
        return true;
    }

    //一键忽略所有请求
    public function refuseAll(): bool
    {
        /**
         * @var FriendRequestModel $request
         */
        $request = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_REQUEST);
        foreach ($request->searchList() as $playerId => $t) {
            $this->refuse($playerId);
        }
        return true;
    }

    //获取我的好友列表
    public function getMineList(int $page): array
    {
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        return $this->getFriendInfo(
            $friend->getMineList(),
            Friend::SortTypeLastInFirstOut,
            $page,
            Friend::SourceMineList
        );
    }

    //获取我的好友申请列表
    public function getMineApplyList(int $page): array
    {
        /**
         * @var FriendRequestModel $request
         */
        $request = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_REQUEST);
        return $this->getFriendInfo(
            $request->searchList(),
            Friend::SortTypeLastInFirstOut,
            $page,
            Friend::SourceApplyList
        );
    }

    //精确搜索列表
    public function getPreciseSearchList($searchKey): array
    {
        // 昵称,roleId
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        return $this->getFriendInfo(
            $friend->preciseSearch($searchKey, $this->getBlackIdList()),
            Friend::SortTypeSearch,
            1,
            Friend::SourceSearch
        );
    }

    //随机搜索列表
    public function getRandomSearchList($male, $female, $cLevel): array
    {
        $playerIds = [];
        //选择男
        if ($male && !$this->getRandomSearchListByGender(1, $cLevel, $playerIds)) {
            return array([], []);
        }
        if ($female && !$this->getRandomSearchListByGender(0, $cLevel, $playerIds)) {
            return array([], []);
        }
        # 筛选好友

        //去掉我的好友,黑名单
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $mine = $friend->getMinePlayerIdList();
        $blackList = $this->getBlackIdList();
        //$playerIds pid=>[空]
        foreach ($playerIds as $id => $array) {
            if (array_key_exists($id, $mine) || array_key_exists($id, $blackList)) {
                unset($playerIds[$id]);
                continue;
            }
            $playerIds[$id][FriendsData::IS_FRIEND] = false;
        }

        //去除自己的信息
        unset($playerIds[$friend->playerId]);

        //每页人数
        $tplLNum = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Friend_List_Number,
            TemplateConst::ConstNum);

        //筛选数据
        if (count($playerIds) > $tplLNum) {
            //随机玩家
            $playerIds_tmp = array_flip(array_rand($playerIds, $tplLNum));
            $tmp = [];
            foreach ($playerIds_tmp as $id => $i) {
                $tmp[$id] = $playerIds[$id];
            }
            $playerIds = $tmp;
        }
        return $this->getFriendInfo(
            $playerIds,
            Friend::SortTypeSearch,
            1,
            Friend::SourceSearch
        );
    }

    private function getRandomSearchListByGender(int $gender, int $cLevel, array &$playerIds): bool
    {
        /**
         * @var FriendRandomModel $random
         */
        $random = ModelManager::getInstance()->getModel(ModelTypeDefine::COMMON_FRIEND);
        //解析Level为具体的等级为最小值到最大值
        $minScore = Utils::makeFriendsScore($gender, TemplateLevel::LEVEL_MAP[$cLevel][0]);
        $maxScore = Utils::makeFriendsScore($gender, TemplateLevel::LEVEL_MAP[$cLevel][1]);
        if (!$random->searchData($minScore, $maxScore, $result)) {
            return false;
        }
        foreach ($result as $id => $val) {
            if ($id == 0) {
                continue;
            }
            $playerIds[$id] = [];
        }
        return true;
    }

    //删除好友
    public function del($respondentPlayerId, &$num, &$code)
    {
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($respondentPlayerId);
        if (!$friend->delMineFriend()) {
            $code = ClientErrorCode::ERROR_FRIEND_DELETE_ERROR;
            return false;
        }
        //删除亲密度记录
        /**
         * @var FriendIntimacyMarkModel $friendIntimacyMarkModel
         */
        $friendIntimacyMarkModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY_MARK);
        $friendIntimacyMarkModel->setRespondentPlayerId($respondentPlayerId);
        $friendIntimacyMarkModel->delIntimacyMark();
        //删除亲密申请
        /**
         * @var FriendIntimacyApplyModel $friendIntimacyApplyModel
         */
        $friendIntimacyApplyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY_APPLY);
        $friendIntimacyApplyModel->setRespondentPlayerId($respondentPlayerId);
        $applyId = $friendIntimacyApplyModel->getMyApplyId();
        if ($applyId > 0) {
            $friendIntimacyApplyModel->delApplyInfo(
                $friendIntimacyApplyModel->playerId,
                $respondentPlayerId,
                $applyId
            );
        }
        //删除亲密关系
        /**
         * @var FriendIntimacyModel $friendIntimacyModel
         */
        $friendIntimacyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY);
        $friendIntimacyModel->setRespondentPlayerId($respondentPlayerId);
        $friendIntimacyModel->delIntimacyId();
        //检查关闭任务
        $this->checkCloseFriendIntimacyReward($friend->playerId);
        $this->checkCloseFriendIntimacyReward($respondentPlayerId);
        $num = $friend->getMineNumber();
        //通知被删除好友
        $message = new GCFriendDeleteNotice();
        $message->setFromPlayerId($friend->playerId);
        $this->addPacket(PacketId::GC_FriendDeleteNotice, $message, $respondentPlayerId);
        return true;
    }

    //黑名单列表
    public function getBlackList(int $page): array
    {
        return $this->getFriendInfo(
            $this->getBlackIdList(),
            Friend::SortTypeLastInFirstOut,
            $page,
            Friend::SourceBlackList);
    }

    //黑名单列表Id
    //return [pid=>time,]
    public function getBlackIdList(): array
    {
        /**
         * @var FriendBlackModel $black
         */
        $black = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_BLACK);
        /**
         * @var StrangerBlackModel $stranger
         */
        $stranger = ModelManager::getInstance()->getModel(ModelTypeDefine::STRANGER_BLACK);
        return $black->searchList() + $stranger->searchList();
    }

    //加入黑名单
    public function addBlack($respondentPlayerId): int
    {
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($respondentPlayerId);
        if ($friend->checkFriend()) {
            /**
             * @var FriendBlackModel $black
             */
            $black = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_BLACK);
            $black->setRespondentPlayerId($respondentPlayerId);
            if (!$black->addBlack()) {
                return ClientErrorCode::ERROR_FRIEND_ADD_BLACK_FAIL;
            }
            return ClientErrorCode::CLIENT_SUCCESS;
        }
        //拉黑陌生人
        /**
         * @var StrangerBlackModel $stranger
         */
        $stranger = ModelManager::getInstance()->getModel(ModelTypeDefine::STRANGER_BLACK);
        $stranger->setRespondentPlayerId($respondentPlayerId);
        $stranger->addBlack();
        return ClientErrorCode::CLIENT_SUCCESS;
    }

    //恢复黑名单
    public function resetBlack($respondentPlayerId, &$code)
    {
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($respondentPlayerId);
        if ($friend->checkFriend()) {
            /**
             * @var FriendBlackModel $black
             */
            $black = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_BLACK);
            $black->setRespondentPlayerId($respondentPlayerId);
            if (!$black->delBlack($code)) {
                return false;
            }
            $code = ClientErrorCode::CLIENT_SUCCESS;
            //同步好友信息
            $this->syncFriendInfo($friend->playerId, $respondentPlayerId);
            return true;
        }
        //检查是否在好友黑名单中
        /**
         * @var FriendBlackModel $black
         */
        $black = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_BLACK);
        $black->setRespondentPlayerId($respondentPlayerId);
        if ($black->checkInBlack()) {
            $black->delBlack($ret);
            $code = ClientErrorCode::CLIENT_SUCCESS;
            return true;
        }
        /**
         * @var StrangerBlackModel $stranger
         */
        $stranger = ModelManager::getInstance()->getModel(ModelTypeDefine::STRANGER_BLACK);
        $stranger->setRespondentPlayerId($respondentPlayerId);
        $stranger->delBlack($ret);
        $code = ClientErrorCode::CLIENT_SUCCESS;
        return true;
    }

    //点赞
    public function goodFriend($respondentPlayerId): int
    {
        /**
         * @var RankModel $rankModel
         */
        $rankModel = ModelManager::getInstance()->getModel(ModelTypeDefine::RANKING);
        if(!$rankModel->rankLike($respondentPlayerId)) {
            //检查点赞
            return ClientErrorCode::ERROR_FRIEND_LIKES_SAVE_ERROR;
        }
        // 触发任务
        $param = array(
            GameConstantDefine::EVENT_KEY_NUM => 1,  //点赞次数
            GameConstantDefine::EVENT_KEY_LIKE_FROM => GameConstantDefine::EVENT_VALUE_LIKE_FRIEND
        );
        $this->triggerEvent(EventTypeDefine::EVENT_TYPE_TASK_GIVE_LIKE, $param);
        //点赞 增加亲密度
        $point = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Add_Point_Like,
            TemplateConst::ConstNum);
        $this->addFriendIntimacy($respondentPlayerId, $point, GameConstantDefine::INTIMACY_SOURCE_LIKE);
        return ClientErrorCode::CLIENT_SUCCESS;
    }

    //更新备注
    public function remark($respondentPlayerId, $remarkName, &$ret, &$code): bool
    {
        //检查敏感词
        if ($this->checkFuckWord($remarkName)) {
            $code = ClientErrorCode::FRIEND_REMARK_HAS_SHIELD_WORD;
            return false;
        }
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($respondentPlayerId);
        //检查是否是好友
        if ($friend->checkFriend()) {
            if (!$friend->upRemark($remarkName, $code)) {
                return false;
            }
            $ret = $this->getFriendInfo(
                [$respondentPlayerId => ["remark" => $remarkName]],
                Friend::SortTypeSearch,
                1,
                Friend::SourceUpMark
            );
            $code = ClientErrorCode::CLIENT_SUCCESS;
            return true;
        }
        $code = ClientErrorCode::ERROR_TARGET_PLAYER_IS_NOT_FRIEND;
        return false;
    }

    //获取最近比赛列表
    public function getLastGamingList(&$ret): bool
    {
        $ret = [];
        /**
         * @var FriendLastGameModel $lastGame
         */
        $lastGame = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_LAST_GAME);
        $list = $lastGame->getLastList();
        if (count($list) > 0) {
            foreach ($list as $gameId => $data) {
                //过滤掉数据不完全的记录
                if (!isset($data[FriendsLastGameData::DB_Ext]) || $data[FriendsLastGameData::DB_Ext] == "") {
                    continue;
                }
                $lastGame = new LastGame();
                $lastGame->setGameType((int)$data[FriendsLastGameData::DB_GameType]);
                $lastGame->setMapId((int)$data[FriendsLastGameData::DB_MapId]);
                $lastGame->setTime((int)$data[FriendsLastGameData::DB_Time]);
                $playerInfo = [];
                foreach ($data[FriendsLastGameData::DB_Ext] as $playerId => $game) {
                    if (is_null($game)) {
                        continue;
                    }
                    $player = new LastGamePlayer();
                    $player->setRanking((int)$game["Ranking"]);
                    $player->setNickName($game[AccountData::DB_NICK_NAME]);
                    $player->setHead((int)$game[AccountData::DB_HEAD]);
                    $player->setLevel((int)$game[AccountData::DB_LEVEL]);
                    $player->setGender((int)$game[AccountData::DB_GENDER]);
                    $player->setDan((int)$game["Dan"]);
                    $player->setIsFriend($this->checkTargetPlayerIsMyFriend($playerId));
                    $player->setPlayerId((int)$playerId);
                    $playerInfo[] = $player;
                }
                $lastGame->setPlayer($playerInfo);
                $ret[] = $lastGame;
            }
        }
        return true;
    }

    //检查是否是自己好友
    public function checkTargetPlayerIsMyFriend(int $respondentPlayerId): bool
    {
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($respondentPlayerId);
        return $friend->checkFriend();
    }

    public function getFriendInfo($playerIds, $sortType, $page, $source): array
    {
        $good = array();
        $this->getRankLike($good);
        $roleArr = array();
        $roleExtArr = array();
        $playerIds = $this->friendSort($playerIds, $sortType, $page, $source);
        foreach ($playerIds as $id => $rInfo) {
            if ($id === 0) {
                continue;
            }
            $paiWei = $this->getTargetPaiWeiLevel($id);

            $roleData = new FriendRole();

            $roleData->setLevel((int)$rInfo[AccountData::DB_LEVEL]);
            $roleData->setPlayerId((int)$id);
            $roleData->setHead((int)$rInfo[AccountData::DB_HEAD]);
            $roleData->setGender((int)$rInfo[AccountData::DB_GENDER]);
            $roleData->setNickName($rInfo[AccountData::DB_NICK_NAME]);
            $roleData->setMember((int)$rInfo[RoleExtData::DB_MEMBER]);
            $roleData->setDan((int)$paiWei->levelId);
            $roleData->setRoleId((int)$rInfo[AccountData::DB_RoleId]);
            $roleData->setProficiencyLv((int)$rInfo[ProficiencyData::PROFICIENCY_LV]);
            $roleData->setIntimacy($rInfo[FriendsData::INTIMACY] ?? 0);
            $roleData->setCDTime($rInfo[FriendsData::CD_TIME] ?? 0);
            $roleData->setIntimacyIcon($rInfo[FriendsData::INTIMACY_ICON] ?? 0);
            $roleData->setIntimacyCreateTime($rInfo[FriendsData::INTIMACY_CREATE_TIME] ?? 0);
            $roleData->setIntimacyId($rInfo[FriendsData::INTIMACY_ID] ?? 0);

            $roleExtData = new FriendRoleExt();
            $roleExtData->setPlayerId((int)$id);
            $roleExtData->setIndex((int)$rInfo["Index"]);
            $roleExtData->setOnline((int)$rInfo[RoleExtData::DB_Status]);
            $roleExtData->setRemark($rInfo[FriendsData::DB_REMARK] ?? "");
            $roleExtData->setOffLineTime((int)($rInfo["OfflineTime"]));
            $roleExtData->setIsFriend((bool)($rInfo["IsFriend"] ?? false));
            $roleExtData->setIsBlack($rInfo[FriendsData::IS_BLACK] ?? false);
            $roleExtData->setGood(in_array($id, $good));

            $roleArr[] = $roleData;
            $roleExtArr[] = $roleExtData;
        }
        return [$roleArr, $roleExtArr];
    }

    public function friendSort(array $playerIdList, $sortType, $page, $source): array
    {
        $rule = "friendRule$sortType";
        $ret = [];
        if (method_exists($this, $rule)){
            return $this->$rule($playerIdList, $page, $source);
        }
        return $ret;
    }

    //获取好友显示信息
    private function getFriendShowData(int $playerId): array
    {
        $param = [
            AccountData::DB_LOGIN_TIME,
            AccountData::DB_LOGOUT_TIME,
            AccountData::DB_LEVEL,
            ProficiencyData::PROFICIENCY_LV,
            AccountData::DB_RoleId,
            AccountData::DB_NICK_NAME,
            AccountData::DB_HEAD,
            AccountData::DB_GENDER,
            RoleExtData::DB_MEMBER,
            RoleExtData::DB_Status,
            AccountData::DB_STEP,
        ];
        return $this->getAccountPropertyArr($playerId, $param);
    }

    //搜索好友数据排序--Friend.php->SortTypeSearch
    //在线：按照上线时间排序（后上先排）
    //离线：按照离线时间排序（后离先排）
    //先获取玩家的在线状态 查询登录时间
    //获取玩家的离线时间 按离线时间排列
    private function friendRule1($playerIdList, $page, $source): array
    {
        $ret = array();
        //排序队列
        $onLinePlayer = array();
        $offLinePlayer = array();
        foreach ($playerIdList as $Id => $attr) {
            if ($Id == 0){
                continue;
            }
            $rInfo = $this->getFriendShowData($Id);
            //排除未完成创建角色的
            if ($rInfo[AccountData::DB_STEP] < GameConstantDefine::STEP_COMPLETE) {
                continue;
            }
            //查找在线/离线玩家
            if ($rInfo[RoleExtData::DB_Status] == GameConstantDefine::PLAYER_STATUS_OFFLINE) {
                //离线
                $offLinePlayer[$Id] = $rInfo[AccountData::DB_LOGIN_TIME];
            } else {
                //在线不用排序
                $onLinePlayer[$Id] = 1;
            }
            if (!is_array($attr)){
                $ret[$Id] = $rInfo;
            }else{
                $ret[$Id] = array_merge($attr, $rInfo);
            }
        }
        //按离线时间排序
        asort($offLinePlayer);
        $index = 1;
        //更新索引
        foreach ($onLinePlayer as $Id => $time){
            $ret[$Id]["Index"] = $index++;
        }
        foreach ($offLinePlayer as $Id => $time){
            $ret[$Id]["Index"] = $index++;
        }
        return $ret;
    }
    //好友申请列表--Friend.php->SortTypeLastInFirstOut
    //黑名单列表
    //我的好友列表
    private function friendRule2($playerList, $page, $source): array
    {
        //黑名单
        $blackList = $this->getBlackIdList();
        //排序队列
        $onLinePlayer = array();
        $offLinePlayer = array();
        $playerData = array();
        $ret = [];
        foreach ($playerList as $pId => $content){
            if ($pId == 0){
                continue;
            }
            $rInfo = $this->getFriendShowData($pId);
            if ($rInfo[RoleExtData::DB_Status] == GameConstantDefine::PLAYER_STATUS_OFFLINE) {
                //离线
                $offLinePlayer[$pId] = $rInfo[AccountData::DB_LOGIN_TIME];
            } else {
                //在线 不排序
                $onLinePlayer[$pId] = 1;
            }
            //整理显示数据
            $rInfo[FriendsData::IS_BLACK] = array_key_exists($pId, $blackList);
            $rInfo[FriendsData::DB_REMARK] = $content[FriendsData::DB_REMARK] ?? "";
            $rInfo[FriendsData::INTIMACY] = $content[FriendsData::INTIMACY] ?? 0;
            $rInfo[FriendsData::CD_TIME] = $content[FriendsData::CD_TIME] ?? 0;
            $rInfo[FriendsData::INTIMACY_ID] = $content[FriendsData::INTIMACY_ID] ?? 0;
            $rInfo[FriendsData::INTIMACY_CREATE_TIME] = $content[FriendsData::INTIMACY_CREATE_TIME] ?? 0;
            $rInfo[FriendsData::INTIMACY_ICON] = $content[FriendsData::INTIMACY_ICON] ?? 0;
            $playerData[$pId] = $rInfo;
        }
        //按离线时间排序
        arsort($offLinePlayer);

        //每页刷新数量
        $tplNum = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Friend_List_Number,
            TemplateConst::ConstNum);
        //分页
        $maxNum = $page * $tplNum;
        $minNum = $maxNum - $tplNum + 1;
        $onLineMaxNum = count($onLinePlayer);

        if ($onLineMaxNum < $minNum){
            //只有离线玩家
            $index = 1;
            foreach ($offLinePlayer as $pId => $t) {
                if ($index >= $minNum && $index <= $maxNum) {
                    $playerData[$pId]["Index"] = $index;
                    $ret[$pId] = $playerData[$pId];
                }
                $index++;
            }
            return $ret;
        }
        $index = 1;
        //最近比赛好友--排序靠前
        if ($source == Friend::SourceMineList && $page == 1){
            $match = $this->getAccountAttribute(RoleExtData::DB_LATELY_MATCH_FRIEND);
            if ($match != ""){
                $lastMatchFriend = json_decode($match, true);
                foreach ($lastMatchFriend as $pId) {
                    if (array_key_exists($pId, $onLinePlayer)) {
                        $playerData[$pId]["Index"] = $index;
                        $ret[$pId] = $playerData[$pId];
                        $index++;
                    }
                }
            }
        }
        //返回在线列表
        foreach ($onLinePlayer as $pId => $t) {
            if (array_key_exists($pId, $ret)) {
                continue;
            }
            if (count($ret) >= $tplNum) {
                return $ret;
            }
            if ($index >= $minNum && $index <= $maxNum) {
                $playerData[$pId]["Index"] = $index;
                $ret[$pId] = $playerData[$pId];
            }
            $index++;
        }
        //在线不够离线凑
        foreach ($offLinePlayer as $pId => $t) {
            if (count($ret) >= $tplNum) {
                return $ret;
            }
            if ($index >= $minNum && $index <= $maxNum) {
                $playerData[$pId]["Index"] = $index;
                $ret[$pId] = $playerData[$pId];
            }
            $index++;
        }
        return $ret;
    }

    //增加好友亲密度
    public function addFriendIntimacy(int $targetPlayerId, int $point, int $source)
    {
        if ($this->checkIsAIPlayer($targetPlayerId)) {
            return;
        }
        //检查对方是否为好友
        if (!$this->checkTargetPlayerIsMyFriend($targetPlayerId)) {
            return;
        }
        /**
         * @var FriendIntimacyMarkModel $friendIntimacyMarkModel
         */
        $friendIntimacyMarkModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY_MARK);
        $friendIntimacyMarkModel->setRespondentPlayerId($targetPlayerId);
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($targetPlayerId);
        $addTarget = true;
        //检查每周获取上限
        switch ($source) {
            case GameConstantDefine::INTIMACY_SOURCE_PLAY_GAME:
                //增加小火苗
                $friend->addIntimacyFire();
                $hasNum = $friendIntimacyMarkModel->getMatchLikeNum();
                $maxNum = $this->getTerm(
                    TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Intimacy_Week_Limit_Match_Like,
                    TemplateConst::ConstNum
                );
                if ($hasNum >= $maxNum) {
                    return;
                }
                $addPoint = $hasNum + $point >= $maxNum ? $maxNum - $hasNum : $point;
                $friendIntimacyMarkModel->addMatchPoint($addPoint);
                $addTarget = false;
                break;
            case GameConstantDefine::INTIMACY_SOURCE_LIKE:
                //600/周
                $hasNum = $friendIntimacyMarkModel->getMatchLikeNum();
                $maxNum = $this->getTerm(
                    TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Intimacy_Week_Limit_Match_Like,
                    TemplateConst::ConstNum
                );
                if ($hasNum >= $maxNum) {
                    return;
                }
                $addPoint = $hasNum + $point >= $maxNum ? $maxNum - $hasNum : $point;
                $friendIntimacyMarkModel->addLikePoint($addPoint);
                break;
            case GameConstantDefine::INTIMACY_SOURCE_SEND_GIFT:
                //5000/周
                $hasNum = $friendIntimacyMarkModel->getSendGiftNum();
                $maxNum = $this->getTerm(
                    TemplateDefine::TYPE_CONST,
                    TemplateConst::Const_Intimacy_Week_Limit_Send_Gift,
                    TemplateConst::ConstNum
                );
                if ($hasNum >= $maxNum) {
                    return;
                }
                $addPoint = $hasNum + $point >= $maxNum ? $maxNum - $hasNum : $point;
                $friendIntimacyMarkModel->addSendGiftPoint($addPoint);
                break;
            case GameConstantDefine::INTIMACY_SOURCE_ZL_GIFT:
                //战令送礼 无上限
                $addPoint = $point;
                break;
            default:
                return;
        }
        if ($addPoint <= 0) {
            return;
        }
        //增加亲密度
        $friend->addIntimacy($addPoint, $addTarget);
    }

    //申请亲密关系
    public function applyIntimacy(int $playerId, int $targetPlayerId, int $intimacyId): int
    {
        //检查对方是否为好友
        if (!$this->checkTargetPlayerIsMyFriend($targetPlayerId)) {
            return ClientErrorCode::ERROR_TARGET_PLAYER_IS_NOT_FRIEND;
        }
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($targetPlayerId);
        $friendData = $friend->getFriendData();
        //检查亲密度
        $needIntimacy = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Open_Limit,
            TemplateConst::ConstNum
        );
        if ($friendData->intimacy < $needIntimacy) {
            return ClientErrorCode::FRIEND_INTIMACY_NOT_ENOUGH;
        }
        //检查自己亲密关系上限
        /**
         * @var FriendIntimacyModel $friendIntimacyModel
         */
        $friendIntimacyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY);
        $friendIntimacyModel->setRespondentPlayerId($targetPlayerId);
        $maxIntimacyNum = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Title_Max,
            TemplateConst::ConstNum
        );
        if ($friendIntimacyModel->getIntimacyNum() >= $maxIntimacyNum) {
            return ClientErrorCode::FRIEND_INTIMACY_TITLE_OVER_MAX;
        }
        //检查与对方是否有关系
        if ($friendIntimacyModel->checkHasIntimacy()) {
            return ClientErrorCode::FRIEND_HAS_INTIMACY_WITH_TARGET_PLAYER;
        }
        //检查指定关系上限
        $configMaxNum = (int)$this->getTerm(
            TemplateDefine::TYPE_INTIMACY,
            $intimacyId,
            TemplateIntimacy::Limit
        );
        if ($friendIntimacyModel->getIntimacyIdNun($intimacyId) >= $configMaxNum) {
            return ClientErrorCode::FRIEND_INTIMACY_TITLE_OVER_LIMIT;
        }
        //检查对方总上限
        $friendIntimacyModel->setPlayerId($targetPlayerId);
        $friendIntimacyModel->setRespondentPlayerId($playerId);
        if ($friendIntimacyModel->getIntimacyNum() >= $maxIntimacyNum) {
            return ClientErrorCode::FRIEND_INTIMACY_TITLE_TARGET_OVER_MAX;
        }
        /**
         * @var FriendIntimacyApplyModel $friendIntimacyApplyModel
         */
        $friendIntimacyApplyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY_APPLY);
        $friendIntimacyApplyModel->setRespondentPlayerId($targetPlayerId);
        //检查是否对对方有申请
        if ($friendIntimacyApplyModel->checkHasApply()) {
            return ClientErrorCode::FRIEND_HAS_APPLY_WAIT_DEAL;
        }
        //检查对方申请信息上限
        $friendIntimacyApplyModel->setPlayerId($targetPlayerId);
        $friendIntimacyApplyModel->setRespondentPlayerId($playerId);
        $applyList = $friendIntimacyApplyModel->getMyApplyInfo();
        $applyMaxNum = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Apply_Max,
            TemplateConst::ConstNum
        );
        if (count($applyList) >= $applyMaxNum) {
            return ClientErrorCode::FRIEND_INTIMACY_APPLY_OVER_MAX;
        }
        //检查道具
        $itemId = TemplateItem::ITEM_ID_JIN_LAN_TIE;
        if ($this->getItemNumByTplID($itemId) <= 0) {
            return ClientErrorCode::FRIEND_INTIMACY_ITEM_NOT_ENOUGH;
        }
        //扣除道具
        $this->setLogConsumeItemSource(GameConstantDefine::ITEM_CONSUME_SOURCE_APPLY_INTIMACY);
        $this->subItemByTID($itemId, 1);

        //发送申请
        $friendIntimacyApplyModel->setPlayerId($playerId);
        $friendIntimacyApplyModel->setRespondentPlayerId($targetPlayerId);
        $applyId = $friendIntimacyApplyModel->makeApplyId();
        $friendIntimacyApplyModel->createIntimacyApply($applyId, $intimacyId);
        $friendIntimacyApplyModel->sendApplyMessageToTargetPlayer($applyId, $intimacyId);
        return ClientErrorCode::CLIENT_SUCCESS;
    }

    //查询亲密关系申请
    public function getMyIntimacyApplyInfo(int $page)
    {
        /**
         * @var FriendIntimacyApplyModel $friendIntimacyApplyModel
         */
        $friendIntimacyApplyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY_APPLY);
        $friendIntimacyApplyModel->setRespondentPlayerId($friendIntimacyApplyModel->playerId);
        $applyInfo = array();
        $result = $friendIntimacyApplyModel->getMyApplyInfo();
        krsort($result);
        $pageNum = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Friend_List_Number,
            TemplateConst::ConstNum
        );
        $maxPage = intdiv(count($result), $pageNum) + (count($result) % $pageNum == 0 ? 0 : 1);
        $i = 0;
        $start = ($page - 1) * $pageNum + 1;
        $end = $start + $pageNum;
        foreach ($result as $applyPlayerId => $applyId) {
            $i++;
            if ($i < $start) {
                continue;
            }
            if ($i >= $end) {
                break;
            }
            $applyArray = $friendIntimacyApplyModel->getApplyInfoByApplyId($applyId);
            if (empty($applyArray)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::FRIEND_INTIMACY_APPLY_DATA_ERROR,
                    "[FriendLogic] getMyIntimacyApplyInfo not found applyId",
                    array(
                        "applyId" => $applyId,
                    )
                );
                $friendIntimacyApplyModel->delApplyInfo($applyPlayerId, $friendIntimacyApplyModel->playerId, $applyId);
                continue;
            }
            $applyInfo[] = $friendIntimacyApplyModel->getApplyMessageProtobuf(
                $applyPlayerId,
                $applyId,
                $applyArray[FriendIntimacyApplyData::INTIMACY_ID]
            );
        }
        return [$applyInfo, $maxPage];
    }

    //申请信息操作 $option=true同意
    public function intimacyApplyOption(int $applyId, bool $option): int
    {
        //申请信息校验
        /**
         * @var FriendIntimacyApplyModel $friendIntimacyApplyModel
         */
        $friendIntimacyApplyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY_APPLY);
        $friendIntimacyApplyModel->setRespondentPlayerId($friendIntimacyApplyModel->playerId);
        $applyArray = $friendIntimacyApplyModel->getApplyInfoByApplyId($applyId);
        if (empty($applyArray)) {
            return ClientErrorCode::FRIEND_INTIMACY_APPLY_NOT_FOUND;
        }
        if ($applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID] != $friendIntimacyApplyModel->playerId) {
            return ClientErrorCode::FRIEND_INTIMACY_APPLY_NOT_FOUND;
        }
        $liveTime = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Apply_Live_Time,
            TemplateConst::ConstNum
        );
        //超时校验
        if (Utils::getServerTimestamp() >= $applyArray[FriendIntimacyApplyData::CREATE_TIME] + $liveTime) {
            return ClientErrorCode::FRIEND_INTIMACY_APPLY_OVER_TIME;
        }
        if ($option) {
            //同意申请
            //检查是否还是好友
            if (!$this->checkTargetPlayerIsMyFriend($applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID])) {
                return ClientErrorCode::ERROR_TARGET_PLAYER_IS_NOT_FRIEND;
            }
            //检查双方亲密关系数量
            /**
             * @var FriendIntimacyModel $friendIntimacyModel
             */
            $friendIntimacyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY);
            $friendIntimacyModel->setRespondentPlayerId($applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID]);
            $maxIntimacyNum = $this->getTerm(
                TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Intimacy_Title_Max,
                TemplateConst::ConstNum
            );
            if ($friendIntimacyModel->getIntimacyNum() >= $maxIntimacyNum) {
                return ClientErrorCode::FRIEND_INTIMACY_TITLE_OVER_MAX;
            }
            //检查与对方是否有关系
            if ($friendIntimacyModel->checkHasIntimacy()) {
                return ClientErrorCode::FRIEND_HAS_INTIMACY_WITH_TARGET_PLAYER;
            }
            //检查指定关系上限
            $configMaxNum = (int)$this->getTerm(
                TemplateDefine::TYPE_INTIMACY,
                $applyArray[FriendIntimacyApplyData::INTIMACY_ID],
                TemplateIntimacy::Limit
            );
            if ($friendIntimacyModel->getIntimacyIdNun(
                    $applyArray[FriendIntimacyApplyData::INTIMACY_ID]
                ) >= $configMaxNum) {
                return ClientErrorCode::FRIEND_INTIMACY_TITLE_OVER_LIMIT;
            }
            //检查对方总上限
            $friendIntimacyModel->setPlayerId($applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID]);
            $friendIntimacyModel->setRespondentPlayerId($applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID]);
            if ($friendIntimacyModel->getIntimacyNum() >= $maxIntimacyNum) {
                return ClientErrorCode::FRIEND_INTIMACY_TITLE_TARGET_OVER_MAX;
            }
            //建立关系
            $friendIntimacyModel->setIntimacy($applyArray[FriendIntimacyApplyData::INTIMACY_ID]);
            /**
             * @var FriendModel $friend
             */
            $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
            $friend->setRespondentPlayerId($applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID]);
            $friend->setIntimacyId($applyArray[FriendIntimacyApplyData::INTIMACY_ID]);

            //发送成功邮件 删除申请
            $friendIntimacyApplyModel->delApplyInfo(
                $applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID],
                $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
                $applyId
            );
            $mailId = $this->getTerm(
                TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Intimacy_Mail_Pass,
                TemplateConst::ConstNum
            );
            //替换邮件内容
            $replaceString = $this->replaceMailContent($mailId, array(
                GameConstantDefine::MAIL_KEY_PLAYER_NAME => $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
                GameConstantDefine::MAIL_KEY_INTIMACY_TYPE => $applyArray[FriendIntimacyApplyData::INTIMACY_ID]
            ));
            $this->createPlayerMail(
                $mailId,
                Utils::getServerTimestamp(),
                array(),
                0,
                $applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID],
                $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
                "",
                $replaceString
            );
            return ClientErrorCode::CLIENT_SUCCESS;
        }
        //拒绝申请 给申请人发送拒绝邮件,删除申请
        $friendIntimacyApplyModel->delApplyInfo(
            $applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID],
            $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
            $applyId
        );
        $mailId = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Mail_Refuse,
            TemplateConst::ConstNum
        );
        //替换邮件内容
        $replaceString = $this->replaceMailContent($mailId, array(
            GameConstantDefine::MAIL_KEY_PLAYER_NAME => $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
            GameConstantDefine::MAIL_KEY_INTIMACY_TYPE => $applyArray[FriendIntimacyApplyData::INTIMACY_ID]
        ));
        $this->createPlayerMail(
            $mailId,
            Utils::getServerTimestamp(),
            array(),
            0,
            $applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID],
            $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
            "",
            $replaceString
        );
        return ClientErrorCode::CLIENT_SUCCESS;
    }

    //解除亲密关系
    public function removeIntimacyTitle(int $playerId, int $targetPlayerId, int &$cdTime): int
    {
        //检查对方是否为好友
        if (!$this->checkTargetPlayerIsMyFriend($targetPlayerId)) {
            return ClientErrorCode::ERROR_TARGET_PLAYER_IS_NOT_FRIEND;
        }
        /**
         * @var FriendIntimacyModel $friendIntimacyModel
         */
        $friendIntimacyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY);
        $friendIntimacyModel->setRespondentPlayerId($targetPlayerId);
        //检查与对方是否有关系
        if (!$friendIntimacyModel->checkHasIntimacy()) {
            return ClientErrorCode::FRIEND_NO_INTIMACY_TITLE_WITH_TARGET_PLAYER;
        }
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setRespondentPlayerId($targetPlayerId);
        $friendData = $friend->getFriendData();
        $configCDTime = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Remove_Title_CD_Time,
            TemplateConst::ConstNum
        );
        $configCDInvalidTime = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Remove_Title_Invalid_Time,
            TemplateConst::ConstNum
        );
        //检查冷静期
        $now = Utils::getServerTimestamp();
        if ($friendData->cdTime == 0 || $now >= $friendData->cdTime + $configCDInvalidTime) {
            //第一次解除关系 进入冷静期
            $friend->updateIntimacyCDTime($now);
            $cdTime = $now;
            return ClientErrorCode::CLIENT_SUCCESS;
        }
        if ($friendData->cdTime + $configCDTime > $now) {
            //在冷静期 不能解除
            return ClientErrorCode::FRIEND_INTIMACY_TITLE_IN_CD_TIME;
        }
        if ($now >= $friendData->cdTime + $configCDTime && $now <= $friendData->cdTime + $configCDInvalidTime) {
            $intimacyId = $friendIntimacyModel->getTargetIntimacyId();
            //可以解除
            $friendIntimacyModel->setIntimacy(0);
            $friendIntimacyModel->delIntimacyId();
            //扣除亲密度
            $subPoint = -(int)$this->getTerm(
                TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Intimacy_Remove_Title_Cost_Point,
                TemplateConst::ConstNum
            );
            $friend->addIntimacy($subPoint, true);
            $friend->updateIntimacyCDTime(0);
            //解除关系邮件
            $mailId = $this->getTerm(
                TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Intimacy_Mail_Remove,
                TemplateConst::ConstNum
            );
            //替换邮件内容
            $replaceString = $this->replaceMailContent($mailId, array(
                GameConstantDefine::MAIL_KEY_PLAYER_NAME => $playerId,
                GameConstantDefine::MAIL_KEY_INTIMACY_TYPE => $intimacyId
            ));
            $this->createPlayerMail(
                $mailId,
                Utils::getServerTimestamp(),
                array(),
                0,
                $targetPlayerId,
                $playerId,
                "",
                $replaceString
            );
            return ClientErrorCode::CLIENT_SUCCESS;
        }
        return ClientErrorCode::CLIENT_SUCCESS;
    }

    //处理过期申请
    public function dealExpireApply(int $time)
    {
        /**
         * @var FriendIntimacyApplyModel $friendIntimacyApplyModel
         */
        $friendIntimacyApplyModel = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_INTIMACY_APPLY);
        $liveTime = $this->getTerm(
            TemplateDefine::TYPE_CONST,
            TemplateConst::Const_Intimacy_Apply_Live_Time,
            TemplateConst::ConstNum
        );
        //超时校验
        $expireTime = $time - $liveTime;
        $expireApply = $friendIntimacyApplyModel->getExpireTimeApply($expireTime);
        foreach ($expireApply as $applyId => $expireTime) {
            $applyArray = $friendIntimacyApplyModel->getApplyInfoByApplyId($applyId);
            if (empty($applyArray)) {
                LogMark::getInstance()->markError(
                    GameErrorCode::FRIEND_INTIMACY_APPLY_DATA_ERROR,
                    "[FriendLogic] dealExpireApply not found applyId",
                    array(
                        "applyId" => $applyId,
                    )
                );
                $friendIntimacyApplyModel->delApplyIndex($applyId);
                continue;
            }
            //给申请人发送退回邮件
            ModelManager::getInstance()->setPlayerId($applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID]);
            $mailId = $this->getTerm(
                TemplateDefine::TYPE_CONST,
                TemplateConst::Const_Intimacy_Mail_UnRead,
                TemplateConst::ConstNum
            );
            //替换邮件内容
            $replaceString = $this->replaceMailContent($mailId, array(
                GameConstantDefine::MAIL_KEY_OTHER_NAME => $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
                GameConstantDefine::MAIL_KEY_INTIMACY_TYPE => $applyArray[FriendIntimacyApplyData::INTIMACY_ID]
            ));
            $this->createPlayerMail(
                $mailId,
                Utils::getServerTimestamp(),
                array(),
                0,
                $applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID],
                $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
                "",
                $replaceString
            );
            //删除申请
            $friendIntimacyApplyModel->delApplyInfo(
                $applyArray[FriendIntimacyApplyData::APPLY_PLAYER_ID],
                $applyArray[FriendIntimacyApplyData::TARGET_PLAYER_ID],
                $applyId
            );
            ModelManager::getInstance()->setPlayerId(0);
        }
    }

    //添加/恢复拉黑后,同步好友信息
    private function syncFriendInfo(int $playerId, int $respondentPlayerId, bool $addPacket = false)
    {
        /**
         * @var FriendModel $friend
         */
        $friend = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND);
        $friend->setPlayerId($playerId);
        $friend->setRespondentPlayerId($respondentPlayerId);
        $friendData = $friend->getFriendData();
        $info = $friendData->getData();
        /**
         * @var FriendBlackModel $black
         */
        $black = ModelManager::getInstance()->getModel(ModelTypeDefine::FRIEND_BLACK);
        $black->setPlayerId($playerId);
        $black->setRespondentPlayerId($respondentPlayerId);
        $info[FriendsData::IS_BLACK] = $black->checkInBlack();
        $playerIds = array(
            $respondentPlayerId => $info
        );
        [$fInfo, $fInfoExt] = $this->getFriendInfo(
            $playerIds,
            Friend::SortTypeLastInFirstOut,
            1,
            Friend::SourceMineList
        );
        $message = new GCSyncFriendInfo();
        $message->setInfo($fInfo);
        $message->setInfoExt($fInfoExt);
        $message->setNum($friend->getMineNumber());
        if ($addPacket) {
            $this->addPacket(PacketId::GC_SyncFriendInfo, $message, $playerId);
        } else {
            SendMessage::getInstance()->sendClient(PacketId::GC_SyncFriendInfo, $message);
        }
    }
}