<?php
namespace App\Controller;
use App\Entity\Configuration;
use App\Entity\Space;
use App\Entity\User;
use App\Entity\WidgetNotes;
use App\Entity\HtFile;
use App\MDS\AvexpressBundle\Entity\AveFiles;
use App\Form\WidgetNotesType;
use App\MDS\VenuesBundle\Entity\Reservation;
use App\MDS\VenuesBundle\Entity\ReservationLoungeDetails;
use App\MDS\VenuesBundle\Entity\ReservationLoungeSimple;
use App\MDS\VenuesBundle\Entity\ReservationVisit;
use App\Service\CalendarService;
use App\Service\UserNotificationService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Contracts\Translation\TranslatorInterface;
use Google_Client;
use Google_Service_Calendar;
use Doctrine\ORM\Query;
class HomeController extends AbstractController
{
private $googleCalendar;
private $translator;
private $userNotificationService;
private SessionInterface $session;
public function __construct(TranslatorInterface $translator, UserNotificationService $userNotificationService, SessionInterface $session)
{
$redirectUri = 'https://' . ($_SERVER['HTTP_HOST'] ?? '') . '/calendar/token';
$client = new Google_Client();
$client->setApplicationName('Google Calendar API');
$client->setClientId('YOUR_GOOGLE_CLIENT_ID');
$client->setClientSecret('YOUR_GOOGLE_CLIENT_SECRET');
$client->setRedirectUri($redirectUri);
$client->addScope(Google_Service_Calendar::CALENDAR);
$guzzle = new \GuzzleHttp\Client(['curl' => [CURLOPT_SSL_VERIFYPEER => false]]);
$client->setHttpClient($guzzle);
$this->googleCalendar = $client;
$this->translator = $translator;
$this->userNotificationService = $userNotificationService;
$this->session = $session;
}
/**
* Portal principal con los 3 accesos (ManteVenues, ManteCatering, ManteAV)
* @Route("/inicio", name="ruta_inicio")
*/
public function inicioAction(AuthenticationUtils $authenticationUtils, EntityManagerInterface $em): Response
{
$configuration = $em->getRepository(Configuration::class)->findOneBy([]);
return $this->render('login/login.html.twig', [
'last_username' => $authenticationUtils->getLastUsername(),
'error' => $authenticationUtils->getLastAuthenticationError(),
'configuration' => $configuration,
]);
}
/**
* @Route("/", name="homepage")
*/
public function index(EntityManagerInterface $em, Request $request): Response
{
$user = $this->getUser();
if (!$user) {
return $this->redirectToRoute('ruta_inicio');
}
// Recuperamos el tipo de acceso actual (Venues, Catering o AV) de la sesión
$accessType = $this->session->get('current_access_type') ?? 'venues';
$tz = new \DateTimeZone('Europe/Madrid');
$from = (new \DateTimeImmutable('today', $tz))->setTime(0, 0, 0);
$to = $from->setTime(23, 59, 59);
$agendaMix = [];
$formattedReservations = [];
$formattedVisits = [];
if ($accessType === 'venues') {
// 1. FILTRADO POR EQUIPO "LEGENDS VIEW"
$isLegendTeam = (strtoupper(trim($user->getTeam() ?? '')) === 'LEGENDS VIEW');
$lSpace = null;
if ($isLegendTeam) {
$lSpace = $em->getRepository(Space::class)->createQueryBuilder('s')
->where('s.name LIKE :n')->setParameter('n', '%LEGEND%')
->setMaxResults(1)->getQuery()->getOneOrNullResult();
}
/** --- LÓGICA Módulo VENUES (Reservas y Visitas) --- */
$qbR = $em->createQueryBuilder()
->select('r.id', 'r.title', 'r.dateStart', 'r.dateEnd', 'lounge.name as loungeName', 'rls.type')
->from(Reservation::class, 'r')
->leftJoin(ReservationLoungeSimple::class, 'rls', 'WITH', 'rls.idReservation = r.id')
->leftJoin(ReservationLoungeDetails::class, 'lounge', 'WITH', 'lounge.id = rls.idLounge')
->where('r.dateStart <= :to AND COALESCE(r.dateEnd, r.dateStart) >= :from')
->andWhere('r.status <> :deleted')
->setParameter('from', $from)
->setParameter('to', $to)
->setParameter('deleted', 'Deleted')
->orderBy('r.dateStart', 'ASC');
if ($isLegendTeam && $lSpace) {
$qbR->andWhere('lounge.space = :sid')->setParameter('sid', $lSpace->getId());
}
$rawRows = $qbR->getQuery()->getArrayResult();
$reservationsMap = [];
foreach ($rawRows as $row) {
$id = $row['id'];
$type = $row['type'];
$key = $id . ($type ? '_' . $type : '');
if (!isset($reservationsMap[$key])) {
$prefix = $type ? '[' . strtoupper($type) . '] ' : '';
$reservationsMap[$key] = [
'id' => $id,
'title' => $prefix . ($row['title'] ?? 'Reserva'),
'dateStart' => $row['dateStart'],
'rooms' => [],
'tipo' => 'RESERVA',
// URL específica de Venues
'url' => $this->generateUrl('reservations_venues_edit_simple', ['id' => $id])
];
}
if ($row['loungeName']) {
$reservationsMap[$key]['rooms'][] = $row['loungeName'];
}
}
foreach ($reservationsMap as $res) {
$res['room'] = implode(', ', array_unique($res['rooms']));
$formattedReservations[] = $res;
}
// VISITAS
$qbV = $em->getRepository(ReservationVisit::class)->createQueryBuilder('v')
->where('v.dateStart <= :to AND COALESCE(v.dateEnd, v.dateStart) >= :from')
->setParameter('from', $from)
->setParameter('to', $to)
->orderBy('v.dateStart', 'ASC');
if ($isLegendTeam && $lSpace) {
$qbV->andWhere('v.space = :sid')->setParameter('sid', $lSpace->getId());
}
$visitEntities = $qbV->getQuery()->getResult();
$loungeRepo = $em->getRepository(ReservationLoungeDetails::class);
foreach ($visitEntities as $visit) {
$lName = '-';
if ($visit->getIdLoungeDetails()) {
$lounge = $loungeRepo->find($visit->getIdLoungeDetails());
$lName = $lounge ? $lounge->getName() : '-';
}
$formattedVisits[] = [
'id' => $visit->getId(),
'title' => ($visit->getTitle() ?? 'Sin título'),
'dateStart' => $visit->getDateStart(),
'room' => $lName,
'tipo' => 'VISITA',
// También enviamos la URL correcta para las visitas
'url' => $this->generateUrl('venues_edit_visit', ['id' => $visit->getId()])
];
}
$agendaMix = array_merge($formattedReservations, $formattedVisits);
usort($agendaMix, fn ($a, $b) => $a['dateStart'] <=> $b['dateStart']);
} else if ($accessType === 'catering') {
/** --- LÓGICA Módulo CATERING (Higo & Trigo + Montajes Venues) --- */
// 1. Eventos de Catering
$htFiles = $em->getRepository(HtFile::class)->createQueryBuilder('h')
->where('h.dateStart <= :to AND COALESCE(h.dateEnd, h.dateStart) >= :from')
->setParameter('from', $from)
->setParameter('to', $to)
->orderBy('h.dateStart', 'ASC')
->getQuery()->getResult();
$htEventsFormatted = [];
foreach ($htFiles as $ht) {
$htEventsFormatted[] = [
'id' => $ht->getId(),
'title' => '[CATERING] ' . ($ht->getTitle() ?? 'Sin título'),
'dateStart' => $ht->getDateStart(),
'room' => 'Servicio Catering',
'tipo' => 'RESERVA',
// URL específica de Catering
'url' => $this->generateUrl('app_ht_file_edit', ['id' => $ht->getId()])
];
}
// 2. Montajes/Desmontajes de Venues para Catering
$qbM = $em->createQueryBuilder()
->select('r.id', 'r.title', 'r.dateStart', 'lounge.name as loungeName', 'rls.type')
->from(Reservation::class, 'r')
->leftJoin(ReservationLoungeSimple::class, 'rls', 'WITH', 'rls.idReservation = r.id')
->leftJoin(ReservationLoungeDetails::class, 'lounge', 'WITH', 'lounge.id = rls.idLounge')
->where('r.dateStart <= :to AND COALESCE(r.dateEnd, r.dateStart) >= :from')
->andWhere('rls.type IN (:types)')
->andWhere('r.status <> :deleted')
->setParameter('from', $from)
->setParameter('to', $to)
->setParameter('types', ['montaje', 'desmontaje'])
->setParameter('deleted', 'Deleted');
$venueRows = $qbM->getQuery()->getArrayResult();
$venueEventsFormatted = [];
foreach ($venueRows as $row) {
$venueEventsFormatted[] = [
'id' => $row['id'],
'title' => '[' . strtoupper($row['type'] ?? 'TRABAJO') . '] ' . ($row['title'] ?? 'Reserva'),
'dateStart' => $row['dateStart'],
'room' => $row['loungeName'] ?? '-',
'tipo' => 'RESERVA',
// Mantenemos la ruta de Venues para montajes de salas
'url' => $this->generateUrl('reservations_venues_edit_simple', ['id' => $row['id']])
];
}
$agendaMix = array_merge($htEventsFormatted, $venueEventsFormatted);
usort($agendaMix, fn ($a, $b) => $a['dateStart'] <=> $b['dateStart']);
$formattedReservations = $agendaMix;
} else if ($accessType === 'av') {
/** --- LÓGICA Módulo AV (Audiovisuales) --- */
$avFiles = $em->getRepository(AveFiles::class)->createQueryBuilder('a')
->where('a.dateStart <= :to AND COALESCE(a.dateEnd, a.dateStart) >= :from')
->andWhere('a.status <> :deleted')
->setParameter('from', $from)
->setParameter('to', $to)
->setParameter('deleted', 'Deleted')
->orderBy('a.dateStart', 'ASC')
->getQuery()->getResult();
foreach ($avFiles as $av) {
$formattedReservations[] = [
'id' => $av->getId(),
'title' => $av->getTitle() ?? 'Servicio AV',
'dateStart' => $av->getDateStart(),
'room' => 'Equipos AV',
'tipo' => 'RESERVA',
// URL específica de AV (Verifica que el nombre de ruta sea exacto el de tu controlador AV)
'url' => $this->generateUrl('ave_edit_file', ['id' => $av->getId()])
];
}
$agendaMix = $formattedReservations;
}
$this->userNotificationService->checkRecentNotifications($user);
return $this->render('home/index.html.twig', [
'accessType' => $accessType,
'agenda' => $agendaMix,
'upcomingReservations' => $formattedReservations,
'upcomingVisits' => $formattedVisits,
'notifications' => $this->userNotificationService->getForModal($user)
]);
}
/**
* @Route("/calendar/catering/menu-testings", name="calendar_catering_menu_testings", methods={"GET"})
*/
public function calendarCateringMenuTestings(Request $request, CalendarService $calendar): JsonResponse
{
$fromParam = $request->query->get('start', 'first day of this month');
$toParam = $request->query->get('end', 'last day of next month');
try {
$from = new \DateTimeImmutable($fromParam);
$to = new \DateTimeImmutable($toParam);
} catch (\Exception $e) {
return new JsonResponse(['error' => 'Formato de fecha inválido'], 400);
}
// Llamamos al método renombrado
return new JsonResponse($calendar->getMenuTestingsForCalendar($from, $to));
}
/**
* @Route("/calendar/global/visits", name="calendar_global_visits", methods={"GET"})
*/
public function globalVisits(Request $request, CalendarService $calendar, EntityManagerInterface $em): JsonResponse
{
$user = $this->getUser();
$spaceIds = null;
if ($user && strtoupper(trim($user->getTeam() ?? '')) === 'LEGENDS VIEW') {
// Buscamos todos los espacios cuyo nombre sea o contenga 'LEGENDS VIEW'
$spaces = $em->getRepository(Space::class)->createQueryBuilder('s')
->where('s.name LIKE :n')
->setParameter('n', '%ESPACIO LEGENDS%')
->getQuery()
->getResult();
if (!empty($spaces)) {
$spaceIds = array_map(fn($s) => $s->getId(), $spaces);
} else {
// Seguridad: Si no encuentra el espacio, ID inexistente para que no vea nada por error
$spaceIds = [99999];
}
}
$from = new \DateTime($request->query->get('start'));
$to = new \DateTime($request->query->get('end'));
return new JsonResponse($calendar->getVisitsForCalendar($spaceIds, null, $from, $to));
}
/**
* @Route("/calendar/global/reservations", name="calendar_global_reservations", methods={"GET"})
*/
public function globalReservations(Request $request, CalendarService $calendar, EntityManagerInterface $em): JsonResponse
{
$user = $this->getUser();
$spaceId = null;
if ($user && strtoupper(trim($user->getTeam() ?? '')) === 'LEGENDS VIEW') {
$lSpace = $em->getRepository(Space::class)->createQueryBuilder('s')
->where('s.name LIKE :n')
->setParameter('n', '%LEGEND%')
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
$spaceId = $lSpace?->getId();
}
$from = new \DateTime($request->query->get('start'));
$to = new \DateTime($request->query->get('end'));
return new JsonResponse($calendar->getReservationsForCalendar($spaceId, null, $from, $to));
}
/**
* Endpoint específico para que el perfil de Catering vea solo montajes y desmontajes.
* * @Route("/calendar/catering/reservations", name="calendar_catering_reservations", methods={"GET"})
*/
public function cateringReservations(Request $request, CalendarService $calendar): JsonResponse
{
// Validamos las fechas de entrada que envía FullCalendar
$startParam = $request->query->get('start');
$endParam = $request->query->get('end');
if (!$startParam || !$endParam) {
return new JsonResponse(['error' => 'Missing date parameters'], 400);
}
try {
$from = new \DateTime($startParam);
$to = new \DateTime($endParam);
} catch (\Exception $e) {
return new JsonResponse(['error' => 'Invalid date format'], 400);
}
// Llamamos al nuevo método específico del servicio
$events = $calendar->getCateringSetupsForCalendar($from, $to);
return new JsonResponse($events);
}
/**
* @Route("/calendar/ht/events", name="calendar_ht_events", methods={"GET"})
*/
public function calendarHtEvents(Request $request, CalendarService $calendar): JsonResponse
{
$from = new \DateTimeImmutable($request->query->get('start', 'first day of this month'));
$to = new \DateTimeImmutable($request->query->get('end', 'last day of next month'));
return new JsonResponse($calendar->getHtEvents($from, $to));
}
/**
* @Route("/calendar/av/events", name="calendar_av_events", methods={"GET"})
*/
public function calendarAvEvents(Request $request, CalendarService $calendar): JsonResponse
{
$from = new \DateTimeImmutable($request->query->get('start', 'first day of this month'));
$to = new \DateTimeImmutable($request->query->get('end', 'last day of next month'));
// 1. Sacamos los eventos propios de Audiovisuales
$avEvents = $calendar->getAvEvents($from, $to, 'av');
// 2. Sacamos los montajes y desmontajes asociados a Audiovisuales
$avSetups = $calendar->getAvSetupsForCalendar($from, $to, 'av');
// 3. Los unimos en un solo array
$allEvents = array_merge($avEvents, $avSetups);
return new JsonResponse($allEvents);
}
/**
* @Route("/ChangeLanguage/{lang}", name="change_language")
*/
public function changeLanguage(Request $request, string $lang): Response
{
$this->translator->setLocale($lang);
$request->getSession()->set('_locale', $lang);
return $this->redirect($request->headers->get('referer'));
}
/**
* @Route("/calendar-full", name="calendar_full")
*/
public function calendarFull(Request $request): Response
{
$token = $request->request->get('token');
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository(User::class)->findOneByAccessKey($token);
if (!$user)
return $this->redirectToRoute('ruta_inicio');
$userId = $user->getId();
$wnotes = new WidgetNotes();
$wnotes->setDateAt(new \DateTime());
$form = $this->createForm(WidgetNotesType::class, $wnotes, [
'action' => $this->generateUrl('widget_notes_create'),
'method' => 'POST',
]);
return $this->render('home/calendar-fullscreen.html.twig', [
'form' => $form->createView(),
'user' => $userId,
'token' => $token,
]);
}
/**
* @Route("/external/calendar-reservation", name="calendar_external_reservation")
*/
public function externalCalendar(): Response
{
return $this->render('home/calendar-reservation.html.twig');
}
}