import assert from "node:assert/strict";
import test from "node:test";
import {
  collectMetricsFromMcp,
  collectXhsMetricsFromMcp,
  resolveAccountProfileFromMcp,
  resolveCollectionMcpConfig,
  resolveXhsPublicAccountDetails,
  resolveXhsPublicAccountId,
  resolveXhsAccountProfileFromMcp,
  resolveXhsProfileDetailsFromMcp,
  xhsNoteIdFromUrl,
} from "../lib/mcp-collection-client.ts";

function sse(payload, options = {}) {
  return new Response(
    `event: message\ndata: ${JSON.stringify(payload)}\n\n`,
    {
      status: 200,
      ...options,
      headers: {
        "content-type": "text/event-stream",
        ...(options.headers ?? {}),
      },
    },
  );
}

function toolEnvelope(payload, isError = false) {
  return {
    jsonrpc: "2.0",
    id: "tool-call",
    result: {
      isError,
      content: [
        {
          type: "text",
          text: JSON.stringify(payload),
        },
      ],
    },
  };
}

function createFakeMcp(toolResults, redirects = {}) {
  let toolIndex = 0;
  const calls = [];
  const fetchImpl = async (url, init) => {
    if (!init?.body) {
      const requestUrl = String(url);
      calls.push({ url: requestUrl, body: null, headers: new Headers(init?.headers) });
      const location = redirects[requestUrl];
      if (!location) throw new Error(`Unexpected public request: ${requestUrl}`);
      return new Response("", { status: 302, headers: { location } });
    }
    const body = JSON.parse(init.body);
    calls.push({ url: String(url), body, headers: new Headers(init.headers) });
    if (body.method === "initialize") {
      return sse(
        {
          jsonrpc: "2.0",
          id: body.id,
          result: {
            protocolVersion: "2025-03-26",
            capabilities: { tools: { listChanged: false } },
            serverInfo: { name: "ai-tool-center", version: "test" },
          },
        },
        { headers: { "mcp-session-id": "session-test" } },
      );
    }
    if (body.method === "notifications/initialized") {
      return new Response("", { status: 202 });
    }
    if (body.method === "tools/call") {
      const result = toolResults[toolIndex];
      toolIndex += 1;
      return sse(result);
    }
    throw new Error(`Unexpected MCP method: ${body.method}`);
  };
  return { calls, fetchImpl };
}

test("collects likes, comments and favorites from the verified MCP shape", async () => {
  const { calls, fetchImpl } = createFakeMcp([
    toolEnvelope({
      http_status: 200,
      response: {
        code: 200,
        success: true,
        msg: "获取内容详情成功",
        data: {
          likes: "483",
          comments: "41",
          collects: "519",
        },
      },
    }),
  ]);

  const result = await collectXhsMetricsFromMcp(
    "https://www.xiaohongshu.com/discovery/item/test?xsec_token=valid",
    {
      endpoint: "https://collector.example/mcp",
      key: "test-key",
    },
    fetchImpl,
  );

  assert.deepEqual(result, {
    likes: 483,
    comments: 41,
    collects: 519,
    shares: 0,
  });
  assert.equal(calls.length, 3);
  assert.equal(calls[2].body.params.name, "fetch_content_detail");
  assert.equal(calls[2].body.params.arguments.request.include_comments, false);
  assert.equal(calls[2].headers.get("mcp-session-id"), "session-test");
  assert.equal(new URL(calls[0].url).searchParams.get("key"), "test-key");
});

test("collects through a stateless MCP server without a session header", async () => {
  const calls = [];
  const fetchImpl = async (_url, init) => {
    const body = JSON.parse(init.body);
    calls.push({ body, headers: new Headers(init.headers) });
    if (body.method === "initialize") {
      return sse({
        jsonrpc: "2.0",
        id: body.id,
        result: {
          protocolVersion: "2025-03-26",
          capabilities: { tools: { listChanged: false } },
          serverInfo: { name: "ai-tool-center", version: "production" },
        },
      });
    }
    if (body.method === "tools/call") {
      return sse(
        toolEnvelope({
          response: {
            code: 200,
            success: true,
            data: { likes: 12, comments: 3, collects: 6 },
          },
        }),
      );
    }
    throw new Error(`Unexpected MCP method: ${body.method}`);
  };

  const result = await collectXhsMetricsFromMcp(
    "https://www.xiaohongshu.com/explore/test",
    { endpoint: "https://collector.example/mcp", key: "test-key" },
    fetchImpl,
  );

  assert.deepEqual(result, { likes: 12, comments: 3, collects: 6, shares: 0 });
  assert.deepEqual(
    calls.map((call) => call.body.method),
    ["initialize", "tools/call"],
  );
  assert.equal(calls[1].headers.get("mcp-session-id"), null);
});

test("resolves the real XHS account profile from a submitted note link", async () => {
  const { calls, fetchImpl } = createFakeMcp([
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          note_detail: {
            products: [
              {
                content_tags: [
                  {
                    notes: [
                      {
                        ip_location: "重庆",
                        note_id: "6a671108000000000f004bef",
                        user: {
                          nickname: "N我的麻辣烫好了吗",
                          profile_url:
                            "https://www.xiaohongshu.com/user/profile/6905cbca0000000037009f49",
                          red_id: "94329495984",
                          user_id: "6905cbca0000000037009f49",
                        },
                      },
                    ],
                  },
                ],
              },
            ],
          },
        },
      },
    }),
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          fans_count: "734",
        },
      },
    }),
  ]);

  const profile = await resolveXhsAccountProfileFromMcp(
    "https://www.xiaohongshu.com/discovery/item/6a671108000000000f004bef",
    "回填昵称",
    {
      endpoint: "https://collector.example/mcp",
      key: "test-key",
    },
    fetchImpl,
  );

  assert.deepEqual(profile, {
    platformUid: "6905cbca0000000037009f49",
    nickname: "N我的麻辣烫好了吗",
    profileUrl:
      "https://www.xiaohongshu.com/user/profile/6905cbca0000000037009f49",
    redId: "94329495984",
    ipLocation: "重庆",
    followers: 734,
    gender: "",
    bio: "",
    recentNoteTitles: [],
    providerTags: [],
  });
  const mcpCalls = calls.filter((call) => call.body?.method);
  assert.equal(mcpCalls.length, 4);
  assert.equal(
    mcpCalls[2].body.params.name,
    "fetch_content_detail",
  );
  assert.equal(
    mcpCalls[2].body.params.arguments.request.link,
    "https://www.xiaohongshu.com/discovery/item/6a671108000000000f004bef",
  );
  assert.equal(mcpCalls[3].body.params.name, "parse_xhs_user_summary");
  assert.equal(
    mcpCalls[3].body.params.arguments.request.url,
    "https://www.xiaohongshu.com/user/profile/6905cbca0000000037009f49",
  );
  assert.equal(
    xhsNoteIdFromUrl(
      "https://www.xiaohongshu.com/explore/6a671108000000000f004bef",
    ),
    "6a671108000000000f004bef",
  );
});

test("resolves followers directly from the supported XHS user summary tool", async () => {
  const { calls, fetchImpl } = createFakeMcp([
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        msg: "用户摘要解析成功",
        data: {
          user: {
            fansCount: 6,
            ipLocation: "福建",
            nickname: "555 五",
            userId: "1020668113",
            gender: "女",
            desc: "分享城市周末与美食",
            tags: ["本地生活"],
          },
          notes: [
            {
              note_id: "note-1",
              title: "长沙湘菜探店",
              url: "https://www.xiaohongshu.com/explore/note-1",
            },
          ],
        },
      },
    }),
  ]);

  const details = await resolveXhsProfileDetailsFromMcp(
    "https://www.xiaohongshu.com/user/profile/5fb21d32000000000101c23e",
    {
      endpoint: "https://collector.example/mcp",
      key: "test-key",
    },
    fetchImpl,
  );

  assert.deepEqual(details, {
    nickname: "555 五",
    followers: 6,
    redId: "1020668113",
    ipLocation: "福建",
    gender: "女",
    bio: "分享城市周末与美食",
    recentNoteTitles: ["长沙湘菜探店"],
    providerTags: ["本地生活"],
  });
  assert.equal(calls[2].body.params.name, "parse_xhs_user_summary");
});

test("resolves an xhslink short URL before requesting the author profile", async () => {
  const fakeMcp = createFakeMcp([
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          note_detail: {
            note_id: "6a572da40000000021018bd2",
            ip_location: "上海",
            user: {
              nickname: "短链作者",
              user_id: "6905cbca0000000037009f49",
            },
          },
        },
      },
    }),
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          red_id: "1020668113",
          followers: "1.2万",
        },
      },
    }),
  ]);
  const fetchImpl = async (url, init) => {
    if (String(url) === "http://xhslink.cn/o/AJFyP5dnj7O") {
      return new Response(null, {
        status: 302,
        headers: {
          location:
            "https://www.xiaohongshu.com/discovery/item/6a572da40000000021018bd2?xsec_token=valid",
        },
      });
    }
    return fakeMcp.fetchImpl(url, init);
  };

  const profile = await resolveXhsAccountProfileFromMcp(
    "http://xhslink.cn/o/AJFyP5dnj7O",
    "回填昵称",
    {
      endpoint: "https://collector.example/mcp",
      key: "test-key",
    },
    fetchImpl,
  );

  assert.equal(profile.nickname, "短链作者");
  assert.equal(profile.platformUid, "6905cbca0000000037009f49");
  assert.equal(profile.redId, "1020668113");
  assert.equal(profile.followers, 12_000);
  assert.equal(
    profile.profileUrl,
    "https://www.xiaohongshu.com/user/profile/6905cbca0000000037009f49",
  );
  assert.equal(
    fakeMcp.calls[2].body.params.arguments.request.link,
    "http://xhslink.cn/o/AJFyP5dnj7O",
  );
});

test("uses the public note author when MCP profile lookup fails", async () => {
  const fakeMcp = createFakeMcp([
    toolEnvelope(
      {
        response: {
          code: 500,
          success: false,
          msg: "作者详情暂时不可用",
        },
      },
      true,
    ),
  ]);
  const fetchImpl = async (url, init) => {
    if (String(url) === "http://xhslink.cn/o/AJFyP5dnj7O") {
      return new Response(
        '<script>window.__STATE__={&quot;noteData&quot;:{&quot;desc&quot;:&quot;测试&quot;,&quot;user&quot;:{&quot;userId&quot;:&quot;5fb21d32000000000101c23e&quot;,&quot;nickName&quot;:&quot;555 五&quot;}}}</script>',
        {
          status: 200,
          headers: {
            "content-type": "text/html; charset=utf-8",
            location:
              "https://www.xiaohongshu.com/discovery/item/6a572da40000000021018bd2?xsec_token=valid",
          },
        },
      );
    }
    if (
      String(url) ===
      "https://www.xiaohongshu.com/user/profile/5fb21d32000000000101c23e"
    ) {
      return new Response(
        '<script>{"redId":"1020668113","interactions":[{"name":"粉丝","count":"6","i18nCount":"6"}]}</script>',
        { status: 200 },
      );
    }
    return fakeMcp.fetchImpl(url, init);
  };

  const profile = await resolveXhsAccountProfileFromMcp(
    "http://xhslink.cn/o/AJFyP5dnj7O",
    "回填昵称",
    {
      endpoint: "https://collector.example/mcp",
      key: "test-key",
    },
    fetchImpl,
  );

  assert.deepEqual(profile, {
    platformUid: "5fb21d32000000000101c23e",
    nickname: "555 五",
    profileUrl:
      "https://www.xiaohongshu.com/user/profile/5fb21d32000000000101c23e",
    redId: "1020668113",
    ipLocation: "待识别",
    followers: 6,
    gender: "",
    bio: "",
    recentNoteTitles: [],
    providerTags: [],
  });
  assert.equal(
    fakeMcp.calls[2].body.params.name,
    "fetch_content_detail",
  );
});

test("reads the user-visible Xiaohongshu number from a public profile", async () => {
  const fetchImpl = async () =>
    new Response(
      '<div class="redId">小红书号：1020668113</div><script>{"redId":"1020668113","interactions":[{"name":"粉丝","count":"10+","i18nCount":"10+"}]}</script>',
      { status: 200 },
    );
  const accountId = await resolveXhsPublicAccountId(
    "https://www.xiaohongshu.com/user/profile/5fb21d32000000000101c23e",
    fetchImpl,
  );
  const details = await resolveXhsPublicAccountDetails(
    "https://www.xiaohongshu.com/user/profile/5fb21d32000000000101c23e",
    fetchImpl,
  );

  assert.equal(accountId, "1020668113");
  assert.deepEqual(details, {
    nickname: "",
    redId: "1020668113",
    followers: 10,
    ipLocation: "",
  });
});

test("treats empty interaction counters from the current detail tool as zero", async () => {
  const { calls, fetchImpl } = createFakeMcp([
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          likes: "2",
          comments: "",
          collects: "",
        },
      },
    }),
  ]);

  const result = await collectXhsMetricsFromMcp(
    "https://www.xiaohongshu.com/explore/test",
    {
      endpoint: "https://collector.example/mcp?key=test-key",
    },
    fetchImpl,
  );

  assert.deepEqual(result, {
    likes: 2,
    comments: 0,
    collects: 0,
    shares: 0,
  });
  assert.equal(calls.length, 3);
  assert.equal(calls[2].body.params.name, "fetch_content_detail");
});

test("surfaces current detail tool failures without calling removed tools", async () => {
  const { calls, fetchImpl } = createFakeMcp([
    toolEnvelope(
      {
        response: {
          code: 400,
          success: false,
          msg: "获取内容详情失败",
          data: null,
        },
      },
      true,
    ),
  ]);

  await assert.rejects(
    collectXhsMetricsFromMcp(
      "https://www.xiaohongshu.com/explore/test",
      {
        endpoint: "https://collector.example/mcp?key=test-key",
      },
      fetchImpl,
    ),
    /MCP工具 fetch_content_detail 返回失败（code 400）：获取内容详情失败/,
  );
  assert.equal(calls.length, 3);
  assert.equal(calls[2].body.params.name, "fetch_content_detail");
});

test("requires the MCP key without sending a network request", async () => {
  let requested = false;
  await assert.rejects(
    collectXhsMetricsFromMcp(
      "https://www.xiaohongshu.com/explore/test",
      resolveCollectionMcpConfig({}),
      async () => {
        requested = true;
        return new Response();
      },
    ),
    /MCP采集密钥未配置/,
  );
  assert.equal(requested, false);
});

test("rebuilds the MCP session after a gateway session miss", async () => {
  let initializeCount = 0;
  const fetchImpl = async (_url, init) => {
    const body = JSON.parse(init.body);
    if (body.method === "initialize") {
      initializeCount += 1;
      return sse(
        {
          jsonrpc: "2.0",
          id: body.id,
          result: {
            protocolVersion: "2025-03-26",
            capabilities: { tools: { listChanged: false } },
            serverInfo: { name: "ai-tool-center", version: "test" },
          },
        },
        {
          headers: {
            "mcp-session-id": `session-${initializeCount}`,
          },
        },
      );
    }
    if (
      body.method === "notifications/initialized" &&
      initializeCount === 1
    ) {
      return Response.json(
        {
          jsonrpc: "2.0",
          id: "server-error",
          error: { code: -32600, message: "Session not found" },
        },
        { status: 404 },
      );
    }
    if (body.method === "notifications/initialized") {
      return new Response("", { status: 202 });
    }
    if (body.method === "tools/call") {
      return sse(
        toolEnvelope({
          response: {
            code: 200,
            success: true,
            data: { likes: 8, comments: 2, collects: 5 },
          },
        }),
      );
    }
    throw new Error(`Unexpected MCP method: ${body.method}`);
  };

  const result = await collectXhsMetricsFromMcp(
    "https://www.xiaohongshu.com/explore/test",
    {
      endpoint: "https://collector.example/mcp",
      key: "test-key",
    },
    fetchImpl,
  );

  assert.deepEqual(result, { likes: 8, comments: 2, collects: 5, shares: 0 });
  assert.equal(initializeCount, 2);
});

test("collects Douyin likes, favorites, shares and comments", async () => {
  const { calls, fetchImpl } = createFakeMcp([
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          digg_count: "120",
          collect_count: "30",
          share_count: "8",
          comment_count: "12",
        },
      },
    }),
  ]);

  const result = await collectMetricsFromMcp(
    "https://www.douyin.com/video/7520000000000000000",
    "抖音",
    { endpoint: "https://collector.example/mcp", key: "test-key" },
    fetchImpl,
  );

  assert.deepEqual(result, {
    likes: 120,
    collects: 30,
    shares: 8,
    comments: 12,
  });
  assert.equal(calls[2].body.params.name, "fetch_content_detail");
  assert.equal(calls[2].body.params.arguments.request.plant, "dy");
});

test("maps the current Douyin MCP metric field names", async () => {
  const { fetchImpl } = createFakeMcp([
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          liked_count: "5682",
          collected_count: "565",
          share_count: "6878",
          comment_count: "332",
        },
      },
    }),
  ]);

  const result = await collectMetricsFromMcp(
    "https://v.douyin.com/5O5VpgomO2U/",
    "抖音",
    { endpoint: "https://collector.example/mcp", key: "test-key" },
    fetchImpl,
  );

  assert.deepEqual(result, {
    likes: 5682,
    collects: 565,
    shares: 6878,
    comments: 332,
  });
});

test("resolves a Douyin account from a submitted work link", async () => {
  const secUid = "MS4wLjABAAAA-test-profile-123456";
  const { calls, fetchImpl } = createFakeMcp([
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          author: {
            nickname: "抖音作者",
            sec_uid: secUid,
            unique_id: "douyin-123",
          },
        },
      },
    }),
    toolEnvelope({
      response: {
        code: 200,
        success: true,
        data: {
          user: {
            nickname: "抖音作者",
            unique_id: "douyin-123",
            sec_uid: secUid,
            follower_count: "1.5万",
            ip_location: "上海",
          },
        },
      },
    }),
  ]);

  const profile = await resolveAccountProfileFromMcp(
    "https://www.douyin.com/video/7520000000000000000",
    "抖音",
    "回填昵称",
    { endpoint: "https://collector.example/mcp", key: "test-key" },
    fetchImpl,
  );

  assert.deepEqual(profile, {
    platformUid: secUid,
    nickname: "抖音作者",
    profileUrl: `https://www.douyin.com/user/${secUid}`,
    redId: "douyin-123",
    ipLocation: "上海",
    followers: 15_000,
    gender: "",
    bio: "",
    recentNoteTitles: [],
    providerTags: [],
  });
  assert.equal(calls[2].body.params.arguments.request.plant, "dy");
  assert.equal(calls[3].body.params.name, "parse_dy_user_summary");
});

test("does not treat a Douyin short-link device id as the author sec_uid", async () => {
  const shortLink = "https://v.douyin.com/5O5VpgomO2U/";
  const { calls, fetchImpl } = createFakeMcp(
    [
      toolEnvelope({
        response: {
          code: 200,
          success: true,
          data: {
            nickname: "搞怪噜噜😜",
            user_id: "4065311277723529",
          },
        },
      }),
    ],
    {
      [shortLink]: "https://www.iesdouyin.com/share/video/7671270545631842038/?did=MS4wLjABAAAA-device-token&with_sec_did=1",
    },
  );

  const profile = await resolveAccountProfileFromMcp(
    shortLink,
    "抖音",
    "回填昵称",
    { endpoint: "https://collector.example/mcp", key: "test-key" },
    fetchImpl,
  );

  assert.deepEqual(profile, {
    platformUid: "4065311277723529",
    nickname: "搞怪噜噜😜",
    profileUrl: "",
    redId: "",
    ipLocation: "待识别",
    followers: null,
    gender: "",
    bio: "",
    recentNoteTitles: [],
    providerTags: [],
  });
  assert.equal(calls.find((call) => call.body === null)?.url, shortLink);
  assert.equal(
    calls.find((call) => call.body?.params?.name === "parse_dy_user_summary"),
    undefined,
  );
});

test("resolves a Douyin profile only when the public redirect exposes sec_uid", async () => {
  const shortLink = "https://v.douyin.com/author-sec-uid/";
  const secUid = "MS4wLjABAAAAOqL4Jdu8htr7EWCDAyIr5z_7uvCAxhj-GOzCWg5zn8bDiKOp3WPw7lWkTyHvZpMY";
  const { calls, fetchImpl } = createFakeMcp(
    [
      toolEnvelope({
        response: {
          code: 200,
          success: true,
          data: {
            nickname: "抖音作者",
            user_id: "4065311277723529",
          },
        },
      }),
      toolEnvelope({
        response: {
          code: 200,
          success: true,
          data: {
            user: {
              nickname: "抖音作者",
              unique_id: "douyin-987",
              sec_uid: secUid,
              follower_count: "2.3万",
              ip_location: "广东",
            },
          },
        },
      }),
    ],
    {
      [shortLink]: `https://www.iesdouyin.com/share/video/7671270545631842038/?sec_uid=${secUid}`,
    },
  );

  const profile = await resolveAccountProfileFromMcp(
    shortLink,
    "抖音",
    "回填昵称",
    { endpoint: "https://collector.example/mcp", key: "test-key" },
    fetchImpl,
  );

  assert.deepEqual(profile, {
    platformUid: secUid,
    nickname: "抖音作者",
    profileUrl: `https://www.douyin.com/user/${secUid}`,
    redId: "douyin-987",
    ipLocation: "广东",
    followers: 23_000,
    gender: "",
    bio: "",
    recentNoteTitles: [],
    providerTags: [],
  });
  assert.equal(
    calls.find((call) => call.body?.params?.name === "parse_dy_user_summary")
      ?.body.params.arguments.request.url,
    `https://www.douyin.com/user/${secUid}`,
  );
});
