package com.yaoai.agent.sse;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@RequiredArgsConstructor
public class AgentSseManager {

    private static final long HEARTBEAT_INTERVAL_SECONDS = 15;

    private final ObjectMapper objectMapper;
    private final Map<Long, List<SseEmitter>> emitters = new ConcurrentHashMap<>();
    private final ScheduledExecutorService heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
        Thread t = new Thread(r, "agent-sse-heartbeat");
        t.setDaemon(true);
        return t;
    });

    @PostConstruct
    void startHeartbeat() {
        heartbeatExecutor.scheduleAtFixedRate(this::heartbeatAll,
                HEARTBEAT_INTERVAL_SECONDS,
                HEARTBEAT_INTERVAL_SECONDS,
                TimeUnit.SECONDS);
    }

    @PreDestroy
    void shutdownHeartbeat() {
        heartbeatExecutor.shutdownNow();
    }

    public SseEmitter subscribe(Long runId) {
        SseEmitter emitter = new SseEmitter(0L);
        List<SseEmitter> list = emitters.computeIfAbsent(runId, k -> new CopyOnWriteArrayList<>());
        list.add(emitter);
        emitter.onCompletion(() -> remove(runId, emitter));
        emitter.onTimeout(() -> {
            log.debug("SSE timeout: runId={}", runId);
            remove(runId, emitter);
            emitter.complete();
        });
        emitter.onError(e -> remove(runId, emitter));
        log.debug("SSE subscribed: runId={}, total={}", runId, list.size());
        return emitter;
    }

    public void emit(Long runId, String type, Object data) {
        List<SseEmitter> list = emitters.get(runId);
        if (list == null || list.isEmpty()) return;

        try {
            String json = objectMapper.writeValueAsString(
                    Map.of("type", type, "data", data == null ? Map.of() : data)
            );
            list.removeIf(emitter -> {
                try {
                    emitter.send(SseEmitter.event().name(type).data(json));
                    return false;
                } catch (Exception e) {
                    log.debug("Removed broken SSE emitter for runId={}", runId);
                    return true;
                }
            });
        } catch (Exception e) {
            log.error("SSE emit failed: runId={}, type={}", runId, type, e);
        }
    }

    public void emitLog(Long runId, String level, String message) {
        emit(runId, "log", Map.of("level", level, "message", message,
                "time", java.time.LocalTime.now().toString().substring(0, 8)));
    }

    private void heartbeatAll() {
        emitters.forEach((runId, list) -> list.removeIf(emitter -> {
            try {
                emitter.send(SseEmitter.event().comment("ping"));
                return false;
            } catch (Exception e) {
                log.debug("Removed heartbeat-broken SSE emitter for runId={}", runId);
                return true;
            }
        }));
    }

    private void remove(Long runId, SseEmitter emitter) {
        List<SseEmitter> list = emitters.get(runId);
        if (list != null) list.remove(emitter);
    }
}
