<?php
namespace App\Controller;
use App\Entity\ChatMessage;
use App\Repository\ChatMessageRepository;
use App\Repository\TeamRepository;
use App\Repository\SeasonRepository;
use App\Repository\UserRepository;
use App\Service\ChatService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Annotation\Groups;
class ChatController extends AbstractController
{
public function __construct(
private ChatService $chatService,
private ChatMessageRepository $chatMessageRepository,
private TeamRepository $teamRepository,
private SeasonRepository $seasonRepository,
private UserRepository $userRepository,
private SerializerInterface $serializer
) {}
#[Route('/api/chat/messages', name: 'api_chat_send_message', methods: ['POST'])]
public function sendMessage(Request $request): Response
{
$data = json_decode($request->getContent(), true);
$content = $data['content'] ?? null;
$type = $data['type'] ?? null;
$attachmentUrl = $data['attachmentUrl'] ?? null;
if (!$content || !$type) {
return $this->json(['error' => 'Missing required fields'], Response::HTTP_BAD_REQUEST);
}
try {
$team = null;
$season = null;
$recipient = null;
if ($type === 'team' && isset($data['teamId'])) {
$team = $this->teamRepository->find($data['teamId']);
if (!$team) {
return $this->json(['error' => 'Team not found'], Response::HTTP_NOT_FOUND);
}
} elseif ($type === 'season' && isset($data['seasonId'])) {
$season = $this->seasonRepository->find($data['seasonId']);
if (!$season) {
return $this->json(['error' => 'Season not found'], Response::HTTP_NOT_FOUND);
}
} elseif ($type === 'direct' && isset($data['recipientId'])) {
$recipient = $this->userRepository->find($data['recipientId']);
if (!$recipient) {
return $this->json(['error' => 'Recipient not found'], Response::HTTP_NOT_FOUND);
}
} elseif ($type === 'global') {
// For global messages, team, season, and recipient remain null.
// No specific ID is needed.
} else {
// This handles cases where type is not 'team', 'season', 'direct', or 'global',
// or if the required ID for those types is missing.
return $this->json(['error' => 'Invalid chat type or missing/invalid ID for the given type'], Response::HTTP_BAD_REQUEST);
}
$message = $this->chatService->sendMessage(
$content,
$type,
$team,
$season,
$recipient,
$attachmentUrl
);
return $this->json($message, Response::HTTP_CREATED, [], ['groups' => 'chat_message']);
} catch (\Exception $e) {
return $this->json(['error' => $e->getMessage()], Response::HTTP_BAD_REQUEST);
}
}
#[Route('/api/chat/team/{id}', name: 'api_chat_team_messages', methods: ['GET'])]
public function getTeamMessages(int $id, Request $request): Response
{
$team = $this->teamRepository->find($id);
if (!$team) {
return $this->json(['error' => 'Team not found'], Response::HTTP_NOT_FOUND);
}
$limit = $request->query->getInt('limit', 50);
$offset = $request->query->getInt('offset', 0);
$messages = $this->chatService->getMessages('team', $team, $limit, $offset);
return $this->json($messages, Response::HTTP_OK, [], ['groups' => 'chat_message']);
}
#[Route('/api/chat/season/{id}', name: 'api_chat_season_messages', methods: ['GET'])]
public function getSeasonMessages(int $id, Request $request): Response
{
$season = $this->seasonRepository->find($id);
if (!$season) {
return $this->json(['error' => 'Season not found'], Response::HTTP_NOT_FOUND);
}
$limit = $request->query->getInt('limit', 50);
$offset = $request->query->getInt('offset', 0);
$messages = $this->chatService->getMessages('season', $season, $limit, $offset);
return $this->json($messages, Response::HTTP_OK, [], ['groups' => 'chat_message']);
}
#[Route('/api/chat/direct/{id}', name: 'api_chat_direct_messages', methods: ['GET'])]
public function getDirectMessages(int $id, Request $request): Response
{
$recipient = $this->userRepository->find($id);
if (!$recipient) {
return $this->json(['error' => 'User not found'], Response::HTTP_NOT_FOUND);
}
$limit = $request->query->getInt('limit', 50);
$offset = $request->query->getInt('offset', 0);
$messages = $this->chatService->getMessages('direct', $recipient, $limit, $offset);
return $this->json($messages, Response::HTTP_OK, [], ['groups' => 'chat_message']);
}
#[Route('/api/chat/team/{id}/new', name: 'api_chat_new_team_messages', methods: ['GET'])]
public function getNewTeamMessages(int $id, Request $request): Response
{
$team = $this->teamRepository->find($id);
if (!$team) {
return $this->json(['error' => 'Team not found'], Response::HTTP_NOT_FOUND);
}
$since = new \DateTime($request->query->get('since', 'now'));
$messages = $this->chatService->getNewMessages('team', $team, $since);
return $this->json($messages, Response::HTTP_OK, [], ['groups' => 'chat_message']);
}
#[Route('/api/chat/season/{id}/new', name: 'api_chat_new_season_messages', methods: ['GET'])]
public function getNewSeasonMessages(int $id, Request $request): Response
{
$season = $this->seasonRepository->find($id);
if (!$season) {
return $this->json(['error' => 'Season not found'], Response::HTTP_NOT_FOUND);
}
$since = new \DateTime($request->query->get('since', 'now'));
$messages = $this->chatService->getNewMessages('season', $season, $since);
return $this->json($messages, Response::HTTP_OK, [], ['groups' => 'chat_message']);
}
#[Route('/api/chat/direct/{id}/new', name: 'api_chat_new_direct_messages', methods: ['GET'])]
public function getNewDirectMessages(int $id, Request $request): Response
{
$recipient = $this->userRepository->find($id);
if (!$recipient) {
return $this->json(['error' => 'User not found'], Response::HTTP_NOT_FOUND);
}
$since = new \DateTime($request->query->get('since', 'now'));
$messages = $this->chatService->getNewMessages('direct', $recipient, $since);
return $this->json($messages, Response::HTTP_OK, [], ['groups' => 'chat_message']);
}
#[Route('/api/chat/messages/{id}/read', name: 'api_chat_mark_read', methods: ['POST'])]
public function markAsRead(int $id): Response
{
$message = $this->chatMessageRepository->find($id);
if (!$message) {
return $this->json(['error' => 'Message not found'], Response::HTTP_NOT_FOUND);
}
$this->chatService->markAsRead($message);
return $this->json(['success' => true]);
}
#[Route('/api/chat/channels', name: 'api_chat_channels', methods: ['GET'])]
public function getChannels(): Response
{
$user = $this->getUser();
// Get all channels the user has access to
$channels = $this->chatService->getAvailableChannels($user);
// Use serialization groups to prevent circular references
return $this->json($channels, Response::HTTP_OK, [], ['groups' => 'chat:channel:list']);
}
#[Route('/api/chat/channels/{id}/messages', name: 'api_chat_channel_messages', methods: ['GET'])]
public function getChannelMessages(string $id, Request $request): Response
{
// Parse the channel ID to determine type and actual ID
list($type, $actualId) = $this->parseChannelId($id);
$limit = $request->query->getInt('limit', 50);
$offset = $request->query->getInt('offset', 0);
$messages = $this->chatService->getMessagesByChannel($type, $actualId, $limit, $offset);
return $this->json($messages, Response::HTTP_OK, [], ['groups' => 'chat_message']);
}
#[Route('/api/chat/unread-counts', name: 'api_chat_unread_counts', methods: ['GET'])]
public function getUnreadCounts(): Response
{
$user = $this->getUser();
$counts = $this->chatService->getUnreadCountsByChannel($user);
return $this->json($counts);
}
#[Route('/api/chat/channels/{id}/read', name: 'api_chat_mark_channel_read', methods: ['POST'])]
public function markChannelAsRead(string $id): Response
{
// Parse the channel ID to determine type and actual ID
list($type, $actualId) = $this->parseChannelId($id);
$this->chatService->markChannelAsRead($type, $actualId);
return $this->json(['success' => true]);
}
#[Route('/api/chat/widget-messages', name: 'api_chat_widget_messages', methods: ['GET'])]
public function getWidgetMessages(Request $request): Response
{
$user = $this->getUser();
if (!$user) {
return $this->json(['error' => 'User not authenticated'], Response::HTTP_UNAUTHORIZED);
}
// Consider a small limit for widget messages, e.g., 5 messages per channel, up to 3-5 channels
$limitPerChannel = $request->query->getInt('limit_per_channel', 3);
$maxChannels = $request->query->getInt('max_channels', 3);
try {
$messages = $this->chatService->getRecentMessagesForWidget($user, $limitPerChannel, $maxChannels);
// Ensure the response is structured as expected by ChatWidget.vue
// e.g., { "Channel Name 1": [msg1, msg2], "Channel Name 2": [msg3, msg4] }
return $this->json($messages, Response::HTTP_OK, [], ['groups' => 'chat_widget_message']); // Define a new serialization group if needed
} catch (\Exception $e) {
// Log the exception $e->getMessage()
return $this->json(['error' => 'Could not retrieve widget messages'], Response::HTTP_INTERNAL_SERVER_ERROR);
}
}
// Helper method to parse channel IDs
private function parseChannelId(string $id): array
{
if ($id === 'global') {
return ['global', null];
}
$parts = explode('-', $id);
if (count($parts) !== 2) {
throw new \InvalidArgumentException('Invalid channel ID format');
}
return [$parts[0], (int)$parts[1]];
}
#[Route('/chat', name: 'chat_index')]
public function index(): Response
{
return $this->render('chat/index.html.twig');
}
// Remove old chat routes
// #[Route('/chat/team/{id}', name: 'chat_team', methods: ['GET'])]
// ...
// #[Route('/chat/season/{id}', name: 'chat_season', methods: ['GET'])]
// ...
// #[Route('/chat/direct/{id}', name: 'chat_direct', methods: ['GET'])]
// ...
}