import { describe, it, expect, vi, afterEach } from "vitest";
import { roepMcpToolAan, lijstMcpTools, bestaatMcpTool, onbekendeToolBoodschap } from "./server";

// De validatiepaden van roepMcpToolAan handelen af vóór de REST-fetch, dus deze
// tests hebben geen netwerk of mock nodig. De origin is daarom irrelevant.
const ORIGIN = "http://localhost";

// Corrigeerbare invoerfouten zijn bewust GEEN isError-resultaat: Copilot
// Studio toont de inhoud van een isError-resultaat niet aan het model ("Tool
// did not respond with success"), waarmee de zelfcorrectie-hints verloren
// zouden gaan. Het model leest ze nu als gewoon resultaat met een "fout"-veld.
describe("roepMcpToolAan — argumentvalidatie vóór de REST-call", () => {
  it("een onbekend argument geeft een leesbare invoerfout met een 'bedoelde je …?'-suggestie", async () => {
    // berekenBelasting kent 'waarde'; 'warde' is een typfout.
    const res = await roepMcpToolAan(
      "berekenBelasting",
      { soort: "schenking", relatie: "anderen", warde: 1000 },
      ORIGIN,
    );
    expect(res.isFout).toBe(false);
    expect(JSON.parse(res.tekst)).toHaveProperty("fout");
    expect(res.tekst).toContain("Onbekend argument");
    expect(res.tekst).toContain("waarde");
  });

  it("een ontbrekende verplichte parameter geeft een leesbare invoerfout", async () => {
    const res = await roepMcpToolAan("berekenBelasting", { soort: "schenking" }, ORIGIN);
    expect(res.isFout).toBe(false);
    expect(JSON.parse(res.tekst)).toHaveProperty("fout");
    expect(res.tekst).toContain("Ontbrekende verplichte parameter");
  });

  it("een ongeldige enum-waarde noemt de toegelaten waarden", async () => {
    const res = await roepMcpToolAan(
      "berekenBelasting",
      { soort: "verkoop", relatie: "anderen", waarde: 1000 },
      ORIGIN,
    );
    expect(res.isFout).toBe(false);
    expect(res.tekst).toContain("schenking");
    expect(res.tekst).toContain("erfenis");
  });

  it("een onbekende tool blijft een echte fout (protocollaag antwoordt -32602) met suggestie zonder fetch", async () => {
    const res = await roepMcpToolAan("berekenBelastng", {}, ORIGIN);
    expect(res.isFout).toBe(true);
    expect(res.tekst).toContain("berekenBelasting");
  });
});

describe("roepMcpToolAan — endpointantwoorden (fetch gemockt)", () => {
  afterEach(() => vi.unstubAllGlobals());

  function stubFetch(status: number, body: string) {
    vi.stubGlobal(
      "fetch",
      vi.fn().mockResolvedValue(new Response(body, { status, headers: { "Content-Type": "application/json" } })),
    );
  }

  it("een 4xx met JSON-foutbody wordt een gewoon, leesbaar resultaat (geen isError) zonder structuredContent", async () => {
    stubFetch(404, JSON.stringify({ fout: 'Onbekend modeldocument — zie /api/modeldocumenten voor de lijst.' }));
    const res = await roepMcpToolAan("haalModeldocumentAlsWord", { id: "verkoopovereenkomst-heldere-taal-fr" }, ORIGIN);
    expect(res.isFout).toBe(false);
    expect(res.tekst).toContain("Onbekend modeldocument");
    // De foutbody voldoet niet aan het beloofde outputSchema → nooit als
    // structuredContent doorgeven (protocol.ts hangt gestructureerd eraan).
    expect(res.gestructureerd).toBeUndefined();
    expect(res.links).toBeUndefined();
  });

  it("een 5xx blijft een echte toolfout, mét de body", async () => {
    stubFetch(500, JSON.stringify({ fout: "Interne fout." }));
    const res = await roepMcpToolAan("haalModeldocumentAlsWord", { id: "verkoopovereenkomst-heldere-taal-fr" }, ORIGIN);
    expect(res.isFout).toBe(true);
    expect(res.tekst).toContain("Interne fout");
  });

  it("een 4xx met onleesbare (niet-JSON) body blijft een echte toolfout", async () => {
    stubFetch(401, "<html>Authentication required</html>");
    const res = await roepMcpToolAan("haalModeldocumentAlsWord", { id: "verkoopovereenkomst-heldere-taal-fr" }, ORIGIN);
    expect(res.isFout).toBe(true);
  });

  it("een 200 met downloadUrl levert het resultaat mét resource_link en structuredContent", async () => {
    stubFetch(
      200,
      JSON.stringify({ bestandsnaam: "test.docx", downloadUrl: "http://localhost/api/bestanden/t1", base64: null }),
    );
    const res = await roepMcpToolAan("haalModeldocumentAlsWord", { id: "verkoopovereenkomst-heldere-taal-fr" }, ORIGIN);
    expect(res.isFout).toBe(false);
    expect(res.gestructureerd).toHaveProperty("downloadUrl");
    expect(res.links?.[0]).toMatchObject({ uri: "http://localhost/api/bestanden/t1", name: "test.docx" });
  });
});

describe("hulpfuncties", () => {
  it("bestaatMcpTool herkent bestaande en onbestaande tools", () => {
    const eerste = lijstMcpTools()[0]?.name;
    expect(bestaatMcpTool(eerste)).toBe(true);
    expect(bestaatMcpTool("zzz-bestaat-niet")).toBe(false);
  });

  it("onbekendeToolBoodschap suggereert de dichtstbijzijnde bestaande naam", () => {
    expect(onbekendeToolBoodschap("berekenAktekstn")).toContain("berekenAktekosten");
  });
});
