import type { McpProxyResponse } from '../mcp-proxy-service.js';

/**
 * SSE transport client for MCP servers using the legacy SSE protocol.
 *
 * Protocol: GET /sse → endpoint event with messages URL → POST to messages URL.
 * Responses come back on the SSE stream, matched by JSON-RPC request ID.
 *
 * Each call opens a fresh SSE connection, initializes, sends the request,
 * reads the response, and closes. Session caching may be added later.
 */
export async function sendViaSse(
  baseUrl: string,
  method: string,
  params?: Record<string, unknown>,
  timeoutMs = 30_000,
): Promise<McpProxyResponse> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);

  try {
    // 1. GET /sse → SSE stream
    const sseResp = await fetch(`${baseUrl}/sse`, {
      method: 'GET',
      headers: { 'Accept': 'text/event-stream' },
      signal: controller.signal,
    });

    if (!sseResp.ok) {
      return errorResponse(`SSE connect failed: HTTP ${sseResp.status}`);
    }

    const reader = sseResp.body?.getReader();
    if (!reader) {
      return errorResponse('No SSE stream body');
    }

    // 2. Read until we get the endpoint event with messages URL
    const decoder = new TextDecoder();
    let buffer = '';
    let messagesUrl = '';

    while (!messagesUrl) {
      const { done, value } = await reader.read();
      if (done) break;
      buffer += decoder.decode(value, { stream: true });

      for (const line of buffer.split('\n')) {
        if (line.startsWith('data: ') && buffer.includes('event: endpoint')) {
          const endpoint = line.slice(6).trim();
          messagesUrl = endpoint.startsWith('http') ? endpoint : `${baseUrl}${endpoint}`;
        }
      }
      const lines = buffer.split('\n');
      buffer = lines[lines.length - 1] ?? '';
    }

    if (!messagesUrl) {
      reader.cancel();
      return errorResponse('No endpoint event from SSE stream');
    }

    const postHeaders = { 'Content-Type': 'application/json' };

    // 3. Initialize
    const initResp = await fetch(messagesUrl, {
      method: 'POST',
      headers: postHeaders,
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 1,
        method: 'initialize',
        params: {
          protocolVersion: '2024-11-05',
          capabilities: {},
          clientInfo: { name: 'mcpctl-proxy', version: '0.0.1' },
        },
      }),
      signal: controller.signal,
    });

    if (!initResp.ok) {
      reader.cancel();
      return errorResponse(`SSE initialize failed: HTTP ${initResp.status}`);
    }

    // 4. Send notifications/initialized
    await fetch(messagesUrl, {
      method: 'POST',
      headers: postHeaders,
      body: JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized' }),
      signal: controller.signal,
    });

    // 5. Send the actual request
    const requestId = 2;
    await fetch(messagesUrl, {
      method: 'POST',
      headers: postHeaders,
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: requestId,
        method,
        ...(params !== undefined ? { params } : {}),
      }),
      signal: controller.signal,
    });

    // 6. Read response from SSE stream (matched by request ID)
    let responseBuffer = '';
    const readTimeout = setTimeout(() => reader.cancel(), 5000);

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      responseBuffer += decoder.decode(value, { stream: true });

      for (const line of responseBuffer.split('\n')) {
        if (line.startsWith('data: ')) {
          try {
            const parsed = JSON.parse(line.slice(6)) as McpProxyResponse;
            if (parsed.id === requestId) {
              clearTimeout(readTimeout);
              reader.cancel();
              return parsed;
            }
          } catch {
            // Not valid JSON, skip
          }
        }
      }
      const respLines = responseBuffer.split('\n');
      responseBuffer = respLines[respLines.length - 1] ?? '';
    }

    clearTimeout(readTimeout);
    reader.cancel();
    return errorResponse('No response received from SSE stream');
  } finally {
    clearTimeout(timer);
  }
}

function errorResponse(message: string): McpProxyResponse {
  return {
    jsonrpc: '2.0',
    id: 1,
    error: { code: -32000, message },
  };
}
