import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
import { getMcpUrl } from '../config.js';

export interface McpToolDef {
  name: string;
  description: string;
  inputSchema: object;
}

let mc: Client | null = null;
let cachedTools: McpToolDef[] = [];
let connected = false;
let connecting = false;

async function connectInternal(): Promise<void> {
  if (connected) return;
  if (connecting) throw new Error('MCP client connection already in progress');
  connecting = true;

  const rawUrl = getMcpUrl();
  if (!rawUrl) {
    console.warn('[mcp] MCP_URL not configured, tools disabled');
    connecting = false;
    return;
  }

  // Ensure the URL points at the /sse endpoint (FastMCP default)
  let connectUrl = rawUrl;
  try {
    const u = new URL(rawUrl);
    if (u.pathname === '/' || u.pathname === '') {
      u.pathname = '/sse';
      connectUrl = u.toString();
    }
  } catch {
    // not a valid URL, use as-is
  }

  console.log(`[mcp] Connecting to ${connectUrl}...`);

  try {
    mc = new Client(
      { name: 'quibot-backend', version: '1.0.0' },
      { capabilities: {} },
    );

    const transport = new SSEClientTransport(new URL(connectUrl));
    await mc.connect(transport);

    console.log('[mcp] Connected, listing tools...');
    const toolsResult = await mc.listTools();
    cachedTools = (toolsResult.tools ?? []).map((t) => ({
      name: t.name,
      description: t.description ?? '',
      inputSchema: t.inputSchema as object,
    }));

    connected = true;
    connecting = false;
    console.log(`[mcp] Connected to MCP server with ${cachedTools.length} tool(s): ${cachedTools.map((t) => t.name).join(', ') || '(none)'}`);
  } catch (err) {
    const msg = err instanceof Error ? err.message : String(err);
    connecting = false;
    console.error(`[mcp] Connection failed: ${msg}`);
    throw err;
  }
}

export const mcpClient = {
  async connect(): Promise<void> {
    await connectInternal();
  },

  getReady(): boolean {
    return connected;
  },

  getTools(): readonly McpToolDef[] {
    return cachedTools;
  },

  async callTool(name: string, args: Record<string, unknown>): Promise<string> {
    if (!mc) throw new Error('MCP client not connected');
    const result = await mc.callTool({ name, arguments: args });
    const content = result.content as Array<{ type: string; text?: string }>;
    const texts = content
      .filter((c) => c.type === 'text')
      .map((c) => c.text ?? '');
    return texts.join('\n') || '[MCP tool returned no text content]';
  },

  async shutdown(): Promise<void> {
    if (mc) {
      try {
        await mc.close();
      } catch {
        // ignore close errors on shutdown
      }
      mc = null;
    }
    connected = false;
    cachedTools = [];
  },
};
