<?php

namespace DMD\LaLigaApi\Service\Season\createGameCalendar;

use DMD\LaLigaApi\Dto\GameDto;
use DMD\LaLigaApi\Dto\TeamDto;
use DMD\LaLigaApi\Entity\Game;
use DMD\LaLigaApi\Entity\League;
use DMD\LaLigaApi\Entity\Season;
use DMD\LaLigaApi\Entity\Team;
use DMD\LaLigaApi\Entity\User;
use DMD\LaLigaApi\Exception\ValidationException;
use DMD\LaLigaApi\Repository\LeagueRepository;
use DMD\LaLigaApi\Repository\SeasonRepository;
use DMD\LaLigaApi\Service\Common\AuthorizeRequest;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Security\Core\User\UserInterface;

class HandleCreateGameCalendarRequest
{
    public function __construct(
        public AuthorizeRequest $authorizeRequest,
        public Security $security,
        public LeagueRepository $leagueRepository,
        public SeasonRepository $seasonRepository
    )
    {
    }

    /**
     * @throws ValidationException
     * @throws \Exception
     */
    public function __invoke(Request $request, int $leagueId, int $seasonId): JsonResponse
    {
        $this->authorizeRequest->authorizeLeaguePresident($leagueId);
        $leaguePresidentEntity = $this->security->getUser();
        $leagueEntity = $this->leagueRepository->find($leagueId);
        $seasonEntity = $this->seasonRepository->findOneBy([
            'active' => true,
            'id' => $seasonId,
            'league' => $leagueEntity
        ]);
        $this->validateEntities($leaguePresidentEntity, $leagueEntity, $leagueId, $seasonEntity);
        $teamEntityList = ($seasonEntity->getTeams())->toArray();
        $requestArray = $request->toArray();
        $numberOfTeams = count($teamEntityList);
        $numberOfWeeks = (2 * ($numberOfTeams * ($numberOfTeams - 1))) / $requestArray['gamesPerWeek'];
        $fromDate = new \DateTime($requestArray['fromDate'], new \DateTimeZone('Europe/Madrid'));
        $fromDate = match($fromDate->format('l')){
            'Saturday', 'Sunday' => $fromDate->modify('next monday'),
            default => $fromDate
        };
        $totalWeeks = $this->weeksBetweenTwoDates(
            new \DateTime($request->toArray()['fromDate'], new \DateTimeZone('Europe/Madrid')),
            new \DateTime($request->toArray()['toDate'], new \DateTimeZone('Europe/Madrid'))
        );
        $currentWeek = 1;
        $currentGame = 1;
        $totalGames = (count($teamEntityList) * ((count($teamEntityList) - 1)) / 2) * ($request->toArray()['gamesBetweenTeams'] ?? 2); //todo con esto puedes validar si el rango de fecha es suficiente para que se jueguen todos los juegos
        $calendarInWeeks = [];
        $gameDtoList = [];
        $firstDayOfWeek = new \DateTime($request->toArray()['fromDate']);
        $lastDayOfWeek = (clone $firstDayOfWeek)->modify('+7 days'); //todo forzar o dar la opcion de que la semana siguiente empiece el lunes
        while ($currentWeek <= $totalWeeks)
        {
            $gamesForCurrentWeekList = [];
            for ($i = 0; $i < $request->toArray()['gamesPerWeek']; $i++)
            {
                $gameEntity = $this->generateCalendar(
                    $teamEntityList->toArray(),
                    $currentGame,
                    $request->toArray()['unavailableLeagueDays'],
                    $request->toArray()['specificDatesNotAvailable']
                );
                $gamesForCurrentWeekList[] = $gameEntity;
                $currentGame++;
            }
            $calendar[$currentWeek] = $gamesForCurrentWeekList;
            $currentWeek++;
        }
        foreach ($calendar as $gamesForWeekList)
        {
            foreach ($gamesForWeekList as $gameEntity)
            {
                $gameDto = new GameDto();
                $gameDto->fillFromObject($gameEntity);
                $gameDtoList[] = $gameDto->toArray();
            }
        }

        return new JsonResponse([
            'success' => true,
            'games' => $gameDtoList
        ]);
    }

    public function validateEntities(?UserInterface $leaguePresidentEntity, ?League $leagueEntity, int $leagueId, ?Season $seasonEntity): void
    {
        if (!($leaguePresidentEntity instanceof User))
        {
            throw new HttpException(Response::HTTP_UNAUTHORIZED, 'Forbidden');
        }
        if (!($leagueEntity instanceof League))
        {
            throw new HttpException(Response::HTTP_UNAUTHORIZED, "Liga con ID $leagueId no ha sido encontrada");
        }
        if (!($seasonEntity instanceof Season))
        {
            throw new HttpException(Response::HTTP_UNAUTHORIZED, "La liga no tiene temporadas activas");
        }
    }

    /**
     * @throws ValidationException
     */
    public function weeksBetweenTwoDates(\DateTime $fromDate, \DateTime $toDate): float
    {
        if ($fromDate > $toDate)
        {
            $toDateString = $toDate->format('Y-m-d');
            $fromDateString = $fromDate->format('Y-m-d');
            throw new ValidationException(["$toDateString no puede ser anterior a $fromDateString"]);
        }
        return floor($fromDate->diff($toDate)->days/7);
    }

    public function generateCalendar(array $teamEntityArray, array $daysNotAvailable, array $specificDatesNotAvailable, int $totalGames): Game
    {
        $gameDtoList = [];
        //generar los posibles juegos
        /**
         * @var Team $awayTeamEntity
         * @var Team $homeTeamEntity
         */
        foreach ($teamEntityArray as $homeTeamEntity)
        {
            foreach ($teamEntityArray as $awayTeamEntity)
            {
                if ($homeTeamEntity->getId() !== $awayTeamEntity->getId())
                {
                    $gameDtoList[$homeTeamEntity->getName().':'. $awayTeamEntity->getName()] = $this->createGameDto(
                        homeTeamEntity: $homeTeamEntity,
                        awayTeamEntity: $awayTeamEntity
                    );
                    $gameDtoList[$awayTeamEntity->getName().':'. $homeTeamEntity->getName()] = $this->createGameDto(
                        homeTeamEntity: $awayTeamEntity,
                        awayTeamEntity: $homeTeamEntity
                    );
                    if (count($gameDtoList) == $totalGames)
                    {
                        break;
                    }
                }
            }
        }

        //asumiendo que tengo un juego por semana por cada equipo y 3 juegos, dame todas las posibles fechas
        //puedes tener un from date o tomas hoy de fecha de partida,
        if (empty($requestArray['fromDate']))
        {
            $fromDate = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Madrid'));
        }
        else
        {
            $fromDate = \DateTimeImmutable::createFromFormat('Y-m-d', $requestArray['fromDate'], new \DateTimeZone('Europe/Madrid'));
        }
//        $totalGames = count($gameDtoList);
//        $totalWeeks = $totalGames/$requestArray['gamesPerWeek'];
//        $toDate = $fromDate->modify("+$totalWeeks weeks");
//        return $game;
    }

    private function createGameDto(Team $homeTeamEntity, Team $awayTeamEntity): GameDto
    {
        $gameDto = new GameDto();
        $homeTeamDto = new TeamDto();
        $homeTeamDto->fillFromObject($homeTeamEntity);
        $gameDto->homeTeamDto = $homeTeamDto;
        $awayTeamDto = new TeamDto();
        $awayTeamDto->fillFromObject($awayTeamEntity);
        $gameDto->awayTeamDto = $awayTeamDto;
        return $gameDto;
    }
    private function generateWeeklyRanges($fromDate, $toDate)
    {
        $start = new \DateTime($fromDate);
        $end = new \DateTime($toDate);
        $ranges = [];

        // If the start date is not a Monday, adjust it to the next Monday
        if ($start->format('N') != 1) {
            $start->modify('next Monday');
        }

        while ($start <= $end) {
            $endOfWeek = clone $start;
            $endOfWeek->modify('next Sunday');

            // Ensure the end of the week does not exceed the end date
            if ($endOfWeek > $end) {
                $endOfWeek = clone $end;
            }

            $ranges[] = [
                'start' => $start->format('Y-m-d'),
                'end' => $endOfWeek->format('Y-m-d')
            ];

            // Move to the next Monday
            $start->modify('next Monday');
        }

        return $ranges;
    }
    function generatePossibleDates($games, $restrictions)
    {
        global $gamesByDate;

        foreach ($games as $game) {
            $team1 = $game[0];
            $team2 = $game[1];

            // Generate dates for the next 4 weeks
            for ($week = 0; $week < 4; $week++) {
                for ($day = 0; $day < 7; $day++) {
                    $date = new \DateTime();
                    $date->modify("+$week weeks");
                    $date->modify("+$day days");

                    // Check if the day is off for either team
                    if (in_array($date->format('l'), $restrictions['daysOff'])) {
                        continue;
                    }

                    // Check if the date is a specific date off for either team
                    if (in_array($date->format('Y-m-d'), $restrictions['specificDatesOff'])) {
                        continue;
                    }

                    // Check if the team has reached the maximum games per week
                    $dateStr = $date->format('Y-m-d');
                    if (isset($gamesByDate[$dateStr])) {
                        $gameCount = count($gamesByDate[$dateStr]);
                        if ($gameCount >= $restrictions['gamesPerWeek']) {
                            continue;
                        }
                    }

                    // Add the game to the list of possible dates for the date
                    $gamesByDate[$dateStr][] = [$team1, $team2];
                }
            }
        }
    }
}
