package com.erp.alarm;

import com.fasterxml.jackson.databind.ObjectMapper;
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.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class AlarmWebSocketHandler extends TextWebSocketHandler {

    private final ObjectMapper objectMapper = new ObjectMapper();

    // userId -> set of sessions (한 사용자가 여러 디바이스/탭 동시 접속 가능)
    private final Map<String, Set<WebSocketSession>> sessionsByUser = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String userId = (String) session.getAttributes().get("user_id");
        if (userId == null) {
            session.close(CloseStatus.POLICY_VIOLATION);
            return;
        }
        sessionsByUser.computeIfAbsent(userId, k -> ConcurrentHashMap.newKeySet()).add(session);
        log.info("[WS] connected: user={}, sessions={}", userId, sessionsByUser.get(userId).size());

        Map<String, Object> hello = Map.of("type", "hello", "user_id", userId, "ts", System.currentTimeMillis());
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(hello)));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        String userId = (String) session.getAttributes().get("user_id");
        if (userId == null) return;
        Set<WebSocketSession> set = sessionsByUser.get(userId);
        if (set != null) {
            set.remove(session);
            if (set.isEmpty()) sessionsByUser.remove(userId);
        }
        log.info("[WS] closed: user={}, status={}", userId, status);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        // 폰 → 서버 메시지 (예: ack). 시연 단계에선 ack 만 받아 로그.
        String userId = (String) session.getAttributes().get("user_id");
        log.info("[WS] msg from {}: {}", userId, message.getPayload());
    }

    /**
     * 특정 user_id 의 모든 active session 에 알람 push.
     * 반환값 = 실제로 메시지가 송신된 세션 수 (0 이면 미접속).
     */
    public int sendAlarm(String userId, Map<String, Object> alarm) {
        Set<WebSocketSession> set = sessionsByUser.get(userId);
        if (set == null || set.isEmpty()) {
            log.info("[WS] no active session for user={}, skipped", userId);
            return 0;
        }
        Map<String, Object> envelope = Map.of("type", "alarm", "payload", alarm);
        String json;
        try {
            json = objectMapper.writeValueAsString(envelope);
        } catch (Exception e) {
            log.error("[WS] serialize fail", e);
            return 0;
        }
        int sent = 0;
        for (WebSocketSession s : set) {
            try {
                if (s.isOpen()) {
                    s.sendMessage(new TextMessage(json));
                    sent++;
                }
            } catch (Exception e) {
                log.warn("[WS] send fail to {}: {}", userId, e.getMessage());
            }
        }
        return sent;
    }

    /** 디버그/모니터링 용 */
    public Set<String> activeUsers() {
        return sessionsByUser.keySet();
    }
}
