import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { createServer, type Server } from "node:http";

let server: Server;
let port = 0;

beforeEach(async () => {
  await new Promise<void>((resolve) => {
    server = createServer((req, res) => {
      const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
      // Decode the path so ids with reserved characters (e.g. "runbooks:vpn")
      // match whether the client encoded the colon as %3A or not.
      const pathname = decodeURIComponent(url.pathname);
      if (req.method === "POST" && pathname === "/search") {
        let body = "";
        req.on("data", (c) => (body += c));
        req.on("end", () => {
          res.writeHead(200, { "content-type": "application/json" });
          res.end(
            JSON.stringify({
              items: [
                { id: "remote:1", title: "Remote doc", source: "remote", tags: ["remote"], relevance: 0.9, excerpt: "x" },
              ],
            }),
          );
        });
        return;
      }
      if (req.method === "GET" && pathname === "/docs/remote:1") {
        res.writeHead(200, { "content-type": "application/json" });
        res.end(
          JSON.stringify({
            id: "remote:1",
            title: "Remote doc",
            source: "remote",
            tags: ["remote"],
            headings: ["Section"],
            content: "Full remote content",
          }),
        );
        return;
      }
      res.writeHead(404).end();
    });
    server.listen(0, "127.0.0.1", () => {
      const a = server.address();
      port = typeof a === "object" && a ? a.port : 0;
      resolve();
    });
  });
});

afterEach(async () => {
  await new Promise<void>((resolve) => server.close(() => resolve()));
});

describe("rag client", () => {
  it("searches via the configured endpoint when set", async () => {
    const { searchViaRag } = await import("../src/rag/client.js");
    const items = await searchViaRag(
      {
        endpoint: `http://127.0.0.1:${port}`,
        authToken: "",
        timeoutMs: 5000,
        fallbackToLocal: false,
        chunkStrategy: "heading",
        chunkSizeChars: 1500,
        topK: 5,
        minRelevance: 0,
        includeTags: [],
        excludeTags: [],
      },
      "anything",
      3,
    );
    expect(items).toHaveLength(1);
    expect(items[0]?.id).toBe("remote:1");
    expect(items[0]?.relevance).toBe(0.9);
  });

  it("fetches a single doc via the endpoint", async () => {
    const { getViaRag } = await import("../src/rag/client.js");
    const doc = await getViaRag(
      {
        endpoint: `http://127.0.0.1:${port}`,
        authToken: "secret",
        timeoutMs: 5000,
        fallbackToLocal: false,
        chunkStrategy: "heading",
        chunkSizeChars: 1500,
        topK: 5,
        minRelevance: 0,
        includeTags: [],
        excludeTags: [],
      },
      "remote:1",
    );
    expect(doc?.id).toBe("remote:1");
    expect(doc?.content).toBe("Full remote content");
  });

  it("isRagRemote returns true when endpoint is set, false otherwise", async () => {
    const { isRagRemote } = await import("../src/rag/client.js");
    const base = {
      authToken: "",
      timeoutMs: 1000,
      fallbackToLocal: true,
      chunkStrategy: "heading" as const,
      chunkSizeChars: 1500,
      topK: 5,
      minRelevance: 0,
      includeTags: [],
      excludeTags: [],
    };
    expect(isRagRemote({ ...base, endpoint: "" })).toBe(false);
    expect(isRagRemote({ ...base, endpoint: "http://x" })).toBe(true);
  });
});
