<?php

namespace App\Service;


use App\Entity\GradingGroup;
use App\Entity\Player;
use App\Entity\Score;
use App\Entity\User;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;

class TiebreakerService
{
    private $em;
    CONST SUFFIX = "R1";
    CONST NEW_SUFFIX = "R2";

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function generateRanksForGroup(GradingGroup $group) {

        $players = $group->getPlayers()->toArray();

        usort($players, function ($a, $b) { 
            if ($a->getTotal() - $b->getTotal() == 0 ) {
                return self::tieBreakerLevel2($a, $b);
            }
                return ($a->getTotal() - $b->getTotal()) > 0 ? -1 : 1;
        });

        $rank = 1;
        /**
         *  assigining ranks for finished and completed players
         */
        foreach($players as $player) {
            if ($player->getStatus() === Player::STATUS_PENDING || $player->getStatus() === Player::STATUS_PLAYING) {
                continue;
            }
            $player->setRankValue($rank++);
            $this->em->persist($player);
        }

        $this->em->flush();
        $this->em->clear();
        
    }

    public static function tieBreakerLevel2(Player $player1, Player $player2): int 
    {
        /**
         * Match lowest technical score, heighest win
         */

        $player1TechScore = self::sortedTechincalScore($player1);
        $player2TechScore = self::sortedTechincalScore($player2);

        if(!$player1TechScore || !$player2TechScore ) {
            return 1;
        }


        $player1TechScoreLowest = end($player1TechScore)->getScore();
        $player2TechScoreLowest = end($player2TechScore)->getScore();
        if(($player1TechScoreLowest - $player2TechScoreLowest == 0 )) {
            return self::tieBreakerLevel3($player1, $player2);
        }
        return ($player1TechScoreLowest - $player2TechScoreLowest) > 0 ? -1 : 1;
    }

    public static function tieBreakerLevel3(Player $player1, Player $player2): int
    {
        /**
         * Match highest technical score, heighest win
         */

        $player1TechScore = self::sortedTechincalScore($player1);
        $player2TechScore = self::sortedTechincalScore($player2);

        if(!$player1TechScore || !$player2TechScore ) {
            return 1;
        }


        $player1TechScoreHeighest = $player1TechScore[0]->getScore();
        $player2TechScoreHeighest = $player2TechScore[0]->getScore();

        return ($player1TechScoreHeighest - $player2TechScoreHeighest) > 0 ? -1 : 1;
    }
    public static function sortedTechincalScore(Player $player): array
    {
        $scores = $player->getScores()->toArray();

        $techScores = array_filter($scores, function ($s) {
            return $s->getScoreType() === Score::TECHNICAL && $s->getIsRemoved();
        });

        usort($techScores, function ($a, $b) { return ($a->getScore() - $b->getScore()) >=0 ? -1 : 1;});

        return $techScores;
    }

    public function createNextLevelGroupof8(GradingGroup $group): GradingGroup
    {
        $leveledupGroup =  clone $group;

        $groupName = sprintf("%s - %s",$group->getName(), self::NEW_SUFFIX);
        if (strpos($group->getName(), self::SUFFIX)) {
            $groupName = str_replace(self::SUFFIX, self::NEW_SUFFIX, $group->getName());
        }
        $leveledupGroup->setName($groupName);
        $leveledupGroup->setStatus(GradingGroup::STATUS_PENDING);

        $this->em->persist($leveledupGroup);
        $this->em->flush($leveledupGroup);
        $this->em->clear(GradingGroup::class);

        $playersByRank = $this->firstEightPlayers($group->getPlayers()->toArray());
        $this->AddPlayersToGroup($playersByRank, $leveledupGroup->getId());

        return $leveledupGroup;
    }

    public function firstEightPlayers(array $players): array {

        usort($players, function ($a, $b) { return ($a->getRankValue() - $b->getRankValue()) >=0 ? 1 : -1;});
        return array_slice($players, 0 ,8);
    }

    public function AddPlayersToGroup(array $players, int $groupId): void
    {
        $leveledupGroup = $this->em->getRepository(GradingGroup::class)->find($groupId);
        foreach ($players as $player) {
            $newPlayer = new Player();
            $newPlayer->setStudent($player->getStudent());
            $newPlayer->setKata($player->getKata());
            $newPlayer->setGgroup($leveledupGroup);
            $this->em->persist($newPlayer);
            $this->em->flush($newPlayer);
        }
        $this->em->clear(Player::class);
    }
}