/**
 * MCP SSE (Server-Sent Events) Protocol Implementation
 *
 * Implements the Model Context Protocol over HTTP using Server-Sent Events
 * for streaming communication with LLM clients like n8n.
 *
 * Protocol: https://spec.modelcontextprotocol.io/specification/2024-11-05/server/transports/#http-with-sse
 */

import { McpTool } from './mcp-types.ts';

/**
 * JSON-RPC 2.0 Request
 */
export interface JsonRpcRequest {
  jsonrpc: '2.0';
  id?: string | number | null;
  method: string;
  params?: any;
}

/**
 * JSON-RPC 2.0 Success Response
 */
export interface JsonRpcSuccessResponse {
  jsonrpc: '2.0';
  id: string | number | null;
  result: any;
}

/**
 * JSON-RPC 2.0 Error Response
 */
export interface JsonRpcErrorResponse {
  jsonrpc: '2.0';
  id: string | number | null;
  error: {
    code: number;
    message: string;
    data?: any;
  };
}

export type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;

/**
 * MCP Initialize Request Params
 */
export interface InitializeParams {
  protocolVersion: string;
  capabilities: {
    roots?: { listChanged?: boolean };
    sampling?: {};
  };
  clientInfo: {
    name: string;
    version: string;
  };
}

/**
 * MCP Initialize Result
 */
export interface InitializeResult {
  protocolVersion: string;
  capabilities: {
    tools?: { listChanged?: boolean };
    prompts?: { listChanged?: boolean };
    resources?: { listChanged?: boolean };
  };
  serverInfo: {
    name: string;
    version: string;
  };
}

/**
 * MCP Tools List Result
 */
export interface ToolsListResult {
  tools: McpTool[];
}

/**
 * MCP Tool Call Params
 */
export interface ToolCallParams {
  name: string;
  arguments?: Record<string, any>;
}

/**
 * MCP Tool Call Result
 */
export interface ToolCallResult {
  content: Array<{
    type: 'text' | 'image' | 'resource';
    text?: string;
    data?: string;
    mimeType?: string;
  }>;
  isError?: boolean;
}

/**
 * Create SSE message data
 */
export function createSseMessage(data: any): string {
  return `data: ${JSON.stringify(data)}\n\n`;
}

/**
 * Create JSON-RPC success response
 */
export function createJsonRpcSuccess(
  id: string | number | null,
  result: any
): JsonRpcSuccessResponse {
  return {
    jsonrpc: '2.0',
    id,
    result
  };
}

/**
 * Create JSON-RPC error response
 */
export function createJsonRpcError(
  id: string | number | null,
  code: number,
  message: string,
  data?: any
): JsonRpcErrorResponse {
  return {
    jsonrpc: '2.0',
    id,
    error: { code, message, data }
  };
}

/**
 * JSON-RPC Error Codes
 */
export const JsonRpcErrorCode = {
  PARSE_ERROR: -32700,
  INVALID_REQUEST: -32600,
  METHOD_NOT_FOUND: -32601,
  INVALID_PARAMS: -32602,
  INTERNAL_ERROR: -32603,
  SERVER_ERROR: -32000
} as const;

/**
 * Create SSE response with streaming
 */
export function createSseResponse(): {
  response: Response;
  controller: ReadableStreamDefaultController;
} {
  const encoder = new TextEncoder();
  let controller: ReadableStreamDefaultController;

  const stream = new ReadableStream({
    start(ctrl) {
      controller = ctrl;
    }
  });

  const response = new Response(stream, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization'
    }
  });

  return { response, controller: controller! };
}

/**
 * Send SSE message through controller
 */
export function sendSseMessage(
  controller: ReadableStreamDefaultController,
  data: JsonRpcResponse
): void {
  const encoder = new TextEncoder();
  const message = createSseMessage(data);
  controller.enqueue(encoder.encode(message));
}

/**
 * Close SSE stream
 */
export function closeSseStream(controller: ReadableStreamDefaultController): void {
  try {
    controller.close();
  } catch (error) {
    // Stream may already be closed
    console.error('Error closing SSE stream:', error);
  }
}

/**
 * Parse JSON-RPC request from body
 */
export async function parseJsonRpcRequest(req: Request): Promise<JsonRpcRequest> {
  try {
    const body = await req.json();

    if (!body.jsonrpc || body.jsonrpc !== '2.0') {
      throw new Error('Invalid JSON-RPC version');
    }

    if (!body.method || typeof body.method !== 'string') {
      throw new Error('Invalid or missing method');
    }

    return body as JsonRpcRequest;
  } catch (error) {
    throw new Error(`Invalid JSON-RPC request: ${error instanceof Error ? error.message : 'Unknown error'}`);
  }
}

/**
 * Create MCP initialize response
 */
export function createInitializeResult(serverName: string, serverVersion: string): InitializeResult {
  return {
    protocolVersion: '2024-11-05',
    capabilities: {
      tools: { listChanged: false },
      prompts: { listChanged: false },
      resources: { listChanged: false }
    },
    serverInfo: {
      name: serverName,
      version: serverVersion
    }
  };
}

/**
 * Validate initialize params
 */
export function validateInitializeParams(params: any): params is InitializeParams {
  return (
    params &&
    typeof params.protocolVersion === 'string' &&
    params.clientInfo &&
    typeof params.clientInfo.name === 'string' &&
    typeof params.clientInfo.version === 'string'
  );
}

/**
 * Validate tool call params
 */
export function validateToolCallParams(params: any): params is ToolCallParams {
  return (
    params &&
    typeof params.name === 'string'
  );
}
