import { Injectable, Logger } from '@nestjs/common';

export interface McpToolDef {
  name: string;
  description?: string;
  inputSchema: Record<string, unknown>;
}

/**
 * McpClientService — 极简 HTTP MCP 客户端(raw JSON-RPC over Streamable HTTP)。
 *
 * 助手作为"外部 agent"通过此客户端**真连** PAC 自己的 MCP 端点(dogfood 验证契约)。
 * 转发调用方(登录用户)的 Bearer token → MCP 工具在该用户的 tenant scope 下执行。
 *
 * 无状态:PAC MCP server enableJsonResponse + 无 session,tools/list / tools/call 可直接 POST,无需握手。
 */
@Injectable()
export class McpClientService {
  private readonly logger = new Logger(McpClientService.name);
  // 与 MCP server 同进程,走 loopback;PORT 即 main.ts 的监听端口(默认 3001,与之一致)。
  // 异常拓扑(反代/独立部署)时用 PAC_MCP_URL 覆盖。
  private readonly url =
    process.env.PAC_MCP_URL ?? `http://127.0.0.1:${process.env.PORT ?? '3001'}/pac/v1/mcp`;
  // 工具清单是静态的(6 个工具与租户无关,scope 只影响执行)→ 进程级缓存,省每轮 tools/list 往返。
  private toolsCache: McpToolDef[] | null = null;

  private async rpc(token: string, method: string, params?: unknown): Promise<Record<string, unknown>> {
    const res = await fetch(this.url, {
      method: 'POST',
      headers: {
        'content-type': 'application/json',
        accept: 'application/json, text/event-stream',
        authorization: `Bearer ${token}`,
      },
      body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params }),
    });
    const text = await res.text();
    const msg = parseRpc(text);
    if (msg.error) {
      throw new Error(`MCP ${method} 失败: ${(msg.error as { message?: string }).message ?? 'unknown'}`);
    }
    return (msg.result ?? {}) as Record<string, unknown>;
  }

  async listTools(token: string): Promise<McpToolDef[]> {
    if (this.toolsCache) return this.toolsCache;
    const r = await this.rpc(token, 'tools/list');
    this.toolsCache = (r.tools as McpToolDef[]) ?? [];
    return this.toolsCache;
  }

  /** 调工具 → 返回纯文本结果(MCP content[].text 拼接);isError 时抛出供 agent 看到。 */
  async callTool(token: string, name: string, args: unknown): Promise<string> {
    const r = await this.rpc(token, 'tools/call', { name, arguments: args ?? {} });
    const content = (r.content as Array<{ type: string; text?: string }>) ?? [];
    const text = content.map((c) => c.text ?? '').join('\n');
    if (r.isError) throw new Error(text || `工具 ${name} 返回错误`);
    return text;
  }
}

/** 解析 MCP 响应:plain JSON 或 SSE 帧(data: {...})。 */
function parseRpc(text: string): Record<string, unknown> {
  const t = text.trim();
  if (t.startsWith('{')) return JSON.parse(t) as Record<string, unknown>;
  const dataLine = t.split('\n').find((l) => l.startsWith('data:'));
  if (!dataLine) throw new Error(`无法解析 MCP 响应: ${t.slice(0, 120)}`);
  return JSON.parse(dataLine.slice('data:'.length).trim()) as Record<string, unknown>;
}
