<?php declare(strict_types=1);
/**
 * This file is part of the SplendidBear Websites' projects.
 *
 * Copyright (c) 2019 @ www.splendidbear.org
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace App\Repository;

use App\Entity\PlayedGame;
use App\Entity\User;
use DateTime;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * Class PlayedGameRepository
 *
 * @package  App\Repository
 * @author   Lang <https://www.splendidbear.org>
 * @category Class
 * @license  https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
 * @link     www.splendidbear.org
 * @since    2019. 10. 27.
 *
 * @method PlayedGame|null find($id, $lockMode = null, $lockVersion = null)
 * @method PlayedGame|null findOneBy(array $criteria, array $orderBy = null)
 * @method PlayedGame[]    findAll()
 * @method PlayedGame[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 * @method PlayedGame|null findOneByGameAssoc(mixed $gameAssoc)
 * @method PlayedGame|null findOneByUuid(\Symfony\Component\Uid\Uuid $uuid)
 */
class PlayedGameRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, PlayedGame::class);
    }

    public function findFinishedForUserSince(User $user, DateTime $since): array
    {
        $qb = $this->createQueryBuilder('g');

        return $qb
            ->where($qb->expr()->andX(
                $qb->expr()->orX(
                    $qb->expr()->eq('g.red', ':u'),
                    $qb->expr()->eq('g.blue', ':u'),
                ),
                $qb->expr()->orX(
                    $qb->expr()->isNotNull('g.redPoints'),
                    $qb->expr()->isNotNull('g.resign'),
                ),
                $qb->expr()->gte('g.updated', ':since'),
            ))
            ->setParameter('u', $user)
            ->setParameter('since', $since)
            ->orderBy('g.updated', 'ASC')
            ->getQuery()
            ->getResult();
    }

    public function findRecentFinishedForUser(User $user, int $limit = 10): array
    {
        $qb = $this->createQueryBuilder('g');

        return $qb
            ->addSelect('rr', 'bb', 'ra', 'ba')
            ->leftJoin('g.red', 'rr')
            ->leftJoin('g.blue', 'bb')
            ->leftJoin('g.redAnon', 'ra')
            ->leftJoin('g.blueAnon', 'ba')
            ->where($qb->expr()->orX(
                $qb->expr()->eq('g.red', ':u'),
                $qb->expr()->eq('g.blue', ':u'),
            ))
            ->setParameter('u', $user)
            ->orderBy('g.updated', 'DESC')
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }

    /**
     * Any legitimately waiting game was updated within the last 10 minutes.
     * Abandoned games are stamped with updated = 2000-01-01, so they fail this filter.
     */
    public function findWaitingGames(int $limit = 20): array
    {
        $qb = $this->createQueryBuilder('p');

        return $qb
            ->where($qb->expr()->isNull('p.resign'))
            ->andWhere($qb->expr()->gt('p.updated', ':cutoff'))
            ->andWhere($qb->expr()->orX(
                $qb->expr()->andX(
                    $qb->expr()->orX(
                        $qb->expr()->isNotNull('p.red'),
                        $qb->expr()->isNotNull('p.redAnon'),
                    ),
                    $qb->expr()->isNull('p.blue'),
                    $qb->expr()->isNull('p.blueAnon'),
                ),
                $qb->expr()->andX(
                    $qb->expr()->orX(
                        $qb->expr()->isNotNull('p.blue'),
                        $qb->expr()->isNotNull('p.blueAnon'),
                    ),
                    $qb->expr()->isNull('p.red'),
                    $qb->expr()->isNull('p.redAnon'),
                ),
            ))
            ->orderBy('p.updated', 'DESC')
            ->setParameter('cutoff', new DateTime('-10 minutes'))
            ->setMaxResults($limit)
            ->getQuery()
            ->getResult();
    }
}
