import { httpClient, HttpMethod } from '@activepieces/pieces-common';

interface MCPConfig {
  mcpServerUrl: string;
}

/**
 * Helper class for interacting with MCP servers using the standard MCP protocol.
 */
export class MCPManager {
  private static initialized = new Map<string, boolean>();

  /**
   * Parse SSE response format to extract JSON data
   */
  private static parseSSEResponse(sseData: any): any {
    try {
      if (typeof sseData === 'string') {
        // Handle different response formats
        if (sseData.startsWith('{') || sseData.startsWith('[')) {
          // Direct JSON response
          return JSON.parse(sseData);
        }
        
        // Extract JSON from SSE format: "event: message\ndata: {...}\n\n"
        const lines = sseData.split('\n');
        for (const line of lines) {
          if (line.startsWith('data: ')) {
            const jsonStr = line.substring(6); // Remove "data: " prefix
            if (jsonStr.trim()) {
              return JSON.parse(jsonStr);
            }
          }
        }
        
        // If no SSE data found, try to parse the entire string as JSON
        if (sseData.trim()) {
          return JSON.parse(sseData);
        }
      }
      return sseData; // If it's already parsed or not a string
    } catch (error: any) {
      console.error('Failed to parse SSE response:', error);
      console.error('Raw response data:', sseData);
      throw new Error(`Failed to parse MCP response: ${error.message}`);
    }
  }

  /**
   * Initialize MCP session if not already initialized
   */
  private static async initializeMCPSession(config: MCPConfig): Promise<boolean> {
    const { mcpServerUrl } = config;
    
    if (this.initialized.get(mcpServerUrl)) {
      return true; // Already initialized
    }

    try {
      console.log('Initializing MCP session with server:', mcpServerUrl);
      
      // Step 1: Initialize
      const initRequest = {
        jsonrpc: '2.0',
        method: 'initialize',
        params: {
          protocolVersion: '2024-11-05',
          capabilities: {
            tools: {}
          },
          clientInfo: {
            name: 'activepieces-task-queuer',
            version: '1.0.0'
          }
        },
        id: `init-${Date.now()}`
      };

      const initResponse = await httpClient.sendRequest({
        method: HttpMethod.POST,
        url: mcpServerUrl,
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json, text/event-stream'
        },
        body: initRequest,
      });

      if (!initResponse.body) {
        throw new Error('Empty response from MCP server during initialization');
      }

      const initData = this.parseSSEResponse(initResponse.body);
      
      if (initData.error) {
        throw new Error(`MCP initialization failed: ${JSON.stringify(initData.error)}`);
      }

      console.log('MCP initialization successful');

      // Step 2: Send initialized notification
      const initializedNotification = {
        jsonrpc: '2.0',
        method: 'initialized',
        params: {}
      };

      try {
        await httpClient.sendRequest({
          method: HttpMethod.POST,
          url: mcpServerUrl,
          headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json, text/event-stream'
          },
          body: initializedNotification,
        });
        console.log('MCP initialized notification sent successfully');
      } catch (notifError: any) {
        // Notification might not return a response, which is fine
        console.log('Initialized notification sent (no response expected)');
      }

      this.initialized.set(mcpServerUrl, true);
      return true;

    } catch (error: any) {
      console.error('MCP initialization error:', error);
      throw new Error(`Failed to initialize MCP session: ${error.message}`);
    }
  }

  /**
   * List available MCP tools with their schemas
   */
  static async listMCPTools(config: MCPConfig): Promise<any[]> {
    try {
      if (!config?.mcpServerUrl) {
        throw new Error('MCP Server URL is required');
      }

      // Initialize session first
      const initialized = await this.initializeMCPSession(config);
      if (!initialized) {
        throw new Error('Failed to initialize MCP session');
      }

      const toolsRequest = {
        jsonrpc: '2.0',
        method: 'tools/list',
        params: {},
        id: `tools-list-${Date.now()}`
      };

      console.log('Sending MCP tools/list request to:', config.mcpServerUrl);
      const toolsResponse = await httpClient.sendRequest({
        method: HttpMethod.POST,
        url: config.mcpServerUrl,
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json, text/event-stream'
        },
        body: toolsRequest,
      });

      if (!toolsResponse.body) {
        throw new Error('Empty response from MCP server');
      }

      const toolsData = this.parseSSEResponse(toolsResponse.body);
      
      if (toolsData.error) {
        throw new Error(`MCP server error: ${JSON.stringify(toolsData.error)}`);
      }

      const tools = toolsData.result?.tools || [];
      console.log(`Retrieved ${tools.length} MCP tools from server`);
      return tools;
    } catch (error: any) {
      console.error('Error listing MCP tools:', error);
      throw new Error(`Failed to list MCP tools: ${error.message}`);
    }
  }

  /**
   * Execute an MCP tool using JSON-RPC. Returns `{ success, result?, error? }`.
   */
  static async executeMCPAction(toolName: string, params: any, config: MCPConfig): Promise<{
    success: boolean;
    result?: any;
    error?: string;
  }> {
    try {
      // Initialize session first
      const initialized = await this.initializeMCPSession(config);
      if (!initialized) {
        return { success: false, error: 'Failed to initialize MCP session' };
      }

      const requestId = `tool-call-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
      const rpcRequest = {
        jsonrpc: '2.0',
        method: 'tools/call',
        params: { name: toolName, arguments: params },
        id: requestId,
      };

      const res = await httpClient.sendRequest({
        method: HttpMethod.POST,
        url: config.mcpServerUrl,
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json, text/event-stream',
        },
        body: rpcRequest,
      });

      const responseData = this.parseSSEResponse(res.body);

      if (responseData.error) {
        return { success: false, error: responseData.error.message || 'MCP error' };
      }
      return { success: true, result: responseData.result };
    } catch (err: any) {
      return { success: false, error: `MCP execution failed: ${err.message}` };
    }
  }
}
