import { describe, expect, it } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { createElement } from "react";
import type { Thing } from "schema-dts";
import { JsonLdRenderer, serializeJsonLd } from "./json-ld.js";

describe("serializeJsonLd", () => {
  it("serializes a single Thing to a JSON-LD string", () => {
    const data: Thing = {
      "@type": "WebSite",
      name: "Aeroflot",
      url: "https://www.aeroflot.ru",
    };

    const result = serializeJsonLd(data);
    const parsed = JSON.parse(result);

    expect(parsed["@context"]).toBe("https://schema.org");
    expect(parsed["@type"]).toBe("WebSite");
    expect(parsed.name).toBe("Aeroflot");
  });

  it("serializes an array of Things with @context on each", () => {
    const data: Thing[] = [
      { "@type": "WebSite", name: "Aeroflot" } as Thing,
      { "@type": "Organization", name: "Aeroflot PJSC" } as Thing,
    ];

    const result = serializeJsonLd(data);
    const parsed = JSON.parse(result);

    expect(Array.isArray(parsed)).toBe(true);
    expect(parsed).toHaveLength(2);
    expect(parsed[0]["@context"]).toBe("https://schema.org");
    expect(parsed[1]["@context"]).toBe("https://schema.org");
  });

  it("escapes </script> to prevent injection", () => {
    const data: Thing = {
      "@type": "WebSite",
      name: '</script><script>alert("xss")</script>',
    };

    const result = serializeJsonLd(data);
    expect(result).not.toContain("</script>");
  });
});

describe("JsonLdRenderer", () => {
  it("renders a <script type=application/ld+json> tag", () => {
    const data: Thing = {
      "@type": "WebSite",
      name: "Aeroflot",
      url: "https://www.aeroflot.ru",
    };

    const html = renderToStaticMarkup(createElement(JsonLdRenderer, { data }));

    expect(html).toContain('<script type="application/ld+json">');
    expect(html).toContain("</script>");
    expect(html).toContain('"@context":"https://schema.org"');
    expect(html).toContain('"@type":"WebSite"');
  });

  it("round-trips: serialize -> DOM string contains valid JSON-LD", () => {
    const data: Thing = {
      "@type": "Organization",
      name: "Aeroflot PJSC",
      url: "https://www.aeroflot.ru",
    };

    const html = renderToStaticMarkup(createElement(JsonLdRenderer, { data }));

    // Extract JSON from the script tag
    const match = html.match(/<script[^>]*>([\s\S]*?)<\/script>/);
    expect(match).not.toBeNull();

    const json = match![1]!.replace(/\\u003c/g, "<");
    const parsed = JSON.parse(json);
    expect(parsed["@context"]).toBe("https://schema.org");
    expect(parsed["@type"]).toBe("Organization");
    expect(parsed.name).toBe("Aeroflot PJSC");
  });
});
