'use strict';

var child_process = require('child_process');
require('fs');
require('path');
var util = require('util');

var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
function stringifyUnknown(value) {
  if (value === void 0 || value === null) {
    return "";
  }
  if (typeof value === "string") {
    return value;
  }
  if (Buffer.isBuffer(value)) {
    return value.toString("utf8");
  }
  if (value instanceof Error) {
    return value.stack || value.message;
  }
  return util.inspect(value, { depth: 6, breakLength: 120 });
}
function getErrorSummary(error) {
  if (error instanceof Error) {
    return `${error.name}: ${error.message}`;
  }
  return stringifyUnknown(error) || "Unknown error";
}

// src/features/mcp/domain/server-record.ts
function isStdioMcpServer(server) {
  return server.transport === "stdio";
}

// src/features/talk-to-figma/infrastructure/direct-mcp-client.ts
var MCP_PROTOCOL_VERSION = "2024-11-05";
var MCP_REQUEST_TIMEOUT_MS = 3e4;
var MCP_LINE_DELIMITER = "\n";
function formatTalkToFigmaMcpResult(value) {
  if (!value || typeof value !== "object") {
    return JSON.stringify(value, null, 2);
  }
  const raw = value;
  const contentText = Array.isArray(raw.content) ? raw.content.map((entry) => entry?.type === "text" && typeof entry.text === "string" ? entry.text : JSON.stringify(entry, null, 2)).filter(Boolean).join("\n\n") : "";
  const serialized = contentText || JSON.stringify(value, null, 2);
  return raw.isError ? `[MCP ERROR]
${serialized}` : serialized;
}
var TalkToFigmaDirectMcpClient = class {
  constructor(server, trace) {
    this.server = server;
    this.trace = trace;
    __publicField(this, "child");
    __publicField(this, "nextRequestId", 1);
    __publicField(this, "pendingRequests", /* @__PURE__ */ new Map());
    __publicField(this, "stderrTail", "");
    __publicField(this, "stdoutBuffer", "");
  }
  /**
   * @description 서버 프로세스를 띄우고 initialize handshake를 완료한다.
   */
  async connect() {
    if (this.child) {
      return;
    }
    this.trace("talk-to-figma:mcp-client:spawn:start", `${this.server.command} ${this.server.args.join(" ")}`.trim());
    this.child = child_process.spawn(this.server.command, this.server.args, {
      cwd: process.cwd(),
      env: process.env,
      stdio: ["pipe", "pipe", "pipe"]
    });
    this.child.stdout?.on("data", (chunk) => {
      this.stdoutBuffer += chunk.toString("utf8");
      this.consumeBufferedMessages();
    });
    this.child.stderr?.on("data", (chunk) => {
      const nextChunk = chunk.toString("utf8");
      const nextTail = `${this.stderrTail}${nextChunk}`;
      this.stderrTail = nextTail.slice(-4e3);
      this.trace("talk-to-figma:mcp-client:stderr", nextChunk.trim());
    });
    this.child.on("error", (error) => {
      this.rejectAllPendingRequests(new Error(`TalkToFigma MCP \uD504\uB85C\uC138\uC2A4 \uC2E4\uD589\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. ${getErrorSummary(error)}`));
    });
    this.child.on("close", (code, signal) => {
      const detail = [`code=${code ?? "null"}`, `signal=${signal ?? "null"}`].join(" ");
      this.trace("talk-to-figma:mcp-client:close", detail);
      this.rejectAllPendingRequests(
        new Error(`TalkToFigma MCP \uD504\uB85C\uC138\uC2A4\uAC00 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. ${detail}${this.stderrTail ? ` stderr=${this.stderrTail.trim()}` : ""}`)
      );
      this.child = void 0;
    });
    await this.request("initialize", {
      capabilities: {},
      clientInfo: {
        name: "ju-hong-e",
        version: "0.1.13"
      },
      protocolVersion: MCP_PROTOCOL_VERSION
    });
    this.notify("notifications/initialized", {});
    this.trace("talk-to-figma:mcp-client:spawn:end", "initialized");
  }
  /**
   * @description 서버가 노출한 TalkToFigma 도구 목록을 읽는다.
   * @returns 도구 메타데이터 목록
   */
  async listTools() {
    const response = await this.request("tools/list", {});
    const rawTools = response.tools;
    if (!Array.isArray(rawTools)) {
      throw new Error("TalkToFigma tools/list \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
    }
    return rawTools.filter((entry) => !!entry && typeof entry === "object" && !Array.isArray(entry)).map((entry) => ({
      description: typeof entry.description === "string" ? entry.description : "",
      inputSchema: entry.inputSchema,
      name: typeof entry.name === "string" ? entry.name : ""
    })).filter((tool) => tool.name.length > 0);
  }
  /**
   * @description TalkToFigma 도구 1개를 직접 호출한다.
   * @param name 도구 이름
   * @param args 도구 인자
   * @returns 서버 응답 본문
   */
  async callTool(name, args) {
    return this.request("tools/call", {
      arguments: args,
      name
    });
  }
  /**
   * @description 서버 프로세스를 종료하고 대기 중 요청을 정리한다.
   */
  close() {
    if (!this.child) {
      return;
    }
    try {
      this.child.stdin?.end();
    } catch {
    }
    try {
      this.child.kill("SIGTERM");
    } catch {
    }
    this.child = void 0;
  }
  /**
   * @description 서버로 notification 메시지를 전송한다.
   * @param method JSON-RPC method
   * @param params notification params
   */
  notify(method, params) {
    this.writeMessage({
      jsonrpc: "2.0",
      method,
      params
    });
  }
  /**
   * @description JSON-RPC request 1건을 보내고 결과를 기다린다.
   * @param method JSON-RPC method
   * @param params request params
   * @returns result payload
   */
  request(method, params) {
    if (!this.child?.stdin) {
      throw new Error("TalkToFigma MCP \uD504\uB85C\uC138\uC2A4\uAC00 \uC544\uC9C1 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
    }
    const requestId = this.nextRequestId++;
    return new Promise((resolve, reject) => {
      const timer = setTimeout(() => {
        this.pendingRequests.delete(requestId);
        reject(new Error(`TalkToFigma MCP \uC751\uB2F5\uC774 \uC2DC\uAC04 \uB0B4\uC5D0 \uC624\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. method=${method}`));
      }, MCP_REQUEST_TIMEOUT_MS);
      this.pendingRequests.set(requestId, { reject, resolve, timer });
      this.writeMessage({
        id: requestId,
        jsonrpc: "2.0",
        method,
        params
      });
    });
  }
  /**
   * @description stdout buffer에서 완성된 newline-delimited MCP 메시지를 꺼내 dispatch 한다.
   */
  consumeBufferedMessages() {
    let messageEndIndex = this.stdoutBuffer.indexOf(MCP_LINE_DELIMITER);
    while (messageEndIndex >= 0) {
      const messageText = this.stdoutBuffer.slice(0, messageEndIndex).trim();
      this.stdoutBuffer = this.stdoutBuffer.slice(messageEndIndex + MCP_LINE_DELIMITER.length);
      if (messageText) {
        this.dispatchMessage(messageText);
      }
      messageEndIndex = this.stdoutBuffer.indexOf(MCP_LINE_DELIMITER);
    }
  }
  /**
   * @description 수신한 JSON-RPC 메시지를 pending request와 매칭한다.
   * @param messageText JSON body 문자열
   */
  dispatchMessage(messageText) {
    let message;
    try {
      message = JSON.parse(messageText);
    } catch (error) {
      this.rejectAllPendingRequests(new Error(`TalkToFigma MCP \uC751\uB2F5 JSON \uD30C\uC2F1\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. ${getErrorSummary(error)}`));
      return;
    }
    if (typeof message.id !== "number") {
      this.trace("talk-to-figma:mcp-client:notification", message.method || "unknown");
      return;
    }
    const pendingRequest = this.pendingRequests.get(message.id);
    if (!pendingRequest) {
      return;
    }
    clearTimeout(pendingRequest.timer);
    this.pendingRequests.delete(message.id);
    if (message.error) {
      pendingRequest.reject(new Error(message.error.message || "TalkToFigma MCP \uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4."));
      return;
    }
    pendingRequest.resolve(message.result);
  }
  /**
   * @description 전송할 JSON-RPC body를 newline-delimited MCP stdio 형식으로 보낸다.
   * @param body JSON-RPC body
   */
  writeMessage(body) {
    if (!this.child?.stdin) {
      throw new Error("TalkToFigma MCP \uD504\uB85C\uC138\uC2A4 stdin\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
    }
    const framedMessage = `${JSON.stringify(body)}${MCP_LINE_DELIMITER}`;
    this.child.stdin.write(framedMessage, "utf8");
  }
  /**
   * @description 대기 중인 모든 요청을 동일한 오류로 종료한다.
   * @param error 공통 오류 객체
   */
  rejectAllPendingRequests(error) {
    for (const [requestId, pendingRequest] of this.pendingRequests.entries()) {
      clearTimeout(pendingRequest.timer);
      pendingRequest.reject(error);
      this.pendingRequests.delete(requestId);
    }
  }
};
function assertTalkToFigmaStdioServer(server) {
  if (!isStdioMcpServer(server)) {
    throw new Error("TalkToFigma direct client\uB294 stdio MCP \uC11C\uBC84\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.");
  }
  return server;
}

exports.TalkToFigmaDirectMcpClient = TalkToFigmaDirectMcpClient;
exports.assertTalkToFigmaStdioServer = assertTalkToFigmaStdioServer;
exports.formatTalkToFigmaMcpResult = formatTalkToFigmaMcpResult;
