<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 21/11/23
 * Time: 19:05
 */

namespace App\Services\Api;

use App\Models\ChatTeam;
use App\Models\Course;
use App\Models\CourseHole;
use App\Models\Game;
use App\Models\GameFictitiou;
use App\Models\GameLooker;
use App\Models\GameType;
use App\Models\GameUserScore;
use App\Models\User;
use App\Models\GameUser;
use App\Models\UserJoin;
use App\Models\Game as GameModel;
use App\Services\TencentImGroupService;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use phpDocumentor\Reflection\PseudoTypes\TraitString;
use PHPUnit\Util\Exception;

class GameService
{
    public function __construct()
    {
        $this->user = auth('api')->user();
    }

    //创建者才可以操作
    public function hasAuth($userId, $gameId)
    {
        if (!$info = GameModel::where(['id' => $gameId, 'creator' => $userId])->first()) {
            return false;
        }
        return true;
    }

    //当前用户是否已加入比赛
    public function isHadJoin($gameId, $userId)
    {
        $gameUser = GameUser::query()
            ->where(['game_id' => $gameId, 'user_id' => $userId])
            ->where('status', 1)
            ->whereNull('deleted_at')
            ->first();

        if (!$gameUser) {
            return 0;
        }

        return 1;
    }

    //当前用户是否已经围观
    public function isHadLook($gameId, $userId)
    {
        $gameLooker = GameLooker::query()
            ->where(['game_id' => $gameId, 'user_id' => $userId])
            ->whereNull('deleted_at')
            ->first();

        if (!$gameLooker) {
            return 0;
        }

        return 1;
    }

    //当前用户是否已申请加入比赛
    public function isHadApplyJoin($gameId, $userId)
    {
        $info = UserJoin::query()
            ->where(['type' => 2, 'matter_id' => $gameId, 'user_id' => $userId])
            ->where('status', 0)
            ->where('invitor_id', '=', 0)
            ->whereNull('deleted_at')
            ->select('id', 'status')
            ->first();

        if (!$info) {
            return -1;
        }

        return $info['status'];
    }

    //当前用户是否已被邀请请加入比赛
    public function isHadInviteJoin($gameId, $userId)
    {
        $info = UserJoin::query()
            ->where(['type' => 2, 'matter_id' => $gameId, 'user_id' => $userId])
            ->where('status', 0)
            ->where('invitor_id', '>', 0)
            ->whereNull('deleted_at')
            ->select('id', 'status')
            ->first();

        if (!$info) {
            return -1;
        }

        return $info['status'];
    }

    //判断当前比赛的状态，1待进行，2进行中，3已结束, $info 是一条比赛的想信息
    public function getGameStatus($info)
    {
        $now = Carbon::now();

        if ($now < $info['begin_time']) {
            $status = 1;
        } elseif ($now > $info['begin_time'] && !$info['end_time']) {
            $status = 2;
        } elseif ($info['end_time']) {
            $status = 3;
        } else {
            $status = 0;
        }

        return $status;
    }

    //是否可以继续加球手
    public function isCanAddPlayer($gameId)
    {
        $game = Game::where('id', $gameId)->select('scale', 'members')->first();

        if ($game['members'] < $game['scale']) {
            return true;
        }

        return false;
    }

    //判断用户是否正在进行比赛
    public function isUserGoingGame($gameId)
    {
        //当前比赛中的用户
        $userIds = GameUser::query()
            ->where('game_id', $gameId)
            ->where('status', 1)
            ->where('type', 1)
            ->whereNull('deleted_at')
            ->pluck('user_id');

        //当前进行的比赛
        $gameIds = Game::query()
            ->where('begin_time', '<=', Carbon::now())
            ->whereNull('end_time')
            ->whereNull('deleted_at')
            ->pluck('id');

        //查找是否正参加其他比赛
        $removeUsers = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->leftJoin('users as u', 'u.id', '=', 'gu.user_id')
            ->distinct()
            ->whereIn('gu.user_id', $userIds)
            ->whereIn('gu.game_id', $gameIds)
            ->whereNull('gu.deleted_at')
            ->where('gu.status', 1)
            ->where('gu.type', 1)
            ->select('u.id', 'u.name', 'tencent_im_user_id')
            ->get()
            ->toArray();

        //tom, jack 正在参加其他赛，启动后，本次比赛会将他们移除，确认启动码？
        $notic = '';
        if (!empty($removeUsers)) {
            foreach ($removeUsers as $key => $val) {
                $notic .= $val['name'] . ',';
            }
            $notic = mb_substr($notic, 0, -1);
            $notic = $notic . ' ' . trans('api.START_NOTICE');
            //将需要移除的用户缓存起来 30 秒，启动比赛需用到
            Redis::setex('checkGame' . $gameId, 10, json_encode($removeUsers));
        }

        return ['notice' => $notic];
    }

    //启动比赛
    public function beginGame($gameId)
    {
        if (!self::isAllowBeginGame($this->user->id)) {
            throw new Exception(trans('api.NOT_ALLOW_BEGIN_GAME'));
        }
        DB::beginTransaction();
        try {
            $now = Carbon::now()->toDateTimeString();
            $game = Game::where('id', $gameId)
                ->whereNull('end_time')
                ->whereNull('deleted_at')
                ->first();
            if (!$game) {
                throw new Exception(trans('api.ILLEGAL_OPERATION'));
            }
            $game->begin_time = $now;
            $game->save();

            if ($removeUsers = Redis::get('checkGame' . $gameId)) {
                $removeUsers = json_decode($removeUsers, true);
                //移除用户本次比赛
                $removeUserIds = array_column($removeUsers, 'id');
                DB::table('game_users')->where('game_id', $gameId)->whereIn('user_id', $removeUserIds)->update(['deleted_at' => $now]);
                //删除群成员
                $groupService = new TencentImGroupService();
                $data = array();
                $data['member_list'] = $removeUsers; //这里不是 $data['member_list'] []
                $data['GroupId'] = $game->GroupId;
                $groupService->deleteGroupMember($data);

                Redis::del('checkGame' . $gameId);
            }
            DB::commit();
            return true;
        } catch (\Exception $exception) {
            Log::error('启动比赛出错');
            Log::error($exception->getMessage());
            DB::rollBack();
        }
    }

    //判断当前启动比赛的人，如果他创建的其他比赛进行，不允许启动比赛
    static protected function isAllowBeginGame($userId)
    {
        $info = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->leftJoin('games as g', 'g.id', '=', 'gu.game_id')
            ->where('gu.user_id', $userId)
            ->where('gu.status', 1)
            ->where('gu.type', 1)
            ->where('g.begin_time', '<=', Carbon::now())
            ->whereNull('g.deleted_at')
            ->whereNull('gu.deleted_at')
            ->whereNull('g.end_time')
            ->first();

        if ($info) {
            return false;
        }

        return true;
    }

    //创建、更新比赛
    public function store($request, $game)
    {
        DB::beginTransaction();
        try {
            if ($request->id) {
                $game = Game::query()->find($request->id);
                $preName = $game->name;
            }
            $game->creator = $this->user->id;
            $typeId = $request->game_type_id;
            $gameName = $request->name ?: 'No game name';
            $beginDate = $request->begin_date ?: date('Y-m-d', time());
            $beginTime = $request->begin_time ?: date('H:i', time());
            $game->name = $gameName;
            $game->begin_time = $beginDate . ' ' . $beginTime;
            $game->forbid_look = $request->forbid_look ?: 0;
            $game->forbid_join = $request->forbid_join ?: 0;
            $game->course_id = $request->course_id;

            if ($typeId) { //如果选择了规模
                $game->game_type_id = $typeId;
                $game->scale = GameType::where('id', $typeId)->value('scale');
            } else { //如果没选规模，下面的代码会根据实际人数设定类型和规模
                $game->game_type_id = 1;
                $game->scale = 5;
            }
            $game->save();

            $gameUser = new GameUser();
            $data = [[
                'game_id' => $game->id,
                'user_id' => $this->user->id,
                'type' => 1,
                'level' => 4, //给个默认值
                'created_at' => Carbon::now()->toDateTimeString(),
                'updated_at' => Carbon::now()->toDateTimeString(),
            ]];

            $gameUsers = $request->game_users;
            if (!empty($gameUsers)) {
                $gameUsers = is_array($gameUsers) ? $gameUsers : json_decode($gameUsers, true);
                foreach ($gameUsers as $key => $val) {
                    if ($val['user_id'] != $this->user->id) {
                        $row = [
                            'game_id' => $game->id,
                            'user_id' => $val['user_id'],
                            'type' => $val['type'],
                            'level' => $val['level'],
                            'created_at' => Carbon::now()->toDateTimeString(),
                            'updated_at' => Carbon::now()->toDateTimeString(),
                        ];
                        array_push($data, $row);
                    }
                    if ($val['type'] == 2) { //虚拟球手
                        $gameFictitiou = GameFictitiou::find($val['user_id']);
                        $gameFictitiou->game_id = $game->id;
                        $gameFictitiou->save();
                    }
                }
            }
            $count = count($data);
            //将创建比赛时成员数量记下来
            $updateData = ['members' => $count];
            if (!$typeId) { //如果不选规模，根据当前实际人数判断设定
                $info = self::setGameScale($count);
                $updateData = [
                    'members' => $count,
                    'scale' => $info['scale'],
                    'game_type_id' => $info['id'],
                ];
            }
            DB::table('games')->where('id', $game->id)->update($updateData);

            if (!$request->id) {
                //创建群聊
                $groupService = new TencentImGroupService();
                $groupData = array();
                $groupIds = array_column($data, 'user_id');
                $gameUsers = User::query()->whereIn('id', $groupIds)->select('id', 'name', 'avatar', 'tencent_im_user_id')->get()->toArray();
                //获取群头像和 users
                $resData = $groupService->getUsersAndAvatar($groupIds);
                $groupData['avatar'] = $resData['file_avatar'];
                $groupData['member_list'] = $gameUsers;
                $groupData['Owner_Account'] = $this->user->tencent_im_user_id;
                $groupData['group_name'] = $gameName;
                $groupResult = $groupService->createGroup($groupData);

                if ($groupResult['ActionStatus'] == 'OK' && $groupResult['ErrorCode'] == 0) {
                    $game->GroupId = $groupResult['GroupId'];
                    $game->save();
                }
                //将群聊信息写入数据表
                $chatTeam = App::make('getChatTeamInstance');
                $chatTeam->user_id = $this->user->id;
                $chatTeam->Owner_Account = $this->user->tencent_im_user_id;
                $chatTeam->group_name = $gameName;
                $chatTeam->GroupId = $groupResult['GroupId'];
                $chatTeam->FaceUrl = $resData['file_avatar'];
                $chatTeam->MaxMemberCount = 20;
                $chatTeam->ApplyJoinOption = 'FreeAccess';
                $chatTeam->type = 3;
                $chatTeam->save();

                $gameUser->addAll($data); //批量新增
            } else {
                self::addOrUpdate($request->id, $data);
                if($gameName != $preName){ //如果群聊名称发生改变，更新群聊名称
                    $groupData['GroupId'] = $game->GroupId;
                    $groupData['name'] = $gameName;
                    $groupService = TencentImGroupService::getInstance();
                    $groupService->modifyGroupBaseInfo($groupData);
                    //修改本地数据
                    $chatTeam = ChatTeam::query()
                        ->where('GroupId', $game->GroupId)
                        ->whereNull('deleted_at')
                        ->first();
                    $chatTeam->group_name = $gameName;
                    $chatTeam->save();
                }
            }
            DB::commit();
        } catch (\Exception $exception) {
            DB::rollBack();
            Log::error('创建比赛群聊 Exception' . $exception->getMessage());
            throw new Exception(trans('api.NETWORK_ERROR'));
        }

        return true;
    }

    //设置比赛规模
    public static function setGameScale($count = 1)
    {
        $A = GameType::query()->where('name', 'A')->select('id', 'name', 'rule', 'scale')->first();
        $B = GameType::query()->where('name', 'B')->select('id', 'name', 'rule', 'scale')->first();

        if ($count <= $A['scale']) {
            return $A;
        } else {
            return $B;
        }
    }

    private static function addOrUpdate($gameId, array $data)
    {
        $gameUserIds = [];
        foreach ($data as $key2 => $val2) {
            $map = [
                'game_id' => $val2['game_id'],
                'user_id' => $val2['user_id'],
                'type' => $val2['type'],
            ];
            $info = GameUser::query()->where($map)->whereNull('deleted_at')->first();
            if (empty($info)) {
                $gameUserIds[] = GameUser::query()->insertGetId($val2);
            } else {
                $gameUserIds[] = $info->id;
            }
        }
        GameUser::query()->where('game_id', $gameId)->whereNotIn('id', $gameUserIds)->update(['deleted_at' => date('Y-m-d H:i:s')]);
    }

    //修改比赛场地
    public function saveGameCourse($gameId, $courseId)
    {
        $game = Game::find($gameId);
        $game->course_id = $courseId;
        if (!$game->save()) {
            return false;
        }
        return true;
    }

    //获取今日比赛
    public function getTodayGames($type)
    {
        $now = Carbon::now();
        if (!$type) {
            return $this->error(trans('api.PARAMS_ERROR'));
        }
        $map = [
            ['begin_time', '>', Carbon::today()],
            ['begin_time', '<', Carbon::tomorrow()],
        ];
        if ($type == 2) { //即将进行
            $map[] = ['begin_time', '>', $now];
        } elseif ($type == 3) { //已结束 game_users
            $map[] = ['end_time', '<>', NULL];
        } else { //正在进行
            $map[] = ['begin_time', '<', $now];
            $map[] = ['end_time', '=', NULL];
        }
        $list = GameModel::query()
            ->leftJoin('game_types as gt', 'gt.id', '=', 'games.game_type_id')
            ->leftJoin('courses', 'courses.id', '=', 'games.course_id')
            ->where($map)
            ->select('games.id as game_id', 'games.name', 'courses.name as course_name', 'courses.img', 'begin_time', 'forbid_look', 'gt.name as type_name', 'gt.scale as scale')
            ->orderBy('begin_time', 'asc')
            ->get()
            ->toArray();

        foreach ($list as $key => $val) {
            $val['begin_time'] = Carbon::parse($val['begin_time'])->diffForHumans();
            $gameInfo = GameModel::getGameInfo($val['game_id']);
            $val['scale'] = $gameInfo['actual_num'];
            $val['players'] = $gameInfo['players'];
            $list[$key] = $val;
        }

        return $list;
    }

    //待进行 比赛 （不包含用户已加入和创建的比赛）
    public function getNotBeginGames($keyword)
    {
        $courseIds = [];
        if($keyword = trim($keyword)){
            $keyword = htmlspecialchars($keyword);
            $courseIds = Course::query()
                ->where('name', 'like', '%'.$keyword.'%')
                ->whereNull('deleted_at')
                ->pluck('id');
            if($courseIds->isNotEmpty()){
                $courseIds = $courseIds->toArray();
            }
        }

        //我相关的未开始比赛
        $myGameIds = GameUser::query()
            ->leftJoin('games', 'games.id', '=', 'game_users.game_id')
            ->where('user_id', $this->user->id)
            ->where('type', 1)
            ->where('game_users.status', 1)
            ->where('begin_time', '>', Carbon::now())
            ->whereNull('game_users.deleted_at')
            ->whereNull('games.end_time')
            ->orderBy('begin_time', 'asc')
            ->pluck('games.id')
            ->toArray();

        //所有未开始、且未加满的比赛
        $gameIds = GameModel::query()
            ->whereNull('deleted_at')
            ->whereNull('end_time')
            ->where('begin_time', '>', Carbon::now())
            ->whereColumn('scale', '>', 'members')
            ->whereNotIn('id', $myGameIds)
            ->when($courseIds, function ($query, $courseIds){
                $query->whereIn('course_id', $courseIds);
            })
            ->orderBy('begin_time', 'asc')
            ->pluck('id')
            ->toArray();

        $list = [];

        if (empty($gameIds)) {
            return $list;
        }
        foreach ($gameIds as $id) {
            $info = GameModel::getGameInfo($id);
            $info['game_type'] = $info['game_type'] . '(' . $info['actual_num'] . ')';
            $info['is_had_join'] = 0;
            $list[] = $info;
        }

        return $list;
    }

    //获取我的活动（待进行）
    public function getMyGames()
    {
        $userId = $this->user->id;
        $gameIds = GameUser::query()
            ->leftJoin('games', 'games.id', '=', 'game_users.game_id')
            ->where('user_id', $userId)
            ->where('game_users.status', 1)
            ->whereNull('game_users.deleted_at')
            ->whereNull('games.deleted_at')
            ->whereNull('games.end_time')
            ->where('begin_time', '>', Carbon::now())
            ->orderBy('begin_time', 'asc')
            ->pluck('games.id');

        $gameIds = !empty($gameIds) ? $gameIds->toArray() : [];
        $list = [];
        if (empty($gameIds)) {
            return $list;
        }
        foreach ($gameIds as $id) {
            $info = GameModel::getGameInfo($id);
            $info['game_type'] = $info['game_type'] . '(' . $info['actual_num'] . ')';
            $list[] = $info;
        }

        return $list;
    }

    //我报名的比赛
    public function getHadSubmitGames($status)
    {
        $userId = $this->user->id;

        $gameIds = UserJoin::query()
            ->join('games', 'games.id', '=', 'user_joins.matter_id')
            ->whereNull('games.deleted_at')
            ->where('games.begin_time', '<', Carbon::now())
            ->where('user_id', $userId)
            ->where('user_joins.status', $status)
            ->where('type', 2)
            ->orderBy('games.created_at', 'asc')
            ->pluck('games.id')
            ->toArray();

        $list = [];
        if (empty($gameIds)) {
            return $list;
        }
        foreach ($gameIds as $id) {
            $info = GameModel::getGameInfo($id);
            $info['game_type'] = $info['game_type'] . '(' . $info['actual_num'] . ')';
            $list[] = $info;
        }

        return $list;
    }

    //正在进行的活动
    public function getUnderwayGame()
    {
        $userId = $this->user->id;
        $gameId = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->leftJoin('games', 'games.id', '=', 'gu.game_id')
            ->where('user_id', $userId)
            ->where('gu.type', 1)
            ->where('gu.status', 1)
            ->whereNull('gu.deleted_at')
            ->whereNull('games.end_time')
            ->where('begin_time', '<', Carbon::now())
            ->orderBy('games.id', 'desc')
            ->value('games.id');

        $info = [];
        if (empty($gameId)) {
            return $info;
        }
        $info = GameModel::getGameInfo($gameId);
        $info['game_type'] = $info['game_type'] . '(' . $info['actual_num'] . ')';
        unset($info['players']);

        return $info;
    }

    //历史球员 ids
    public function getHistoryPlayerIds()
    {
        $userId = $this->user->id;
        //和我在同一场比赛中出现过的球员 ids
        $ids = GameUser::whereIn('game_id', function ($query) use ($userId) {
            $query->select('game_id')
                ->from('game_users')
                ->where('user_id', $userId)
                ->get();
        })->groupBy('user_id')->where('type', 1)->pluck('user_id')->toArray();
        //去掉自己的id
        array_splice($ids, array_search($userId, $ids), 1);

        return $ids;
    }

    //搜索球友
    public function getPlayers($keyword = '')
    {
        $list = User::query()
            ->leftJoin('game_users as gu', 'users.id', '=', 'gu.user_id')
            ->where('users.name', 'like', '%' . $keyword . '%')
            ->orWhere('users.id', $keyword)
            ->where('gu.type', 1)
            ->distinct(true)
            ->select('users.id', 'users.name', 'avatar', 'is_auth')
            ->orderBy('users.id')
            ->get();

        if($list->isEmpty()){
            return [];
        }
        $list = $list->toArray();

        foreach ($list as $key => $val) {
            $val['total_joins'] = GameUser::query()->where('user_id', $val['id'])->where('type', 1)->count();
            $val['have_user_course'] = UserService::haveUserCourse($val['id']);
            $list[$key] = $val;
        }

        return $list;
    }

    //围观比赛
    public function doLookGame($userId, $gameId)
    {
        if (!$this->isHadLook($gameId, $userId) && !$this->isHadJoin($gameId, $userId)) { //既没有围观，也没有加入比赛
            $gameLooker = new GameLooker();
            $gameLooker->game_id = $gameId;
            $gameLooker->user_id = $userId;
            $gameLooker->save();
            return true;
        }
        return false;
    }

    //删除围观者
    public function delLooker($userId, $gameId)
    {
        $looker = GameLooker::query()->where(['game_id' => $gameId, 'user_id' => $userId])->first();
        if (!$looker->delete()) {
            return false;
        }
        return true;
    }

    //用户接受邀请加入比赛
    public function agreeJon($gameId, $userId)
    {
        //同意加入后，新增一条记录
        $gameUser = new GameUser();
        $gameUser->game_id = $gameId;
        $gameUser->user_id = $userId;
        $gameUser->type = 1;
        $gameUser->save();

        return true;
    }

    //场地球洞信息
    public function getHolesInfo($gameId)
    {
        $gameInfo = GameModel::find($gameId);
        $info = Course::with(['course_holes' => function ($query) {
            $query->select('*')->orderBy('sort', 'asc');
        }])->where('id', $gameInfo->course_id)->first();

        $resVal = [];
        foreach ($info['course_holes'] as $key => $val) {
            $resVal['sort'] = $val['sort'];
            $resVal['hole_id'] = $val['id'];
            $resVal['par'] = $val['par'];
            $resVal['difficulty'] = $val['difficulty'];
            $resVal['gold_dis'] = $val['gold_dis'];
            $resVal['blue_dis'] = $val['blue_dis'];
            $resVal['white_dis'] = $val['white_dis'];
            $resVal['red_dis'] = $val['red_dis'];
            unset($val['id']);
            unset($val['course_id']);
            $info['course_holes'][$key] = $resVal;
        }

        $data['name'] = $info['name'];
        $data['img'] = $info['img'];
        $data['course_id'] = $gameInfo->course_id;
        $data['date'] = date('m-d H:i', strtotime($gameInfo->begin_time));
        $data['scale'] = $gameInfo['scale'];
        $data['course_holes'] = $info['course_holes'];

        return $data;
    }

    //比赛进行中的球员成绩
    public function getOngoingGameScores($gameId)
    {
        $list = self::getGameUsers($gameId);

        foreach ($list as $key => $val) {
            $data = self::userScores($val['game_user_id']);
            $val['bf9_score'] = $data['bf9_score'];
            $val['af9_score'] = $data['af9_score'];
            $val['total_score'] = $data['total_score'];
            $val['scores'] = $data['scores'];

            $list[$key] = $val;
        }
        return $list;
    }

    //比赛进行中的球员分数
    private function userScores($gameUserId)
    {
        $holes = self::getCousesHoleInfo($gameUserId);

        //用户打球纪录（可能只打了几个，也可能全部打完了）
        $info = GameUser::with(['game_user_scores' => function ($query) {
            $query->select('game_user_id', 'change_user_id', 'hole_id', 'score')->orderBy('id', 'asc');
        }])->where('id', $gameUserId)->select('id')->first()->toArray();

        foreach ($holes as $key => $val) {
            $val['score'] = 0; //追加一个默认数组元素
            if (!empty($info['game_user_scores'])) {
                foreach ($info['game_user_scores'] as $k => $v) {
                    if ($val['hole_id'] == $v['hole_id']) { //如果用户已打过这个球洞
                        $val['score'] = $v['score'];
                        $val['change_user_id'] = $v['change_user_id'];
                    }
                }
            }
            $holes[$key] = $val;
        }
        //标准杆总数和杆差
        $holeInfo = self::getTotalParAndScore($holes);

        $data = [
            'bf9_par' => $holeInfo['bf_par'],
            'bf9_score' => $holeInfo['bf_score'],
            'af9_par' => $holeInfo['af_par'],
            'af9_score' => $holeInfo['af_score'],
            'total_score' => $holeInfo['total_score'],
            'total_par' => $holeInfo['bf_par'] + $holeInfo['af_par'],
            'scores' => $holes
        ];

        return $data;
    }

    //比赛中的所有球员
    public static function getGameUsers($gameId)
    {
        //真球员
        $list1 = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->join('users as u', 'u.id', '=', 'gu.user_id')
            ->where('gu.game_id', $gameId)
            ->whereNull('u.deleted_at')
            ->where('gu.status', 1)
            ->whereNull('gu.deleted_at')
            ->where('gu.type', 1)
            ->select('u.name', 'u.sex', 'u.avatar', 'u.is_auth', 'gu.id as game_user_id', 'gu.user_id', 'gu.type', 'level')
            ->orderBy('gu.id', 'asc')
            ->get();

        if($list1->isEmpty()){
            $list1 = [];
        }else{
            $list1 = $list1->toArray();
        }
        foreach ($list1 as $key => &$val) {
            $val['have_user_course'] = UserService::haveUserCourse($val['user_id']);
        }

        //虚拟球员
        $list2 = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->join('game_fictitious as gf', 'gf.id', '=', 'gu.user_id')
            ->where('gu.game_id', $gameId)
            ->whereNull('gu.deleted_at')
            ->where('gu.type', 2)
            ->select('gf.name', 'gf.sex', 'gf.avatar', 'gu.user_id', 'gu.id as game_user_id', 'gu.type', 'level')
            ->orderBy('gu.id', 'asc')
            ->get();
        if($list2->isEmpty()){
            $list2 = [];
        }else{
            $list2 = $list2->toArray();
        }
        foreach ($list2 as $key2 => &$val2) {
            $val2['is_auth'] = 0;
            $val2['have_user_course'] = 0;
        }

        $list = array_merge($list1, $list2);

        return $list;
    }

    //获取比赛全部球员的成绩
    public static function getAllScores(array $list)
    {
        foreach ($list as $key => $val) {
            $val['user_scores'] = self::getGameUserScore($val['game_user_id']);
            $list[$key] = $val;
        }
        return $list;
    }

    //通过 $gameUserId 获取比赛相关的 18个球洞
    private static function getCousesHoleInfo($gameUserId)
    {
        $holes = CourseHole::query()->from('course_holes as ch')
            ->leftJoin('courses as c', 'c.id', '=', 'ch.course_id')
            ->leftJoin('games as g', 'g.course_id', '=', 'c.id')
            ->leftJoin('game_users as gu', 'gu.game_id', '=', 'g.id')
            ->where('gu.id', $gameUserId)
            ->select('ch.sort', 'ch.id as hole_id', 'ch.par', 'ch.difficulty')
            ->orderBy('ch.sort', 'asc')
            ->get();

        if($holes->isEmpty()){
            return [];
        }

        return $holes->toArray();
    }

    //获取指定球员的比赛成绩
    public static function getGameUserScore(int $gameUserId)
    {
        $redisKey = 'game_user_score:' . $gameUserId;
        if ($data = Redis::get($redisKey)) {
            return json_decode($data, true);
        }

        //查出比赛相关的 18 个 球洞
        $holes = self::getCousesHoleInfo($gameUserId);

        //用户打球纪录（可能只打了几个，也可能全部打完了）
        $info = GameUser::with(['game_user_scores' => function ($query) {
            $query->select('game_user_id', 'change_user_id', 'hole_id', 'score')->orderBy('id', 'asc');
        }])->where('id', $gameUserId)->select('id')->first();

        $info = $info ? $info->toArray() : [];

        foreach ($holes as $key => $val) {
            $val['score'] = 0; //追加一个默认数组元素
            if (!empty($info['game_user_scores'])) {
                foreach ($info['game_user_scores'] as $k => $v) {
                    if ($val['hole_id'] == $v['hole_id']) { //如果用户已打过这个球洞
                        $val['score'] = $v['score'];
                    }
                }
            }
            $holes[$key] = $val;
        }
        //标准杆总数和杆差
        $holeInfo = self::getTotalParAndScore($holes);

        $data = [
            'game_user_id' => $gameUserId,
            'user_name' => self::getUserByGameUserId($gameUserId),
            'bf9_par' => $holeInfo['bf_par'],
            'bf9_score' => $holeInfo['bf_score'],
            'af9_par' => $holeInfo['af_par'],
            'af9_score' => $holeInfo['af_score'],
            'total_score' => $holeInfo['total_score'],
            'total_par' => $holeInfo['bf_par'] + $holeInfo['af_par'],
            'scores' => $holes
        ];
        //如果比赛结束了，将这个人的比赛成绩存入 redis （比赛结束后，成绩不会再变化）
        $gameId = GameUser::where('id', $gameUserId)->value('game_id');
        $endTime = Game::where('id', $gameId)->value('end_time');
        if (!empty($endTime)) {
            Redis::setex($redisKey, 30 * 24 * 3600, json_encode($data));
        }

        return $data;
    }

    //通过 game_user_id 获取球员名称
    public static function getUserByGameUserId($gameUserId)
    {
        $userId = GameUser::where('id', $gameUserId)->value('user_id');

        return User::where('id', $userId)->value('name');
    }

    protected static function getTotalParAndScore(array $arr)
    {
        $bf_par = $bf_score = 0;
        $af_par = $af_score = 0;

        for ($i = 0; $i < 18; $i++) {
            if (isset($arr[$i]['par'])) {
                if ($i < 9) {
                    $bf_par += $arr[$i]['par'];
                    $bf_score += $arr[$i]['score'];
                } else {
                    $af_par += $arr[$i]['par'];
                    $af_score += $arr[$i]['score'];
                }
            }
        }
        $total_score = $bf_score + $af_score; //总成绩

        return ['bf_par' => $bf_par, 'bf_score' => $bf_score, 'af_par' => $af_par, 'af_score' => $af_score, 'total_score' => $total_score];
    }

    //比赛计分
    public function changeScore($gameUserId, $holeId, $score)
    {
        $map = ['game_user_id' => $gameUserId, 'hole_id' => $holeId];

        //非比赛成员，无权修改
        if (!GameUser::query()->where('id', $gameUserId)->first()) {
            throw new Exception(trans('api.UNAUTHORIZED_OPERATION'));
        }
        $scoreLog = GameUserScore::where($map)->select('hole_id', 'change_user_id', 'score')->first();
        //如果用户的球洞成绩被填写过，其他人不可以修改
        if (!empty($scoreLog) && ($scoreLog['change_user_id'] !== $this->user->id)) {
            throw new Exception(trans('api.UNAUTHORIZED_OPERATION'));
        }
        if (!empty($scoreLog) && ($score == $scoreLog['score'])) {
            throw new Exception(trans('api.UNAUTHORIZED_OPERATION'));
        }
        if ($scoreLog) {
            GameUserScore::where($map)->update(['score' => $score]);
        } else {
            $scoreLog = new GameUserScore();
            $scoreLog->game_user_id = $gameUserId;
            $scoreLog->hole_id = $holeId;
            $scoreLog->change_user_id = $this->user->id;
            $scoreLog->score = $score;
        }
        if (!$scoreLog->save()) {
            throw new Exception(trans('api.DATA_SAVE_FALSE'));
        }
        return true;
    }

    //设置 Tee 台
    public function setTee($gameUserId, $level)
    {
        $res = GameUser::where(['id' => $gameUserId])->update(['level' => $level]);
        if (!$res) {
            throw new Exception(trans('api.DATA_SAVE_FALSE'));
        }
        return true;
    }

    //同一球洞的不同球员成绩(真实球员)
    public function getHoleScores($gameUserIds, $holeId)
    {
        $list = GameUserScore::query()->from('game_user_scores as log')
            ->join('game_users as gu', 'gu.id', '=', 'log.game_user_id')
            ->join('users as u', 'u.id', '=', 'gu.user_id')
            ->where('log.hole_id', $holeId)
            ->whereIn('log.game_user_id', $gameUserIds)
            ->select(DB::raw('u.name, u.avatar, log.score, log.game_user_id'))
            ->get();

        if($list->isEmpty()){
            return [];
        }

        return $list->toArray();
    }

    //(虚拟球员)
    public function getHoleScores2($fictitiousIds, $holeId)
    {
        $list = GameUserScore::query()->from('game_user_scores as log')
            ->join('game_users as gu', 'gu.id', '=', 'log.game_user_id')
            ->join('game_fictitious as f', 'f.id', '=', 'gu.user_id')
            ->where('log.hole_id', $holeId)
            ->whereIn('log.game_user_id', $fictitiousIds)
            ->select(DB::raw('f.name, f.avatar, log.score, log.game_user_id'))
            ->get();

        if($list->isEmpty()){
            return [];
        }

        return $list->toArray();
    }

    //软件内好友 首字母排序
    public function getSoftFriends()
    {
        //获取我的好友，即相互关注的人
        $list = (new UserService())->getMyFriends();
        if($list->isEmpty()){
            return [];
        }
        $userIds = array_column($list, 'id');
        if (empty($userIds)) {
            return [];
        }
        $list = User::query()
            ->leftJoin('game_users', 'game_users.user_id', '=', 'users.id')
            ->select('users.id', 'name', 'avatar', DB::raw('count(game_users.id) as game_count'))
            ->whereIn('users.id', $userIds)
            ->where('game_users.type', 1)
            ->groupBy('users.id')
            ->get();

        if($list->isEmpty()){
            return [];
        }

        return SortService::getResultList($list);
    }

    //围观者数量 +1
    public function incLookersCount($gameId, $count = 1)
    {
        Game::where('id', $gameId)->increment('lookers', $count);
    }

    //围观者数量 -1
    public function decLookersCount($gameId, $count = 1)
    {
        Game::where('id', $gameId)->decrement('lookers', $count);
    }

    //比赛人数数量 +1
    public function incMembersCount($gameId, $count = 1)
    {
        Game::where('id', $gameId)->increment('members', $count);
    }

    //比赛人数数量 -1
    public function decMembersCount($gameId, $count = 1)
    {
        Game::where('id', $gameId)->decrement('members', $count);
    }

    //修改人数限制
    public function setScale($gameId, $num)
    {
        $game = Game::find($gameId);
        if (empty($game)) {
            throw new Exception(trans('api.NOT_EXIST'));
        }
        $count = GameUser::where('game_id', $gameId)->where('status', 1)->whereNull('deleted_at')->count();
        if ($num < $count || $num > $game->game_types->scale) {
            throw new Exception(trans('api.COUNT_IS_WRONGFUL'));
        }
        $game->scale = $num;
        if (!$game->save()) {
            throw new Exception(trans('api.DATA_SAVE_FALSE'));
        }

        return true;
    }

    //添加虚拟球手
    public function addFictitious($request, $gameUser, $gameFictitiou)
    {
        $gameFictitiou->name = $request->name;
        $gameFictitiou->sex = $request->sex;
        $gameFictitiou->game_id = $request->game_id ?: 0;
        $gameFictitiou->save();

        if ($request->game_id) { //已创建的比赛添加虚拟球手
            $gameUser->user_id = $gameFictitiou->id; //虚拟球手的id
            $gameUser->game_id = $request->game_id;
            $gameUser->level = $request->level;
            $gameUser->status = 1;
            $gameUser->type = 2; //1 正常球手，2虚拟
            $gameUser->save();
        }

        if (!$gameFictitiou->save()) {
            return false;
        }
        return $gameFictitiou->id;
    }

    //获取精彩时刻
    public function getWonderfulMoment()
    {
        $list = (new GameUserScore())->setTable('gs')->from('game_user_scores as gs')
            ->join('course_holes as ch', 'ch.id', '=', 'gs.hole_id')
            ->join('game_users as gu', 'gu.id', '=', 'gs.game_user_id')
            ->join('users as u', 'u.id', '=', 'gu.user_id')
            ->where('gu.type', 1)//真实球手
            ->whereColumn('ch.par', '>', 'gs.score')
            ->select('u.name', 'u.avatar', 'ch.par', 'gs.score', DB::raw('(ch.par - gs.score) as diff_par'))
            ->orderBy('gu.id', 'desc')
            ->limit(10)
            ->get();

        if(empty($list)){
            return [];
        }
        $list = $list->toArray();

        foreach ($list as $key => $val) {
            $val['speed'] = 1;
            switch ($val['diff_par']) {
                case 0: //和标准杆一样
                    $val['speed'] = 3;
                    $val['nickname'] = trans('api.PAR');
                    break;
                case 1: //比标准杆少 1
                    $val['speed'] = 2;
                    $val['nickname'] = trans('api.BIRD');
                    break;
                case 2: //比标准杆少 2
                    $val['nickname'] = trans('api.EAGLE');
                    break;
                case 3: //比标准杆少 3
                    $val['nickname'] = trans('api.EAGLE_PLUS');
                    break;
            }
            if ($val['score'] == 1) { //一杆进洞
                $val['nickname'] = trans('api.HIO');
            }
            unset($val['diff_par']);
            $val['is_total'] = 0;

            $list[$key] = $val;
        }

        $data = [
            'single' => $list ?: [],
            'total' => self::ovarallScore(),
        ];

        return $data;
    }

    //总干差(比赛结束的)
    protected static function ovarallScore()
    {
        $list = [
            [
                "name" => "euser2",
                "avatar" => "https://easygolf.oss-ap-southeast-6.aliyuncs.com/golf/2022-03/04/4a47e8074e63d0b68afe.png",
                "total_score" => "60",
                "nickname" => trans('api.OVERALL') . " 60",
                "speed" => 1,
                "is_total" => 1
            ],
            [
                "name" => "euser2",
                "avatar" => "https://easygolf.oss-ap-southeast-6.aliyuncs.com/golf/2022-03/04/4a47e8074e63d0b68afe.png",
                "total_score" => "75",
                "nickname" => trans('api.OVERALL') . " 75",
                "speed" => 1,
                "is_total" => 1
            ],
            [
                "name" => "euser2",
                "avatar" => "https://easygolf.oss-ap-southeast-6.aliyuncs.com/golf/2022-03/04/4a47e8074e63d0b68afe.png",
                "total_score" => "80",
                "nickname" => trans('api.OVERALL') . " 80",
                "speed" => 1,
                "is_total" => 1
            ],
            [
                "name" => "Euser158811",
                "avatar" => "https://easygolf.oss-ap-southeast-6.aliyuncs.com/golf/2022-03/28/718c16b792db4c4ea5bf.JPG",
                "total_score" => "85",
                "nickname" => trans('api.OVERALL') . " 85",
                "speed" => 2,
                "is_total" => 1
            ],
            [
                "name" => "we",
                "avatar" => "https://zhengda.oss-accelerate.aliyuncs.com/golf/images/3772ac1b95b11c00e3663e3ae5fa6c9f.png",
                "total_score" => "88",
                "nickname" => trans('api.OVERALL') . " 88",
                "speed" => 2,
                "is_total" => 1
            ],
            [
                "name" => "euser1",
                "avatar" => "https://zhengda.oss-accelerate.aliyuncs.com/golf/images/8bdab5d0e82a45cf6b1a5cf860a3a136.png",
                "total_score" => "90",
                "nickname" => trans('api.OVERALL') . " 90",
                "speed" => 2,
                "is_total" => 1
            ],
            [
                "name" => "alike",
                "avatar" => "https://easygolf.oss-ap-southeast-6.aliyuncs.com/golf/2022-03/28/98857c2657762e775385.jpeg",
                "total_score" => "92",
                "nickname" => trans('api.OVERALL') . " 92",
                "speed" => 3,
                "is_total" => 1
            ],
            [
                "name" => "alike",
                "avatar" => "https://easygolf.oss-ap-southeast-6.aliyuncs.com/golf/2022-03/28/98857c2657762e775385.jpeg",
                "total_score" => "96",
                "nickname" => trans('api.OVERALL') . " 96",
                "speed" => 3,
                "is_total" => 1
            ],
            [
                "name" => "Euser依栏静望",
                "avatar" => "https://zhengda.oss-accelerate.aliyuncs.com/golf/images/5a2d773ada20093e0522eb23114d662f.png",
                "total_score" => "99",
                "nickname" => trans('api.OVERALL') . " 99",
                "speed" => 3,
                "is_total" => 1
            ],
            [
                "name" => "Euser依栏静望",
                "avatar" => "https://zhengda.oss-accelerate.aliyuncs.com/golf/images/5a2d773ada20093e0522eb23114d662f.png",
                "total_score" => "100",
                "nickname" => trans('api.OVERALL') . " 100",
                "speed" => 3,
                "is_total" => 1
            ]
        ];

        return $list;

        $redisKey = 'ovarallScore';

        if ($list = Redis::get($redisKey)) {
            return json_decode($list, true);
        }

        $gameIds = Game::query()
            ->whereNull('deleted_at')
            ->whereNotNull('end_time')
            ->pluck('id')
            ->toArray();

        $list = User::query()
            ->join('game_users as gu', 'gu.user_id', '=', 'users.id')
            ->join('game_user_scores as gs', 'gs.game_user_id', '=', 'gu.id')
            ->where('gu.type', 1)//真实球手
            ->whereIn('gu.game_id', $gameIds)
            ->select('users.name', 'users.avatar', DB::raw('SUM(gs.score) as total_score'))
            ->groupBy('gs.game_user_id')
            ->orderBy('gs.created_at', 'desc')
            ->limit(10)
            ->get()
            ->toArray();

        foreach ($list as $key => $val) {
            $val['nickname'] = trans('api.OVERALL') . $val['total_score'];

            if ($val['total_score'] <= 80) {
                $val['speed'] = 1; //慢
            } elseif ($val['total_score'] <= 90) {
                $val['speed'] = 2; //常规
            } else {
                $val['speed'] = 3; //快
            }
            $val['is_total'] = 1;

            $list[$key] = $val;
        }

        if (!empty($list)) {
            Redis::setex($redisKey, 3600, json_encode($list)); //精彩时刻总杆缓存 1 小时，如果有比赛结束，会清除缓存
        }

        return $list ?: [];
    }

    //获取我打过的比赛 array $gameIds
    public function getMyGameIds($userId)
    {
        $gameIds = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->join('games', 'games.id', '=', 'gu.game_id')
            ->where('user_id', $userId)
            ->where('gu.type', 1)
            ->whereNull('gu.deleted_at')
            ->whereNotNull('games.end_time')
            ->orderBy('end_time', 'desc')
            ->pluck('games.id')
            ->toArray();

        return $gameIds;
    }

    //打球纪录
    public function getHistoryGames($userId)
    {
        $gameIds = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->join('games', 'games.id', '=', 'gu.game_id')
            ->where('user_id', $userId)
            ->where('games.begin_time', '<', Carbon::now())
            ->where('gu.type', 1)
            ->whereNull('gu.deleted_at')
            ->whereNotNull('games.end_time')
            ->orderBy('begin_time', 'desc')
            ->pluck('games.id')
            ->toArray();

        $list = [];
        if (empty($gameIds)) {
            return $list;
        }
        foreach ($gameIds as $id) {
            $info = Game::getGameInfo($id);
            $info['game_type'] = $info['game_type'] . '(' . $info['actual_num'] . ')';
            $list[] = $info;
        }
        $resultList = [];
        foreach ($list as $key => $val) {
            $ym = date('Y-m', strtotime($val['begin_time']));
            $resultList[$ym][] = $val;
        }

        return array_values($resultList);
    }

    //获取我的指定比赛的成绩
    public function getGameScore($gameId, $userId)
    {
        $gameUserId = GameUser::query()->where(['game_id' => $gameId, 'user_id' => $userId])->value('id');
        if (!$gameUserId) {
            return [];
        }
        $scoreData = (new GameService())->getGameUserScore($gameUserId);
        $scoreData['game_info'] = Game::getGameInfo($gameId);

        return $scoreData;
    }

    //打过的球场
    public function getHistoryCourses($userId, $page)
    {
        $offset = ($page - 1) * 10;
        $list = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->join('games as g', 'g.id', '=', 'gu.game_id')
            ->join('courses as c', 'c.id', '=', 'g.course_id')
            ->where('user_id', $userId)
            ->where('gu.type', 1)
            ->whereNull('gu.deleted_at')
            ->whereNotNull('g.end_time')
            ->select('c.id', 'c.name', 'c.img', 'c.address', DB::raw('COUNT(c.id) as count'))
            ->groupBy('c.id')
            ->offset($offset)
            ->limit(20)
            ->orderBy('count', 'desc')
            ->get();

        if(empty($list)){
            return [];
        }

        return $list->toArray();
    }

    //获取球场比赛记录
    public function getCourseGames($courseId, $offset, $limit)
    {
        $info = Course::where('id', $courseId)->select('name', 'img')->first()->toArray();
        $gameList = Game::where('course_id', $courseId)
            ->whereNull('deleted_at')
            ->select('id', 'begin_time')
            ->offset($offset)
            ->limit($limit)
            ->orderBy('begin_time', 'desc')
            ->get()
            ->toArray();
        $list = [];
        foreach ($gameList as $key => $val) {
            $gameInfo = Game::getGameInfo($val['id']);
            unset($gameInfo['course_name']);
            $list[$gameInfo['month']][] = $gameInfo;
        }
        $list = array_values($list);
        $data = [
            'name' => $info['name'],
            'img' => $info['img'],
            'game_count' => count($gameList),
            'game_list' => $list,
        ];

        return $data;
    }

    //用户哪个月份打过球，获取 game_id, month 数组
    public function getGameMonth($userId)
    {
        $gameIds = (new GameService())->getMyGameIds($userId); //已结束的
        if (empty($gameIds)) {
            return [];
        }
        $monthGameIds = [];
        $list = Game::query()->whereIn('id', $gameIds)->select('id', 'begin_time')->orderBy('begin_time', 'desc')->get()->toArray();
        foreach ($list as $key => $val) {
            $arr['game_id'] = $val['id'];
            $arr['month'] = date('Y-m', strtotime($val['begin_time']));
            $monthGameIds[] = $arr;
        }

        return $monthGameIds;
    }

    //获取每个月份分数最高的那场比赛信息 $month 格式：1970-01
    public function getMonthBestScore($month, $userId)
    {
        $starMonth = Carbon::parse($month)->startOfMonth()->toDateTimeString();
        $endMonth = Carbon::parse($month)->endOfMonth()->toDateTimeString();

        $gameUserIds = (new GameUser())->setTable('gu')->from('game_users as gu')
            ->join('games', 'games.id', '=', 'gu.game_id')
            ->where('user_id', $userId)
            ->where('gu.type', 1)
            ->whereBetween('games.begin_time', [$starMonth, $endMonth])
            ->whereNull('games.deleted_at')
            ->whereNull('gu.deleted_at')
            ->whereNotNull('games.end_time')
            ->orderBy('games.begin_time', 'desc')
            ->pluck('gu.id as game_user_id')
            ->toArray();

        //存放用户指定月份 每一场比赛成绩
        $allScores = [];
        foreach ($gameUserIds as $gameUserId) {
            $scores = (new GameService())->getGameUserScore($gameUserId);
            if ($scores['total_score']) { //总杆数为零的比赛不参与统计
                $allScores[] = $scores;
            }
        }
        if (empty($allScores)) {
            return $allScores;
        }

        $sortKey = array_column($allScores, 'total_score');
        //排序后 获取 $sortKey 值最小的哪个，即第一个
        array_multisort($sortKey, SORT_ASC, $allScores);
        $best = array_shift($allScores);
        $gameId = GameUser::where('id', $best['game_user_id'])->value('game_id');
        //比赛信息
        $gameInfo = Game::getGameInfo($gameId);
        //不需要球员，去掉更简洁
        unset($gameInfo['players']);
        //最后把比赛信息放到开头
        return array_merge(['game_info' => $gameInfo], $best);
    }


}
