ForumChatWebSocketHandler.java

package com.wavii.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wavii.dto.forum.CreatePostRequest;
import com.wavii.dto.forum.PostResponse;
import com.wavii.model.Forum;
import com.wavii.model.User;
import com.wavii.repository.ForumMembershipRepository;
import com.wavii.repository.ForumRepository;
import com.wavii.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Component
@RequiredArgsConstructor
@Slf4j
public class ForumChatWebSocketHandler extends TextWebSocketHandler {

    private final ObjectMapper objectMapper;
    private final JwtService jwtService;
    private final UserRepository userRepository;
    private final ForumRepository forumRepository;
    private final ForumMembershipRepository membershipRepository;
    private final ForumService forumService;
    private final ChatRealtimeBroadcaster chatRealtimeBroadcaster;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        try {
            URI uri = session.getUri();
            Map<String, String> query = parseQuery(uri != null ? uri.getQuery() : null);
            String token = query.get("token");
            String forumIdRaw = query.get("forumId");
            if (token == null || forumIdRaw == null) {
                log.warn("WS forum rechazado: faltan parametros");
                session.close(CloseStatus.BAD_DATA);
                return;
            }

            User user = authenticate(token);
            UUID forumId = UUID.fromString(forumIdRaw);
            Forum forum = forumRepository.findById(forumId)
                    .orElseThrow(() -> new IllegalArgumentException("Comunidad no encontrada"));
            if (!membershipRepository.existsByForumAndUser(forum, user)) {
                throw new IllegalArgumentException("Debes unirte a la comunidad");
            }

            chatRealtimeBroadcaster.register(chatRealtimeBroadcaster.forumRoom(forumId), session);
            session.getAttributes().put("user", user);
            session.getAttributes().put("forumId", forumId);
            log.debug("WS forum conectado forum={} user={}", shortId(forumId), shortId(user.getId()));
        } catch (Exception e) {
            log.warn("WS forum rechazado: {}", e.getMessage());
            session.close(CloseStatus.BAD_DATA);
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        UUID forumId = (UUID) session.getAttributes().get("forumId");
        User user = (User) session.getAttributes().get("user");
        if (forumId == null || user == null) {
            return;
        }

        Map<String, Object> payload = objectMapper.readValue(message.getPayload(), new TypeReference<>() {});
        String content = payload.get("content") != null ? payload.get("content").toString() : "";
        PostResponse saved = forumService.createPost(forumId, new CreatePostRequest(content), user);
        chatRealtimeBroadcaster.broadcast(chatRealtimeBroadcaster.forumRoom(forumId), saved);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        chatRealtimeBroadcaster.unregister(session);
        log.debug("WS forum cerrado session={} status={}", session.getId(), status);
    }

    private User authenticate(String token) {
        String raw = token.startsWith("Bearer ") ? token.substring(7) : token;
        String email = jwtService.extractEmail(raw);
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new IllegalArgumentException("Usuario no encontrado"));
        if (!jwtService.isTokenValid(raw, user)) {
            throw new IllegalArgumentException("Token invalido");
        }
        return user;
    }

    private Map<String, String> parseQuery(String query) {
        Map<String, String> result = new HashMap<>();
        if (query == null || query.isBlank()) {
            return result;
        }
        for (String pair : query.split("&")) {
            String[] parts = pair.split("=", 2);
            if (parts.length == 2) {
                result.put(
                        URLDecoder.decode(parts[0], StandardCharsets.UTF_8),
                        URLDecoder.decode(parts[1], StandardCharsets.UTF_8)
                );
            }
        }
        return result;
    }

    private String shortId(UUID id) {
        String raw = id.toString();
        return raw.substring(0, Math.min(8, raw.length()));
    }
}