<?php
declare(strict_types=1);

namespace App\Game\Controller;

use App\Game\Entity\Player;
use App\Game\Entity\Session;
use App\Game\Entity\SessionSetting;
use App\Game\Enum\SessionSettingType;
use App\Game\Enum\SessionStatus;
use App\Game\Repository\GameRepository;
use App\Game\Repository\PlayerRepository;
use App\Game\Repository\SessionRepository;
use App\Game\Service\GameDashboardService;
use App\Game\Service\GameResponseService;
use App\Tech\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use Symfony\Component\ExpressionLanguage\Expression;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

final class GameController extends AbstractController
{
    public function __construct(
        #[Autowire('%env(MERCURE_PUBLIC_URL)%')]
        private string $mercurePublicUrl,
        private \Doctrine\ORM\EntityManagerInterface $entityManager
    ) {
    }

    #[Route(path: '', name: 'game_dashboard', methods: ['GET', 'POST'])]
    #[IsGranted(new Expression("is_granted('ROLE_PLAYER') or is_granted('ROLE_ADMIN')"))]
    public function dashboard(
        Request $request,
        GameRepository $gameRepository,
        SessionRepository $sessionRepository,
        GameDashboardService $dashboardService,
        Security $security
    ): Response {
        $user = $security->getUser();
        $isAdmin = $this->isGranted('ROLE_ADMIN');

        if ($request->isMethod('POST')) {
            if ($request->request->has('create_session')) {
                $gameId = $request->request->get('game_id');
                $game = $gameRepository->find($gameId);

                if ($game) {
                    if ($dashboardService->createSession($game, $user, $isAdmin)) {
                        $this->addFlash('success', 'New session created!');
                    }
                }
            } elseif ($request->request->has('join_session')) {
                $inviteCode = $request->request->get('invite_code');
                if ($dashboardService->joinSession($inviteCode, $user)) {
                    $this->addFlash('success', 'Joined session successfully!');
                } else {
                    $this->addFlash('error', 'Invalid invite code or session full.');
                }
            } elseif ($request->request->has('create_invite')) {
                $sessionId = $request->request->get('session_id');
                $session = $sessionRepository->find($sessionId);

                if (!$session) {
                    $this->addFlash('error', 'Session not found.');
                    return $this->redirectToRoute('game_dashboard');
                }

                $inviteCode = $dashboardService->generateInviteCode($session, $user, $isAdmin);
                if ($inviteCode) {
                    $this->addFlash('success', 'Invite link created: ' . $inviteCode);
                }
            } elseif ($request->request->has('leave_session')) {
                $sessionId = $request->request->get('session_id');
                $session = $sessionRepository->find($sessionId);

                if ($session) {
                    if ($dashboardService->leaveSession($session, $user)) {
                        $this->addFlash('success', 'Left session successfully.');
                    } else {
                        $this->addFlash('error', 'Could not leave session (game might have started).');
                    }
                }
            } elseif ($request->request->has('start_session')) {
                $sessionId = $request->request->get('session_id');
                $session = $sessionRepository->find($sessionId);

                if ($session) {
                    if ($dashboardService->startSession($session)) {
                        $this->addFlash('success', 'Session started! Screens have been assigned.');
                    } else {
                        $this->addFlash('error', 'Could not start session. Make sure all players have joined.');
                    }
                }
            }

            return $this->redirectToRoute('game_dashboard');
        }

        return $this->render('game/dashboard.html.twig', [
            'sessions' => $dashboardService->getSessionsForUser($user),
            'availableGames' => $dashboardService->getAvailableGames($isAdmin),
        ]);
    }

    #[Route(path: '/{session}', name: 'game', methods: ['GET', 'POST'])]
    #[IsGranted(new Expression("is_granted('ROLE_PLAYER') or is_granted('ROLE_ADMIN')"))]
    #[IsGranted('SESSION_VIEW', subject: 'session')]
    public function index(
        Session $session,
        Request $request,
        Security $security,
        PlayerRepository $playerRepository,
        GameDashboardService $dashboardService,
        GameResponseService $gameResponseService
    ): Response
    {
        $user = $security->getUser();
        if (!$user instanceof User) {
            throw $this->createAccessDeniedException();
        }

        $player = $playerRepository->findOneBy(['session' => $session, 'user' => $user]);

        if ($request->isMethod('POST') && $request->request->has('toggle_ready')) {
            $dashboardService->toggleReady($session, $user);
            return $this->redirectToRoute('game', ['session' => $session->getId()]);
        }

        // Lazily pick up readiness changes from other players since our last request
        $dashboardService->checkAllPlayersReady($session);

        if ($session->getStatus() === SessionStatus::READY) {
            $isReady = false;
            if ($player) {
                $settingName = SessionSettingType::tryFrom('ReadyAtForPlayer' . $player->getScreen());
                if ($settingName) {
                    $setting = $session->getSettings()->filter(fn(SessionSetting $s) => $s->getName() === $settingName && $s->getPlayer() === $player)->first();
                    $isReady = $setting !== null;
                }
            }

            return $this->render('game/waiting.html.twig', [
                'session' => $session,
                'isReady' => $isReady,
                'mercure_public_url' => $this->mercurePublicUrl,
            ]);
        }

        $screen = $player ? $player->getScreen() : 0;
        $session_id = $session->getId();
        $lock = $player ? $gameResponseService->getPublicLockState($player) : null;
        $filesRemovalDeadline = $gameResponseService->getPublicLockedFilesDeadline($session);

        return $this->render('game/index.html.twig', [
            'session' => $session,
            'screen' => $screen,
            'session_id' => $session_id,
            'lock' => $lock,
            'filesRemovalDeadline' => $filesRemovalDeadline,
        ]);
    }

    #[Route(path: '/lost/{session}', name: 'game_lost', methods: ['GET', 'POST'])]
    #[IsGranted(new Expression("is_granted('ROLE_PLAYER') or is_granted('ROLE_ADMIN')"))]
    #[IsGranted('SESSION_VIEW', subject: 'session')]
    public function lost(
        Session $session,
        Request $request,
        Security $security,
        PlayerRepository $playerRepository
    ): Response {
        /** @var User $user */
        $user = $security->getUser();
        $player = $playerRepository->findOneBy(['session' => $session, 'user' => $user]);

        if ($request->isMethod('POST')) {
            $difficulty = $request->request->get('difficulty');
            $entertaining = $request->request->get('entertaining');
            $theme = $request->request->get('theme');
            $feedback = $request->request->get('feedback');

            // Save feedback
            if ($player) {
                $this->saveFeedback($session, $player, $difficulty, $entertaining, $theme, $feedback);
                $this->addFlash('success', 'Thank you for your feedback!');
                return $this->redirectToRoute('game_dashboard');
            }
        }

        return $this->render('game/lost.html.twig', [
            'session' => $session,
        ]);
    }

    #[Route(path: '/won/{session}', name: 'game_won', methods: ['GET', 'POST'])]
    #[IsGranted(new Expression("is_granted('ROLE_PLAYER') or is_granted('ROLE_ADMIN')"))]
    #[IsGranted('SESSION_VIEW', subject: 'session')]
    public function won(
        Session $session,
        Request $request,
        Security $security,
        PlayerRepository $playerRepository
    ): Response {
        /** @var User $user */
        $user = $security->getUser();
        $player = $playerRepository->findOneBy(['session' => $session, 'user' => $user]);

        if ($request->isMethod('POST')) {
            $difficulty = $request->request->get('difficulty');
            $entertaining = $request->request->get('entertaining');
            $theme = $request->request->get('theme');
            $feedback = $request->request->get('feedback');

            // Save feedback
            if ($player) {
                $this->saveFeedback($session, $player, $difficulty, $entertaining, $theme, $feedback);
                $this->addFlash('success', 'Thank you for your feedback!');
                return $this->redirectToRoute('game_dashboard');
            }
        }

        return $this->render('game/won.html.twig', [
            'session' => $session,
        ]);
    }

    private function saveFeedback(Session $session, Player $player, $difficulty, $entertaining, $theme, $feedback): void
    {
        $settings = [
            SessionSettingType::FEEDBACK_DIFFICULTY,
            SessionSettingType::FEEDBACK_ENTERTAINING,
            SessionSettingType::FEEDBACK_THEME,
            SessionSettingType::FEEDBACK_TEXT,
        ];

        $values = [
            $difficulty,
            $entertaining,
            $theme,
            $feedback,
        ];

        foreach ($settings as $index => $type) {
            $value = $values[$index];
            if ($value === null || $value === '') continue;

            $setting = new SessionSetting();
            $setting->setSession($session);
            $setting->setPlayer($player);
            $setting->setName($type);
            $setting->setValue((string)$value);
            $this->entityManager->persist($setting);
        }

        $this->entityManager->flush();
    }
}
