package com.cloudhandson.vpdbackoffice.service;

import com.cloudhandson.vpdbackoffice.domain.mcp.McpToolView;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.List;
import org.springframework.stereotype.Service;

/** MCP boundary exposing only the row-access-aware GPT-5.4-mini Select AI query tool. */
@Service
public class McpSseService {

  private static final String SELECT_AI_VPD_QUERY_TOOL = "ords.query.kb_select_ai_vpd";
  private static final String SELECT_AI_VPD_QUERY_PATH = "cb-ords/kb-select-ai-vpd/query";
  private static final String SELECT_AI_VPD_QUERY_PROFILE =
      "KB_AIDP_SELECTAI_GPT54_MINI_FULLMETA_PROFILE_V1";
  private static final McpToolView SELECT_AI_VPD_QUERY_VIEW = new McpToolView(
      SELECT_AI_VPD_QUERY_TOOL,
      "GPT-5.4-mini Select AI 자연어 질의를 행 접근 컨텍스트로 실행합니다. 테이블/컬럼 comment, annotation, constraint 메타데이터를 사용하고 생성 SQL은 KB 업무 테이블의 읽기 전용 SELECT/WITH만 허용합니다.",
      -1L,
      "KB Select AI 행 접근 자연어 조회",
      SELECT_AI_VPD_QUERY_PATH
  );

  private final SelectAiAgentOrdsService selectAiAgentOrdsService;
  private final ObjectMapper objectMapper;

  public McpSseService(
      SelectAiAgentOrdsService selectAiAgentOrdsService,
      ObjectMapper objectMapper
  ) {
    this.selectAiAgentOrdsService = selectAiAgentOrdsService;
    this.objectMapper = objectMapper;
  }

  public ObjectNode handle(String contextPath, JsonNode request) {
    return handle(contextPath, request, "");
  }

  /**
   * The HTTP bearer token is the business-user subject token; no separate MCP token is used.
   */
  public ObjectNode handle(String contextPath, JsonNode request, String vpdBearerToken) {
    ObjectNode response = objectMapper.createObjectNode();
    response.put("jsonrpc", "2.0");
    if (request != null && request.has("id")) {
      response.set("id", request.get("id"));
    }

    String method = request == null || !request.hasNonNull("method") ? "" : request.get("method").asText();
    JsonNode parameters = request == null ? objectMapper.createObjectNode() : request.path("params");
    try {
      response.set("result", switch (method) {
        case "initialize" -> initializeResult(contextPath);
        case "notifications/initialized" -> objectMapper.createObjectNode();
        case "tools/list" -> toolsListResult();
        case "tools/call" -> toolsCallResult(parameters, vpdBearerToken);
        default -> throw new AppException("지원하지 않는 MCP method입니다: " + method);
      });
    } catch (Exception e) {
      response.remove("result");
      ObjectNode error = objectMapper.createObjectNode();
      error.put("code", -32000);
      error.put("message", e.getMessage());
      response.set("error", error);
    }
    return response;
  }

  /** The only tool registered by this MCP server. */
  public List<McpToolView> registeredTools() {
    return List.of(SELECT_AI_VPD_QUERY_VIEW);
  }

  private ObjectNode initializeResult(String contextPath) {
    ObjectNode result = objectMapper.createObjectNode();
    result.put("protocolVersion", "2024-11-05");
    ObjectNode serverInfo = objectMapper.createObjectNode();
    serverInfo.put("name", "vpd-ords-backoffice-" + contextPath);
    serverInfo.put("version", "0.1.0");
    result.set("serverInfo", serverInfo);
    ObjectNode capabilities = objectMapper.createObjectNode();
    capabilities.set("tools", objectMapper.createObjectNode());
    result.set("capabilities", capabilities);
    return result;
  }

  private ObjectNode toolsListResult() {
    ObjectNode result = objectMapper.createObjectNode();
    ArrayNode tools = objectMapper.createArrayNode();
    tools.add(selectAiVpdQueryTool());
    result.set("tools", tools);
    return result;
  }

  private ObjectNode selectAiVpdQueryTool() {
    ObjectNode item = objectMapper.createObjectNode();
    item.put("name", SELECT_AI_VPD_QUERY_TOOL);
    item.put("description", SELECT_AI_VPD_QUERY_VIEW.description());

    ObjectNode schema = objectMapper.createObjectNode();
    schema.put("type", "object");
    ObjectNode properties = objectMapper.createObjectNode();

    ObjectNode prompt = objectMapper.createObjectNode();
    prompt.put("type", "string");
    prompt.put("description", "KB 업무 원장에 대해 조회할 내용을 자연어로 입력합니다.");
    prompt.put("maxLength", 4000);
    properties.set("prompt", prompt);

    ObjectNode limit = objectMapper.createObjectNode();
    limit.put("type", "integer");
    limit.put("description", "최대 반환 행 수. 1부터 100까지 허용하며 기본값은 50입니다.");
    limit.put("minimum", 1);
    limit.put("maximum", 100);
    properties.set("limit", limit);

    schema.set("properties", properties);
    ArrayNode required = objectMapper.createArrayNode();
    required.add("prompt");
    schema.set("required", required);
    schema.put("additionalProperties", false);
    item.set("inputSchema", schema);
    return item;
  }

  private ObjectNode toolsCallResult(JsonNode params, String vpdBearerToken) {
    String toolName = params.path("name").asText("");
    if (!SELECT_AI_VPD_QUERY_TOOL.equals(toolName)) {
      throw new AppException("등록되지 않은 MCP tool입니다: " + toolName);
    }

    JsonNode arguments = params.path("arguments");
    String token = vpdBearerToken == null ? "" : vpdBearerToken.trim();
    if (token.isBlank()) {
      return tokenAccessDeniedResult();
    }
    JsonNode response;
    try {
      response = selectAiAgentOrdsService.run(
          token,
          arguments.path("prompt").asText(""),
          normalizeLimit(arguments.path("limit").asInt(50))
      );
    } catch (VpdTokenAccessDeniedException ignored) {
      return tokenAccessDeniedResult();
    }

    ObjectNode payload = objectMapper.createObjectNode();
    payload.put("toolName", SELECT_AI_VPD_QUERY_TOOL);
    payload.put("profile", SELECT_AI_VPD_QUERY_PROFILE);
    payload.put("ordsPath", SELECT_AI_VPD_QUERY_PATH);
    payload.set("response", response);

    ObjectNode result = objectMapper.createObjectNode();
    ArrayNode content = objectMapper.createArrayNode();
    ObjectNode text = objectMapper.createObjectNode();
    text.put("type", "text");
    text.put("text", pretty(payload));
    content.add(text);
    result.set("content", content);
    result.put("isError", false);
    return result;
  }

  private ObjectNode tokenAccessDeniedResult() {
    ObjectNode payload = objectMapper.createObjectNode();
    payload.put("status", "VPD_TOKEN_DENIED");
    payload.put("message", "토큰이 없거나 유효하지 않아 이 요청을 수행할 권한이 없습니다.");

    ObjectNode result = objectMapper.createObjectNode();
    ArrayNode content = objectMapper.createArrayNode();
    ObjectNode text = objectMapper.createObjectNode();
    text.put("type", "text");
    text.put("text", pretty(payload));
    content.add(text);
    result.set("content", content);
    result.put("isError", true);
    return result;
  }

  private int normalizeLimit(int limit) {
    if (limit < 1) {
      return 50;
    }
    return Math.min(limit, 100);
  }

  private String pretty(Object value) {
    try {
      return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(value);
    } catch (Exception e) {
      return String.valueOf(value);
    }
  }
}
