package com.cloudhandson.ddsbackoffice.web;

import com.cloudhandson.ddsbackoffice.domain.DdsMcpAuthenticatedUser;
import com.cloudhandson.ddsbackoffice.service.DdsMcpBearerAuthenticator;
import com.cloudhandson.ddsbackoffice.service.DdsMcpSseService;
import com.cloudhandson.vpdbackoffice.service.AppException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

/**
 * DDS-specific SSE transport. Authorization is deliberately rechecked for
 * every message rather than being trusted for the life of an SSE connection.
 */
@Controller
public class DdsMcpSseController {

  private static final long SSE_TIMEOUT_MILLIS = 30L * 60L * 1000L;

  private final DdsMcpBearerAuthenticator bearerAuthenticator;
  private final DdsMcpSseService service;
  private final Map<String, Session> sessions = new ConcurrentHashMap<>();

  public DdsMcpSseController(DdsMcpBearerAuthenticator bearerAuthenticator, DdsMcpSseService service) {
    this.bearerAuthenticator = bearerAuthenticator;
    this.service = service;
  }

  @GetMapping(path = "/dds/mcp/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
  public SseEmitter sse(@RequestHeader(name = "Authorization", required = false) String authorization) throws IOException {
    DdsMcpAuthenticatedUser user = bearerAuthenticator.authenticate(authorization);
    String sessionId = UUID.randomUUID().toString();
    SseEmitter emitter = new SseEmitter(SSE_TIMEOUT_MILLIS);
    sessions.put(sessionId, new Session(user.applicationUserId(), emitter));
    emitter.onCompletion(() -> sessions.remove(sessionId));
    emitter.onTimeout(() -> sessions.remove(sessionId));
    emitter.onError(error -> sessions.remove(sessionId));
    emitter.send(SseEmitter.event().name("endpoint").data("/dds/mcp/messages?sessionId=" + sessionId));
    return emitter;
  }

  @PostMapping(path = "/dds/mcp/messages", consumes = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<?> message(
      @RequestHeader(name = "Authorization", required = false) String authorization,
      @RequestParam(required = false) String sessionId,
      @RequestBody JsonNode request
  ) throws IOException {
    DdsMcpAuthenticatedUser user = bearerAuthenticator.authenticate(authorization);
    ObjectNode response = service.handle(user, request);
    if (sessionId == null || sessionId.isBlank()) {
      return ResponseEntity.ok(response);
    }
    Session session = sessions.get(sessionId);
    if (session == null || session.applicationUserId() != user.applicationUserId()) {
      throw new AppException("AUTHORIZATION_DENIED: MCP 세션을 확인할 수 없습니다.");
    }
    try {
      session.emitter().send(SseEmitter.event().name("message").data(response));
      return ResponseEntity.accepted().build();
    } catch (IOException exception) {
      sessions.remove(sessionId);
      throw exception;
    }
  }

  private record Session(long applicationUserId, SseEmitter emitter) {
  }
}
