<?php


namespace Game\Model\Pvp;

use Framework\Define\ErrorDefine;
use Framework\Lib\Utils;
use Framework\Log\LogMark;
use Framework\MVC\ModelManager;
use Framework\Network\SendMessage;
use Framework\DB\Handler\PlayerDBHandler;
use Framework\Logic\PacketCacheLogic;
use Framework\Protobuf\ClientError;
use Game\Config\GameConfig;
use Game\Constant\ClientErrorCode;
use Game\Constant\ConstTemplate\TemplatePVPMap;
use Game\Constant\DBTableDefine;
use Game\Constant\GameConstantDefine;
use Game\Constant\GameErrorCode;
use Game\Constant\ModelTypeDefine;
use Game\Constant\TemplateDefine;
use Game\Data\Friends\FriendsLastGameData;
use Game\Data\JoinIdData;
use Game\Data\PvpPacket\CancelMatchData;
use Game\Data\RecordData;
use Game\Data\RoomCustomizeData;
use Game\Data\RoomData;
use Game\Logic\Activity\ScuffleLogic;
use Game\Logic\CarLogic;
use Game\Logic\NoticeLogic;
use Game\Logic\PaiweiLevelLogic;
use Game\Logic\Room\RoomLogic;
use Game\Logic\SeasonLogic;
use Game\Model\RoomCustomizeModel;
use Game\Model\RoomModel;
use Game\Protobuf\GCGetPracticeRandMap;
use Game\Protobuf\PacketId;

class PvpBaseModel
{
    use CarLogic;
    use RoomLogic;
    use PacketCacheLogic;
    use PlayerDBHandler;
    use NoticeLogic;
    use ScuffleLogic;
    use PaiweiLevelLogic;
    use SeasonLogic;

    private int $playerId;

    private ?JoinIdData $jd = null;

    public int $joinID = 0;

    public const RandMapNum = 1;
    public const MapRand = 'MapRand';

    public function setPlayerId($playerId)
    {
        $this->playerId = $playerId;
    }

    // 处理自定义房间匹配匹配信息
    public function joinCustomizeRoomMatching(int $roomID): array
    {
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][1][PvpBaseModel] start pvp Join Customize Matching",
            array(
                "roomId" => $roomID
            )
        );
        $isRand = false;
        $cRoom = new RoomCustomizeData($roomID);
        if(!$cRoom->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[PVPFlowLog][PvpBaseModel] pvp search customize room info error!'
            );
            return array(ClientErrorCode::ERROR_ROOM_NOT_EXIST, 0);
        }
        $mapId = $cRoom->mapID;  // 好友最近比赛记录使用
        // 随机地图处理
        if($cRoom->mapID == self::RandMapNum) {
            // 获取所有开放地图 随机一张
            $pvpMap = $this->getTable(TemplateDefine::TYPE_PVP_MAP);
            $map = [];
            foreach($pvpMap as $id=>$item) {
                if($item[TemplatePVPMap::OpenIf] === "True" &&
                    $item[TemplatePVPMap::Random] == TemplatePVPMap::UseRandom) {
                    $map[] = $item[TemplatePVPMap::Id];
                }
            }

            $key = array_rand($map);
            $cRoom->mapID = $map[$key];
            $isRand = true;
            $mapId = $map[$key];
        }

        $matchType = $cRoom->modeType;
        $modeModel = ModelManager::getInstance()->getModel(ModelTypeDefine::MODE_MAP[$matchType]);
        $modeModel->setPlayerId($this->playerId);

        // joinID 生成
        $this->joinID = $this->makeJoinMatchingId();
        foreach($cRoom->playerInfo as $id => $p) {
            if($p[RoomCustomizeData::READY_STATE] == RoomCustomizeModel::ROOM_PLAY_NOT_READY
            && $p[RoomCustomizeData::ROLE] == RoomCustomizeModel::IN_ROOM) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_SEARCH_ERROR,
                    '[PVPFlowLog][PvpBaseModel] join match customize room player not ready error!'
                );
                $modeModel->joinRoomMsg(ClientErrorCode::PVP_JOIN_MATCH_PLAYER_NOT_READY);
                $modeModel->joinRoomMsgSend();
                return array(ClientErrorCode::PVP_JOIN_MATCH_ERROR, 0);
            }
            $this->initJd();
            $this->jd->joinId = $this->joinID;
            $this->jd->status = GameConstantDefine::PLAYER_STATUS_MATCH;
            $this->jd->saveJoinId($id);
        }

        if(!$modeModel->sendJoinMatchPacket($this->joinID, $cRoom, $isRand)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[PVPFlowLog][PvpBaseModel] join match send packet error!!'
            );
        }

        // 存储每个人匹配信息
        $this->initRecordData($this->joinID, $cRoom, $mapId);
        $this->createFriendLastGameRecord($this->joinID, $cRoom, $mapId);
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_MATCH, $this->playerId);
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][3][PvpBaseModel] Join Matching success",
            array(
                "joinId" => $this->joinID
            )
        );
        return array(ClientErrorCode::CLIENT_SUCCESS, $this->joinID);
    }

    // 处理匹配信息
    public function joinMatching(int $roomID): array
    {
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][1][PvpBaseModel] start pvp Join Matching",
            array(
                "roomId" => $roomID
            )
        );
        $isRand = false;
        $room = new RoomData($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[PVPFlowLog][PvpBaseModel] pvp search room info error!'
            );
            return array(ClientErrorCode::ERROR_ROOM_NOT_EXIST, 0);
        }
        //大乱斗 检查活动是否开启
        if (($room->modeType == GameConstantDefine::MATCH_MODE_SCUFFLE) && !$this->checkScuffleIsOpen()) {
            logMark::getInstance()->markError(
                GameErrorCode::ROOM_MODE_TYPE_NOT_OPEN,
                '[PvpBaseModel] scuffle not open',
                ['modeType' => $room->modeType]
            );
            return array(ClientErrorCode::SCUFFLE_NOT_OPEN, 0);
        }
        if (in_array($room->modeType, [
                               GameConstantDefine::MATCH_MODE_SINGLE_PAI_WEI,
                               GameConstantDefine::MATCH_MODE_TEAM_PAI_WEI]))
        {
            //排位赛,玩家段位检查
            if (!$this->checkRoomPlayerLevel($room->playerInfo)) {
                return array(ClientErrorCode::PVP_RANKING_LEVEL_NOT_MATE, 0);
            }
            if ($this->getSeasonStatus() == GameConstantDefine::SEASON_STATUS_CLOSE) {
                return array(ClientErrorCode::SEASON_CLOSE, 0);
            }
        }
        $mapId = $room->mapID;  // 好友最近比赛记录使用
        // 随机地图处理--练习赛随机
        if($room->mapID == self::RandMapNum &&
            in_array($room->modeType, [GameConstantDefine::MATCH_MODE_SINGLE_PRACTISE, GameConstantDefine::MATCH_MODE_TEAM_PRACTISE])
        ) {
            // 获取所有开放地图 随机一张
            $pvpMap = $this->getTable(TemplateDefine::TYPE_PVP_MAP);
            $map = [];
            foreach($pvpMap as $id=>$item) {
                if($item[TemplatePVPMap::OpenIf] === "True" &&
                    $item[TemplatePVPMap::Random] == TemplatePVPMap::UseRandom) {
                    $map[] = $item[TemplatePVPMap::Id];
                }
            }

            $key = array_rand($map);
            $room->mapID = $map[$key];
            $isRand = true;
            $mapId = $map[$key];
        }

        $matchType = $room->modeType;
        $modeModel = ModelManager::getInstance()->getModel(ModelTypeDefine::MODE_MAP[$matchType]);
        $modeModel->setPlayerId($this->playerId);

        // joinID 生成
        $this->joinID = $this->makeJoinMatchingId();
        foreach($room->playerInfo as $id => $p) {
            if($p[RoomData::READY_STATE] == RoomModel::ROOM_PLAY_NOT_READY) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_SEARCH_ERROR,
                    '[PVPFlowLog][PvpBaseModel] join match player not ready error!'
                );
                $modeModel->joinRoomMsg(ClientErrorCode::PVP_JOIN_MATCH_PLAYER_NOT_READY);
                $modeModel->joinRoomMsgSend();
                return array(ClientErrorCode::PVP_JOIN_MATCH_ERROR, 0);
            }
            $this->initJd();
            $this->jd->joinId = $this->joinID;
            $this->jd->status = GameConstantDefine::PLAYER_STATUS_MATCH;
            $this->jd->saveJoinId($id);
        }

        if(!$modeModel->sendJoinMatchPacket($this->joinID, $room, $isRand)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[PVPFlowLog][PvpBaseModel] join match send packet error!!'
            );
            return array(ClientErrorCode::PVP_JOIN_MATCH_ERROR, 0);
        }

        // 存储每个人匹配信息
        $this->initRecordData($this->joinID, $room, $mapId);
        $this->createFriendLastGameRecord($this->joinID, $room, $mapId);
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_MATCH, $this->playerId);
        LogMark::getInstance()->markInfo(
            "[PVPFlowLog][3][PvpBaseModel] Join Matching success",
            array(
                "joinId" => $this->joinID
            )
        );
        return array(ClientErrorCode::CLIENT_SUCCESS, $this->joinID);
    }

    //初始比赛记录
    private function initRecordData(int $joinID, RoomData $room, int $mapId)
    {
        $playerInfo = $room->playerInfo;
        foreach ($playerInfo as $id => $p) {
            $record = new RecordData($id, $joinID);
            if(!$record->initRecord($mapId, $room->modeType, $room->roomID, $p[RoomData::CAR_TPL_ID])) {
                LogMark::getInstance()->markError(
                    GameErrorCode::DATA_CREATE_ERROR,
                    '[pvpBaseModel] init record error!'
                );
            }
        }
    }

    //创建好友最近比赛记录
    private function createFriendLastGameRecord($joinID,  RoomData $room, $mapId)
    {
        $lastGame = new FriendsLastGameData();
        $lastGame->gameId = $joinID;
        $lastGame->mapId = $mapId;
        $lastGame->gameType = $room->modeType;
        $lastGame->time = Utils::getServerTimestamp();
        foreach ($room->playerInfo as $pId => $value) {
            $lastGame->playerId = $pId;
            $lastGame->Init();
        }
    }

    // 取消匹配
    public function cancelMatch(int $roomID): bool {
        LogMark::getInstance()->markDebug(
            "[PVPFlowLog][40][PvpBaseModel] cancelMatch"
        );
        $room = new RoomData($roomID);
        if(!$room->searchRoomByRoomID()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[PvpBaseModel] cancelMatch pvp search room info error!'
            );
            return false;
        }
        $cancel = new CancelMatchData();
        $joinID = $this->getJoinId();
        $cancel->joinID = $joinID;
        $cancel->serverID = GameConfig::getInstance()->SERVER_ID();
        $msgID = PacketId::GM_CancelMatch;
        if(!$cancel->sendPacket($msgID)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[PVPFlowLog][cancelMatch] send cancel match to match server db error!'
            );
            return false;
        }
        LogMark::getInstance()->markDebug(
            "[PVPFlowLog][41][PvpBaseModel] send cancelMatch success"
        );
        foreach($room->playerInfo as $pid => $data) {
            $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ROOM, $pid);
        }
        return true;
    }

    //生成匹配id
    private function makeJoinMatchingId(): int
    {
        return (int)(Utils::getServerTimestamp() . random_int(1000, 2000));
    }

    public function updateStatus(int $status)
    {
        $this->initJd();
        $this->jd->status = $status;
        $this->jd->saveJoinId();
        $this->syncFriendStatusToAll($status, $this->jd->playerId);
    }

    public function clearJoinId()
    {
        $this->initJd();
        $this->jd->joinId = 0;
        $this->jd->status = GameConstantDefine::PLAYER_STATUS_ROOM;
        $this->jd->saveJoinId();
        $this->syncFriendStatusToAll(GameConstantDefine::PLAYER_STATUS_ROOM, $this->jd->playerId);
    }

    public function getJoinId(): int
    {
        if (!$this->initJd()) {
            return 0;
        }
        return $this->jd->joinId;
    }

    public function getJoinIdByPlayerId(int $playerId): int
    {
        $data = new JoinIdData($playerId);
        $data->searchDB();
        return $data->joinId;
    }

    //初始新手引导相关,返回joinId
    public function initGuideJoinIdData(RoomData $room, int $mapId): int
    {
        $this->joinID = $this->makeJoinMatchingId();
        $this->initJd();
        $this->jd->joinId = $this->joinID;
        $this->jd->status = GameConstantDefine::PLAYER_STATUS_MATCH;
        $this->jd->saveJoinId();
        $record = new RecordData($this->playerId, $this->joinID);
        $record->initRecord($mapId, $room->modeType, $room->roomID, $room->playerInfo[$this->playerId][RoomData::CAR_TPL_ID]);
        $this->createFriendLastGameRecord($this->joinID, $room, $mapId);
        return $this->joinID;
    }

    private function initJd(): bool
    {
        if (!is_null($this->jd)) {
            return true;
        }
        $this->jd = new JoinIdData($this->playerId);
        if ($this->jd->searchDB()) {
            return true;
        }
        unset($this->jd);
        $this->jd = null;
        return false;
    }

    public function getPracticeRandMap(): bool {

        $ret = [];
        $this->searchTermMulti(
            $ret,
            DBTableDefine::TABLE_COMMON_CONST,
            0,
            0,
            [
                self::MapRand,
            ]
        );

        if(empty($ret)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[PvpBaseModel] practice get rand map but is empty error!'
            );
            return false;
        }

        $map = json_decode($ret[self::MapRand], true);
        $mapMsg = new GCGetPracticeRandMap();
        $mapMsg->setCode(ClientErrorCode::CLIENT_SUCCESS);
        $mapMsg->setMap($map);
        SendMessage::getInstance()->sendClient(PacketId::GC_GetPracticeRandMap, $mapMsg);
        return true;
    }

    //发送失败消息
    public function sendFailMessage(int $playerId, int $code)
    {
        $packet = new ClientError();
        $packet->setErrorId($code);
        $this->addPacket(PacketId::Server_Error, $packet, $playerId);
    }
}
