import {
  renderContentfulRichText,
  renderContentfulRichTextTable,
  useContentfulRichText,
} from "./use-contentful-rich-text";

import {
  BLOCKS,
  INLINES,
  MARKS,
  type Document,
} from "@contentful/rich-text-types";
import { render, renderHook, screen } from "@testing-library/react";

// Mock dependencies
jest.mock("@shared/components/text", () => ({
  Text: ({ as: Tag = "span", children, className }: any) => (
    <Tag className={className}>{children}</Tag>
  ),
}));

jest.mock("@shared/components/link", () => ({
  Link: ({ href, children, target, rel, className }: any) => (
    <a href={href} target={target} rel={rel} className={className}>
      {children}
    </a>
  ),
}));

jest.mock("@shared/components/material-icon", () => ({
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  MaterialIcon: ({ name, color, fill }: any) => (
    <span data-testid={`icon-${name}`} data-color={color}>
      {name}
    </span>
  ),
}));

jest.mock("@shared/components/checklist", () => ({
  Checklist: ({ items }: any) => (
    <ul data-testid="checklist">
      {items?.map((item: any, i: number) => (
        <li key={i}>{item}</li>
      ))}
    </ul>
  ),
}));

// Helper to build a Contentful Document
function makeDoc(...content: any[]): Document {
  return {
    nodeType: BLOCKS.DOCUMENT,
    data: {},
    content,
  };
}

function makeParagraph(text: string): any {
  return {
    nodeType: BLOCKS.PARAGRAPH,
    data: {},
    content: [{ nodeType: "text", value: text, marks: [], data: {} }],
  };
}

function makeHeading(level: number, text: string): any {
  const nodeType = `heading-${level}` as any;
  return {
    nodeType,
    data: {},
    content: [{ nodeType: "text", value: text, marks: [], data: {} }],
  };
}

function makeMarkedText(text: string, markType: string): any {
  return {
    nodeType: BLOCKS.PARAGRAPH,
    data: {},
    content: [
      {
        nodeType: "text",
        value: text,
        marks: [{ type: markType }],
        data: {},
      },
    ],
  };
}

function makeHyperlink(url: string, text: string): any {
  return {
    nodeType: BLOCKS.PARAGRAPH,
    data: {},
    content: [
      {
        nodeType: INLINES.HYPERLINK,
        data: { uri: url },
        content: [{ nodeType: "text", value: text, marks: [], data: {} }],
      },
    ],
  };
}

function makeList(ordered: boolean, items: string[]): any {
  return {
    nodeType: ordered ? BLOCKS.OL_LIST : BLOCKS.UL_LIST,
    data: {},
    content: items.map(item => ({
      nodeType: BLOCKS.LIST_ITEM,
      data: {},
      content: [makeParagraph(item)],
    })),
  };
}

function makeBlockquote(text: string): any {
  return {
    nodeType: BLOCKS.QUOTE,
    data: {},
    content: [makeParagraph(text)],
  };
}

function makeEmbeddedAsset(url: string | undefined, title?: string): any {
  return {
    nodeType: BLOCKS.EMBEDDED_ASSET,
    data: {
      target: url
        ? {
            fields: {
              file: { url },
              title: title ?? "Asset",
            },
          }
        : { fields: {} },
    },
    content: [],
  };
}

function makeEmbeddedEntry(contentTypeId: string, fields: any): any {
  return {
    nodeType: BLOCKS.EMBEDDED_ENTRY,
    data: {
      target: {
        sys: { contentType: { sys: { id: contentTypeId } } },
        fields,
      },
    },
    content: [],
  };
}

function makeInlineEntry(contentTypeId: string, fields: any): any {
  return {
    nodeType: INLINES.EMBEDDED_ENTRY,
    data: {
      target: {
        sys: { contentType: { sys: { id: contentTypeId } } },
        fields,
      },
    },
    content: [],
  };
}

// ──────────────────────────────────────────────
// renderContentfulRichText
// ──────────────────────────────────────────────
describe("renderContentfulRichText", () => {
  describe("Null / invalid input", () => {
    it("returns null for null doc", () => {
      expect(renderContentfulRichText(null)).toBeNull();
    });

    it("returns null for undefined doc", () => {
      expect(renderContentfulRichText(undefined)).toBeNull();
    });

    it("returns null for doc without content array", () => {
      const bad = { nodeType: BLOCKS.DOCUMENT, data: {} } as any;
      expect(renderContentfulRichText(bad)).toBeNull();
    });
  });

  describe("Paragraph rendering", () => {
    it("renders paragraph with default className 'body1'", () => {
      const doc = makeDoc(makeParagraph("Hello world"));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      const div = container.querySelector("div.body1");
      expect(div).toBeInTheDocument();
      expect(div).toHaveTextContent("Hello world");
    });

    it("renders paragraph with custom className", () => {
      const doc = makeDoc(makeParagraph("Custom class"));
      const { container } = render(
        <>{renderContentfulRichText(doc, undefined, "custom-class")}</>
      );
      expect(container.querySelector("div.custom-class")).toHaveTextContent(
        "Custom class"
      );
    });
  });

  describe("Mark rendering", () => {
    it("renders bold text", () => {
      const doc = makeDoc(makeMarkedText("Bold", MARKS.BOLD));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      const strong = container.querySelector("strong");
      expect(strong).toHaveTextContent("Bold");
    });

    it("renders italic text", () => {
      const doc = makeDoc(makeMarkedText("Italic", MARKS.ITALIC));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("em")).toHaveTextContent("Italic");
    });

    it("renders underline text", () => {
      const doc = makeDoc(makeMarkedText("Underline", MARKS.UNDERLINE));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("u")).toHaveTextContent("Underline");
    });

    it("renders code text", () => {
      const doc = makeDoc(makeMarkedText("Code", MARKS.CODE));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("code")).toHaveTextContent("Code");
    });
  });

  describe("Hyperlink rendering", () => {
    it("renders external link with target=_blank when target is not specified", () => {
      const doc = makeDoc(makeHyperlink("https://example.com", "Click"));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      const a = container.querySelector("a")!;
      expect(a).toHaveAttribute("href", "https://example.com");
      expect(a).toHaveAttribute("target", "_blank");
      expect(a).toHaveAttribute("rel", "noopener noreferrer");
    });

    it("renders internal link with target=_self for relative URLs", () => {
      const doc = makeDoc(makeHyperlink("/about", "About"));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      const a = container.querySelector("a")!;
      expect(a).toHaveAttribute("target", "_self");
      expect(a).not.toHaveAttribute("rel");
    });

    it("respects target=true flag to force _blank", () => {
      const doc = makeDoc(makeHyperlink("/local", "Local"));
      const { container } = render(<>{renderContentfulRichText(doc, true)}</>);
      const a = container.querySelector("a")!;
      expect(a).toHaveAttribute("target", "_blank");
    });

    it("respects target=false flag to force _self", () => {
      const doc = makeDoc(makeHyperlink("https://example.com", "External"));
      const { container } = render(<>{renderContentfulRichText(doc, false)}</>);
      const a = container.querySelector("a")!;
      expect(a).toHaveAttribute("target", "_self");
    });

    it("applies custom linkClassName", () => {
      const doc = makeDoc(makeHyperlink("https://x.com", "Link"));
      const { container } = render(
        <>{renderContentfulRichText(doc, undefined, "body1", "custom-link")}</>
      );
      const a = container.querySelector("a")!;
      expect(a).toHaveClass("custom-link");
    });
  });

  describe("Custom options merge", () => {
    it("merges custom renderNode options", () => {
      const doc = makeDoc(makeParagraph("Custom"));
      const customOptions = {
        renderNode: {
          [BLOCKS.PARAGRAPH]: (_node: any, children: any) => (
            <p data-testid="custom-p">{children}</p>
          ),
        },
      };
      render(
        <>
          {renderContentfulRichText(
            doc,
            undefined,
            "body1",
            "body1 font-bold",
            customOptions
          )}
        </>
      );
      // The merged PARAGRAPH comes from the function's own override, not from customOptions
      // because the function re-defines BLOCKS.PARAGRAPH after spreading options.renderNode
      // The function's PARAGRAPH override wins.
      expect(screen.queryByTestId("custom-p")).not.toBeInTheDocument();
    });
  });

  describe("Inline entry rendering", () => {
    it("renders componentCheckList inline entry via defaultOptions", () => {
      const doc = makeDoc({
        nodeType: BLOCKS.PARAGRAPH,
        data: {},
        content: [
          makeInlineEntry("componentCheckList", { title: "Check Item" }),
        ],
      });
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container).toHaveTextContent("Check Item");
    });

    it("renders unknown inline entry title via defaultOptions", () => {
      const doc = makeDoc({
        nodeType: BLOCKS.PARAGRAPH,
        data: {},
        content: [makeInlineEntry("someOtherType", { title: "Fallback" })],
      });
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container).toHaveTextContent("Fallback");
    });

    it("renders unknown inline entry with missing title as empty", () => {
      const doc = makeDoc({
        nodeType: BLOCKS.PARAGRAPH,
        data: {},
        content: [makeInlineEntry("someOtherType", {})],
      });
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      const spans = container.querySelectorAll("span");
      const emptySpan = Array.from(spans).find(s => s.textContent === "");
      expect(emptySpan).toBeDefined();
    });
  });

  describe("Embedded entry rendering via defaultOptions", () => {
    it("renders callout block entry", () => {
      const doc = makeDoc(
        makeEmbeddedEntry("callout", {
          title: "Note",
          body: "Important info",
        })
      );
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("aside")).toBeInTheDocument();
      expect(container).toHaveTextContent("Note");
    });

    it("returns null for unknown block entry type", () => {
      const doc = makeDoc(makeEmbeddedEntry("unknownType", { title: "X" }));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("aside")).not.toBeInTheDocument();
    });
  });

  describe("Embedded asset via defaultOptions", () => {
    it("renders image with protocol-relative URL", () => {
      const doc = makeDoc(
        makeEmbeddedAsset("//images.ctfl.net/photo.jpg", "Photo")
      );
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      const img = container.querySelector("img")!;
      expect(img).toHaveAttribute("src", "https://images.ctfl.net/photo.jpg");
    });

    it("uses description as alt when title is missing", () => {
      const node: any = {
        nodeType: BLOCKS.EMBEDDED_ASSET,
        data: {
          target: {
            fields: {
              file: { url: "https://cdn.com/i.png" },
              description: "Desc text",
            },
          },
        },
        content: [],
      };
      const doc = makeDoc(node);
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("img")).toHaveAttribute(
        "alt",
        "Desc text"
      );
    });

    it("uses default alt when no title or description", () => {
      const node: any = {
        nodeType: BLOCKS.EMBEDDED_ASSET,
        data: {
          target: {
            fields: {
              file: { url: "https://cdn.com/i.png" },
            },
          },
        },
        content: [],
      };
      const doc = makeDoc(node);
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("img")).toHaveAttribute(
        "alt",
        "Embedded asset"
      );
    });

    it("uses target.url fallback when file.url is missing", () => {
      const node: any = {
        nodeType: BLOCKS.EMBEDDED_ASSET,
        data: {
          target: {
            url: "https://direct.com/img.png",
            fields: {},
          },
        },
        content: [],
      };
      const doc = makeDoc(node);
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("img")).toHaveAttribute(
        "src",
        "https://direct.com/img.png"
      );
    });

    it("returns null when no url available", () => {
      const doc = makeDoc(makeEmbeddedAsset(undefined));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("img")).not.toBeInTheDocument();
    });
  });

  describe("HYPERLINK via defaultOptions in renderContentfulRichText", () => {
    it("renders external link with _blank via default HYPERLINK", () => {
      // When target param is not set, renderContentfulRichText uses its own HYPERLINK handler
      // But defaultOptions.HYPERLINK is still in the spread, though overridden
      // This tests renderContentfulRichText's own HYPERLINK handler
      const doc = makeDoc(makeHyperlink("https://ext.com", "Ext"));
      const { container } = render(
        <>{renderContentfulRichText(doc, undefined)}</>
      );
      const a = container.querySelector("a")!;
      expect(a).toHaveAttribute("target", "_blank");
    });

    it("renders internal link with _self when target is undefined", () => {
      const doc = makeDoc(makeHyperlink("/page", "Page"));
      const { container } = render(
        <>{renderContentfulRichText(doc, undefined)}</>
      );
      const a = container.querySelector("a")!;
      expect(a).toHaveAttribute("target", "_self");
    });

    it("handles hyperlink with missing uri data", () => {
      const node: any = {
        nodeType: BLOCKS.PARAGRAPH,
        data: {},
        content: [
          {
            nodeType: INLINES.HYPERLINK,
            data: {},
            content: [
              { nodeType: "text", value: "No URI", marks: [], data: {} },
            ],
          },
        ],
      };
      const doc = makeDoc(node);
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container).toHaveTextContent("No URI");
    });

    it("handles hyperlink with null data", () => {
      const node: any = {
        nodeType: BLOCKS.PARAGRAPH,
        data: {},
        content: [
          {
            nodeType: INLINES.HYPERLINK,
            data: null,
            content: [
              { nodeType: "text", value: "Null data", marks: [], data: {} },
            ],
          },
        ],
      };
      const doc = makeDoc(node);
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container).toHaveTextContent("Null data");
    });
  });

  describe("Superscript and subscript marks", () => {
    it("renders superscript text", () => {
      const doc = makeDoc(makeMarkedText("Sup", MARKS.SUPERSCRIPT));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("sup")).toHaveTextContent("Sup");
    });

    it("renders subscript text", () => {
      const doc = makeDoc(makeMarkedText("Sub", MARKS.SUBSCRIPT));
      const { container } = render(<>{renderContentfulRichText(doc)}</>);
      expect(container.querySelector("sub")).toHaveTextContent("Sub");
    });
  });
});

// ──────────────────────────────────────────────
// useContentfulRichText
// ──────────────────────────────────────────────
describe("useContentfulRichText", () => {
  it("returns null for null doc", () => {
    const { result } = renderHook(() => useContentfulRichText(null));
    expect(result.current).toBeNull();
  });

  it("returns null for undefined doc", () => {
    const { result } = renderHook(() => useContentfulRichText(undefined));
    expect(result.current).toBeNull();
  });

  it("returns null for doc without content array", () => {
    const bad = { nodeType: BLOCKS.DOCUMENT, data: {} } as any;
    const { result } = renderHook(() => useContentfulRichText(bad));
    expect(result.current).toBeNull();
  });

  it("renders paragraph content", () => {
    const doc = makeDoc(makeParagraph("Hook test"));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container).toHaveTextContent("Hook test");
  });

  it("renders headings h1-h6", () => {
    const doc = makeDoc(
      makeHeading(1, "H1"),
      makeHeading(2, "H2"),
      makeHeading(3, "H3"),
      makeHeading(4, "H4"),
      makeHeading(5, "H5"),
      makeHeading(6, "H6")
    );
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("h1")).toHaveTextContent("H1");
    expect(container.querySelector("h2")).toHaveTextContent("H2");
    expect(container.querySelector("h3")).toHaveTextContent("H3");
    expect(container.querySelector("h4")).toHaveTextContent("H4");
    expect(container.querySelector("h5")).toHaveTextContent("H5");
    expect(container.querySelector("h6")).toHaveTextContent("H6");
  });

  it("renders unordered list", () => {
    const doc = makeDoc(makeList(false, ["Item A", "Item B"]));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("ul")).toBeInTheDocument();
    expect(container.querySelectorAll("li")).toHaveLength(2);
  });

  it("renders ordered list", () => {
    const doc = makeDoc(makeList(true, ["First", "Second"]));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("ol")).toBeInTheDocument();
  });

  it("renders blockquote", () => {
    const doc = makeDoc(makeBlockquote("Quote text"));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("blockquote")).toHaveTextContent(
      "Quote text"
    );
  });

  it("renders hyperlink with external target", () => {
    const doc = makeDoc(makeHyperlink("https://ext.com", "External"));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    const a = container.querySelector("a")!;
    expect(a).toHaveAttribute("target", "_blank");
  });

  it("renders hyperlink with internal target", () => {
    const doc = makeDoc(makeHyperlink("/page", "Internal"));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    const a = container.querySelector("a")!;
    expect(a).not.toHaveAttribute("target");
  });

  it("renders embedded asset with image", () => {
    const doc = makeDoc(
      makeEmbeddedAsset("//images.ctfl.net/photo.jpg", "Photo")
    );
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    const img = container.querySelector("img")!;
    expect(img).toHaveAttribute("src", "https://images.ctfl.net/photo.jpg");
    expect(img).toHaveAttribute("alt", "Photo");
  });

  it("renders embedded asset with absolute URL", () => {
    const doc = makeDoc(makeEmbeddedAsset("https://cdn.example.com/img.png"));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    const img = container.querySelector("img")!;
    expect(img).toHaveAttribute("src", "https://cdn.example.com/img.png");
  });

  it("returns null for embedded asset without URL", () => {
    const doc = makeDoc(makeEmbeddedAsset(undefined));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("img")).not.toBeInTheDocument();
  });

  it("renders embedded asset using target.url when fields.file.url is missing", () => {
    const node: any = {
      nodeType: BLOCKS.EMBEDDED_ASSET,
      data: {
        target: {
          url: "https://direct.com/image.png",
          fields: { title: "Direct" },
        },
      },
      content: [],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("img")).toHaveAttribute(
      "src",
      "https://direct.com/image.png"
    );
  });

  it("renders embedded entry with callout type", () => {
    const doc = makeDoc(
      makeEmbeddedEntry("callout", { title: "Notice", body: "Important info" })
    );
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("aside")).toBeInTheDocument();
    expect(container).toHaveTextContent("Notice");
    expect(container).toHaveTextContent("Important info");
  });

  it("returns null for embedded entry with unknown type", () => {
    const doc = makeDoc(makeEmbeddedEntry("unknown", { title: "X" }));
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("aside")).not.toBeInTheDocument();
  });

  it("renders inline entry with componentCheckList type", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        makeInlineEntry("componentCheckList", { title: "Checklist Item" }),
      ],
    });
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container).toHaveTextContent("Checklist Item");
  });

  it("renders inline entry with unknown type showing title", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [makeInlineEntry("other", { title: "Other Entry" })],
    });
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container).toHaveTextContent("Other Entry");
  });

  it("renders inline entry with unknown type and missing title as empty", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [makeInlineEntry("other", {})],
    });
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    const spans = container.querySelectorAll("span");
    const emptySpan = Array.from(spans).find(s => s.textContent === "");
    expect(emptySpan).toBeDefined();
  });

  it("merges custom mark options", () => {
    const doc = makeDoc(makeMarkedText("Custom bold", MARKS.BOLD));
    const customOpts = {
      renderMark: {
        [MARKS.BOLD]: (text: any) => <b data-testid="custom-bold">{text}</b>,
      },
    };
    const { result } = renderHook(() => useContentfulRichText(doc, customOpts));
    render(<>{result.current}</>);
    expect(screen.getByTestId("custom-bold")).toHaveTextContent("Custom bold");
  });

  it("supports optional bold class name", () => {
    const doc = makeDoc(makeMarkedText("Styled bold", MARKS.BOLD));
    const { result } = renderHook(() =>
      useContentfulRichText(doc, undefined, { boldClassName: "label3" })
    );
    const { container } = render(<>{result.current}</>);
    const strong = container.querySelector("strong")!;
    expect(strong).toHaveClass("label3");
    expect(strong).toHaveTextContent("Styled bold");
  });

  it("keeps custom renderMark bold precedence over boldClassName", () => {
    const doc = makeDoc(makeMarkedText("Priority", MARKS.BOLD));
    const customOpts = {
      renderMark: {
        [MARKS.BOLD]: (text: any) => (
          <b data-testid="custom-priority-bold">{text}</b>
        ),
      },
    };
    const { result } = renderHook(() =>
      useContentfulRichText(doc, customOpts, { boldClassName: "label3" })
    );
    render(<>{result.current}</>);
    expect(screen.getByTestId("custom-priority-bold")).toHaveTextContent(
      "Priority"
    );
  });

  it("memoizes result for same doc reference", () => {
    const doc = makeDoc(makeParagraph("Memoized"));
    const { result, rerender } = renderHook(
      ({ d }) => useContentfulRichText(d),
      { initialProps: { d: doc } }
    );
    const first = result.current;
    rerender({ d: doc });
    expect(result.current).toBe(first);
  });

  it("recomputes when doc changes", () => {
    const doc1 = makeDoc(makeParagraph("First"));
    const doc2 = makeDoc(makeParagraph("Second"));
    const { result, rerender } = renderHook(
      ({ d }) => useContentfulRichText(d),
      { initialProps: { d: doc1 as Document | null } }
    );
    const first = result.current;
    rerender({ d: doc2 });
    expect(result.current).not.toBe(first);
  });

  it("transitions from null doc to valid doc", () => {
    const { result, rerender } = renderHook(
      ({ d }) => useContentfulRichText(d),
      { initialProps: { d: null as Document | null } }
    );
    expect(result.current).toBeNull();
    rerender({ d: makeDoc(makeParagraph("Now visible")) });
    const { container } = render(<>{result.current}</>);
    expect(container).toHaveTextContent("Now visible");
  });
});

// ──────────────────────────────────────────────
// renderContentfulRichTextTable
// ──────────────────────────────────────────────
describe("renderContentfulRichTextTable", () => {
  it("returns null for null doc", () => {
    expect(renderContentfulRichTextTable(null)).toBeNull();
  });

  it("returns null for undefined doc", () => {
    expect(renderContentfulRichTextTable(undefined)).toBeNull();
  });

  it("returns null for doc without content array", () => {
    const bad = { nodeType: BLOCKS.DOCUMENT, data: {} } as any;
    expect(renderContentfulRichTextTable(bad)).toBeNull();
  });

  it("renders table with rows", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_HEADER_CELL,
              data: {},
              content: [makeParagraph("Header")],
            },
            {
              nodeType: BLOCKS.TABLE_HEADER_CELL,
              data: {},
              content: [makeParagraph("Header 2")],
            },
          ],
        },
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("Cell 1")],
            },
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("Cell 2")],
            },
          ],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(container.querySelector("table")).toBeInTheDocument();
    expect(container.querySelectorAll("th")).toHaveLength(2);
    expect(container.querySelectorAll("td")).toHaveLength(2);
  });

  it("renders check_circle icon for 'yes' cell value", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("yes")],
            },
          ],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(
      container.querySelector('[data-testid="icon-check_circle"]')
    ).toBeInTheDocument();
  });

  it("renders cancel icon for 'no' cell value", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("no")],
            },
          ],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(
      container.querySelector('[data-testid="icon-cancel"]')
    ).toBeInTheDocument();
  });

  it("renders text content for non-yes/no cell values", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("Custom text")],
            },
          ],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(container).toHaveTextContent("Custom text");
    expect(
      container.querySelector('[data-testid="icon-check_circle"]')
    ).not.toBeInTheDocument();
    expect(
      container.querySelector('[data-testid="icon-cancel"]')
    ).not.toBeInTheDocument();
  });

  it("renders bold with table-specific class", () => {
    const doc = makeDoc(makeMarkedText("Table bold", MARKS.BOLD));
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    const strong = container.querySelector("strong")!;
    expect(strong).toHaveClass("label4");
    expect(strong).toHaveClass("md:label2");
  });

  it("renders paragraph as fragment (no wrapper div)", () => {
    const doc = makeDoc(makeParagraph("Fragment text"));
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    // Should NOT have a div.body3 wrapper like the default options
    expect(container.querySelector("div.body3")).not.toBeInTheDocument();
    expect(container).toHaveTextContent("Fragment text");
  });

  it("renders inline embedded checklist entry from links", () => {
    const entryId = "entry-123";
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: entryId } } },
          content: [],
        },
      ],
    });
    const links = {
      entries: {
        inline: [
          {
            sys: { id: entryId },
            __typename: "ComponentCheckList",
            list: {
              items: [{ checkListTitle: null }],
            },
          },
        ],
      },
    };
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, links)}</>
    );
    expect(
      container.querySelector('[data-testid="checklist"]')
    ).toBeInTheDocument();
  });

  it("renders inline entry title for non-checklist type", () => {
    const entryId = "entry-456";
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: entryId } } },
          content: [],
        },
      ],
    });
    const links = {
      entries: {
        inline: [
          {
            sys: { id: entryId },
            __typename: "OtherType",
            title: "Other Title",
          },
        ],
      },
    };
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, links)}</>
    );
    expect(container).toHaveTextContent("Other Title");
  });

  it("returns null for inline entry not found in links", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: "missing" } } },
          content: [],
        },
      ],
    });
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, { entries: { inline: [] } })}</>
    );
    // Should render the paragraph wrapper but no entry content
    expect(container.textContent).toBe("");
  });

  it("renders scrollable table with >2 columns", () => {
    const headerRow = {
      nodeType: BLOCKS.TABLE_ROW,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_HEADER_CELL,
          data: {},
          content: [makeParagraph("A")],
        },
        {
          nodeType: BLOCKS.TABLE_HEADER_CELL,
          data: {},
          content: [makeParagraph("B")],
        },
        {
          nodeType: BLOCKS.TABLE_HEADER_CELL,
          data: {},
          content: [makeParagraph("C")],
        },
      ],
    };
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [headerRow],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(container.querySelector("table")?.className).toContain(
      "min-w-[100.1%]"
    );
  });

  it("renders non-scrollable table with <=2 columns", () => {
    const headerRow = {
      nodeType: BLOCKS.TABLE_ROW,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_HEADER_CELL,
          data: {},
          content: [makeParagraph("A")],
        },
        {
          nodeType: BLOCKS.TABLE_HEADER_CELL,
          data: {},
          content: [makeParagraph("B")],
        },
      ],
    };
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [headerRow],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(container.querySelector("table")?.className).toContain("min-w-full");
  });

  it("renders table cells with non-scrollable layout (node.parent not available)", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_HEADER_CELL,
              data: {},
              content: [makeParagraph("H1")],
            },
            {
              nodeType: BLOCKS.TABLE_HEADER_CELL,
              data: {},
              content: [makeParagraph("H2")],
            },
            {
              nodeType: BLOCKS.TABLE_HEADER_CELL,
              data: {},
              content: [makeParagraph("H3")],
            },
          ],
        },
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("C1")],
            },
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("C2")],
            },
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("C3")],
            },
          ],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    const tds = container.querySelectorAll("td");
    expect(tds.length).toBe(3);
    // node.parent is not set by documentToReactComponents, so isScrollable is always false
    tds.forEach(td => {
      expect(td.className).toContain("w-1/4");
    });
  });

  it("renders non-scrollable table cells with w-1/4 for <=2 columns", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_HEADER_CELL,
              data: {},
              content: [makeParagraph("H1")],
            },
            {
              nodeType: BLOCKS.TABLE_HEADER_CELL,
              data: {},
              content: [makeParagraph("H2")],
            },
          ],
        },
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("D1")],
            },
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [makeParagraph("D2")],
            },
          ],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    const tds = container.querySelectorAll("td");
    tds.forEach(td => {
      expect(td.className).toContain("w-1/4");
    });
  });

  it("renders checklist with empty items when list has no items", () => {
    const entryId = "entry-empty";
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: entryId } } },
          content: [],
        },
      ],
    });
    const links = {
      entries: {
        inline: [
          {
            sys: { id: entryId },
            __typename: "ComponentCheckList",
            list: undefined,
          },
        ],
      },
    };
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, links)}</>
    );
    expect(
      container.querySelector('[data-testid="checklist"]')
    ).toBeInTheDocument();
  });

  it("renders non-checklist inline entry with empty title", () => {
    const entryId = "entry-no-title";
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: entryId } } },
          content: [],
        },
      ],
    });
    const links = {
      entries: {
        inline: [
          {
            sys: { id: entryId },
            __typename: "OtherType",
            title: "",
          },
        ],
      },
    };
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, links)}</>
    );
    const span = container.querySelector("span");
    expect(span?.textContent).toBe("");
  });

  it("renders scrollable table header cells when node.parent has >2 children", () => {
    // Build row with 3 header cells and set parent on each cell
    const headerCells = [
      {
        nodeType: BLOCKS.TABLE_HEADER_CELL,
        data: {},
        content: [makeParagraph("H1")],
      },
      {
        nodeType: BLOCKS.TABLE_HEADER_CELL,
        data: {},
        content: [makeParagraph("H2")],
      },
      {
        nodeType: BLOCKS.TABLE_HEADER_CELL,
        data: {},
        content: [makeParagraph("H3")],
      },
    ];
    const row = {
      nodeType: BLOCKS.TABLE_ROW,
      data: {},
      content: headerCells,
    };
    // Set parent on each cell so isScrollable = true
    headerCells.forEach(cell => {
      (cell as any).parent = row;
    });
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [row],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    const ths = container.querySelectorAll("th");
    expect(ths.length).toBe(3);
    ths.forEach(th => {
      expect(th.className).toContain("sticky");
    });
  });

  it("renders scrollable table data cells when node.parent has >2 children", () => {
    const dataCells = [
      {
        nodeType: BLOCKS.TABLE_CELL,
        data: {},
        content: [makeParagraph("D1")],
      },
      {
        nodeType: BLOCKS.TABLE_CELL,
        data: {},
        content: [makeParagraph("D2")],
      },
      {
        nodeType: BLOCKS.TABLE_CELL,
        data: {},
        content: [makeParagraph("D3")],
      },
    ];
    const row = {
      nodeType: BLOCKS.TABLE_ROW,
      data: {},
      content: dataCells,
    };
    dataCells.forEach(cell => {
      (cell as any).parent = row;
    });
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [row],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    const tds = container.querySelectorAll("td");
    expect(tds.length).toBe(3);
    tds.forEach(td => {
      expect(td.className).toContain("min-w-[50vw]");
    });
  });

  it("renders scrollable table cell with 'yes' value and isScrollable true", () => {
    const dataCells = [
      {
        nodeType: BLOCKS.TABLE_CELL,
        data: {},
        content: [makeParagraph("yes")],
      },
      {
        nodeType: BLOCKS.TABLE_CELL,
        data: {},
        content: [makeParagraph("no")],
      },
      {
        nodeType: BLOCKS.TABLE_CELL,
        data: {},
        content: [makeParagraph("other")],
      },
    ];
    const row = {
      nodeType: BLOCKS.TABLE_ROW,
      data: {},
      content: dataCells,
    };
    dataCells.forEach(cell => {
      (cell as any).parent = row;
    });
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [row],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(
      container.querySelector('[data-testid="icon-check_circle"]')
    ).toBeInTheDocument();
    expect(
      container.querySelector('[data-testid="icon-cancel"]')
    ).toBeInTheDocument();
    const tds = container.querySelectorAll("td");
    tds.forEach(td => {
      expect(td.className).toContain("min-w-[50vw]");
    });
  });

  it("renders cell with empty content array gracefully", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [
            {
              nodeType: BLOCKS.TABLE_CELL,
              data: {},
              content: [],
            },
          ],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    const td = container.querySelector("td");
    expect(td).toBeInTheDocument();
  });

  it("renders superscript mark", () => {
    const doc = makeDoc(makeMarkedText("Sup", MARKS.SUPERSCRIPT));
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(container.querySelector("sup")).toHaveTextContent("Sup");
  });

  it("renders subscript mark", () => {
    const doc = makeDoc(makeMarkedText("Sub", MARKS.SUBSCRIPT));
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(container.querySelector("sub")).toHaveTextContent("Sub");
  });

  it("renders table when first row content is empty", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.TABLE,
      data: {},
      content: [
        {
          nodeType: BLOCKS.TABLE_ROW,
          data: {},
          content: [],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(container.querySelector("table")).toBeInTheDocument();
    expect(container.querySelector("table")?.className).toContain("min-w-full");
  });

  it("renders inline entry when links is undefined", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: "any" } } },
          content: [],
        },
      ],
    });
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, undefined)}</>
    );
    expect(container).toBeInTheDocument();
  });

  it("renders inline entry when links has no entries", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: "any" } } },
          content: [],
        },
      ],
    });
    const { container } = render(<>{renderContentfulRichTextTable(doc, {})}</>);
    expect(container).toBeInTheDocument();
  });

  it("renders inline entry when links.entries has no inline", () => {
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: "any" } } },
          content: [],
        },
      ],
    });
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, { entries: {} })}</>
    );
    expect(container).toBeInTheDocument();
  });

  it("renders checklist entry with list but no items", () => {
    const entryId = "entry-no-items";
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: entryId } } },
          content: [],
        },
      ],
    });
    const links = {
      entries: {
        inline: [
          {
            sys: { id: entryId },
            __typename: "ComponentCheckList",
            list: { items: null },
          },
        ],
      },
    };
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, links)}</>
    );
    expect(
      container.querySelector('[data-testid="checklist"]')
    ).toBeInTheDocument();
  });

  it("renders checklist entry with no list property", () => {
    const entryId = "entry-no-list";
    const doc = makeDoc({
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: { sys: { id: entryId } } },
          content: [],
        },
      ],
    });
    const links = {
      entries: {
        inline: [
          {
            sys: { id: entryId },
            __typename: "ComponentCheckList",
          },
        ],
      },
    };
    const { container } = render(
      <>{renderContentfulRichTextTable(doc, links)}</>
    );
    expect(
      container.querySelector('[data-testid="checklist"]')
    ).toBeInTheDocument();
  });

  it("handles hyperlink with missing uri in table", () => {
    const node: any = {
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.HYPERLINK,
          data: {},
          content: [{ nodeType: "text", value: "NoUri", marks: [], data: {} }],
        },
      ],
    };
    const doc = makeDoc(node);
    const { container } = render(<>{renderContentfulRichTextTable(doc)}</>);
    expect(container).toHaveTextContent("NoUri");
  });
});

// ──────────────────────────────────────────────
// Additional defaultOptions coverage
// ──────────────────────────────────────────────
describe("defaultOptions (via useContentfulRichText)", () => {
  it("renders embedded asset with description fallback alt", () => {
    const node: any = {
      nodeType: BLOCKS.EMBEDDED_ASSET,
      data: {
        target: {
          fields: {
            file: { url: "https://cdn.com/img.png" },
            description: "Desc alt",
          },
        },
      },
      content: [],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("img")).toHaveAttribute("alt", "Desc alt");
  });

  it("renders embedded asset with default alt when no title or description", () => {
    const node: any = {
      nodeType: BLOCKS.EMBEDDED_ASSET,
      data: {
        target: {
          fields: {
            file: { url: "https://cdn.com/img.png" },
          },
        },
      },
      content: [],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("img")).toHaveAttribute(
      "alt",
      "Embedded asset"
    );
  });

  it("renders inline entry with missing fields as empty span", () => {
    const node: any = {
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: {
            target: {
              sys: { contentType: { sys: { id: "unknown" } } },
              fields: {},
            },
          },
          content: [],
        },
      ],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    const spans = container.querySelectorAll("span");
    const emptySpan = Array.from(spans).find(s => s.textContent === "");
    expect(emptySpan).toBeDefined();
  });

  // Cover ?.  null short-circuit branches in defaultOptions
  it("handles embedded asset with missing target fields gracefully", () => {
    const node: any = {
      nodeType: BLOCKS.EMBEDDED_ASSET,
      data: { target: null },
      content: [],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("img")).not.toBeInTheDocument();
  });

  it("handles embedded asset with missing data gracefully", () => {
    const node: any = {
      nodeType: BLOCKS.EMBEDDED_ASSET,
      data: {},
      content: [],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("img")).not.toBeInTheDocument();
  });

  it("handles embedded asset with fields but no file", () => {
    const node: any = {
      nodeType: BLOCKS.EMBEDDED_ASSET,
      data: {
        target: {
          fields: { title: "No file" },
        },
      },
      content: [],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("img")).not.toBeInTheDocument();
  });

  it("handles embedded entry with missing data target", () => {
    const node: any = {
      nodeType: BLOCKS.EMBEDDED_ENTRY,
      data: { target: null },
      content: [],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("aside")).not.toBeInTheDocument();
  });

  it("handles embedded entry with no sys contentType", () => {
    const node: any = {
      nodeType: BLOCKS.EMBEDDED_ENTRY,
      data: {
        target: {
          sys: {},
          fields: { title: "No CT" },
        },
      },
      content: [],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container.querySelector("aside")).not.toBeInTheDocument();
  });

  it("handles inline entry with missing data target", () => {
    const node: any = {
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: { target: null },
          content: [],
        },
      ],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container).toBeInTheDocument();
  });

  it("handles inline entry with no sys on target", () => {
    const node: any = {
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: {
            target: { fields: { title: "No sys" } },
          },
          content: [],
        },
      ],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container).toHaveTextContent("No sys");
  });

  it("handles inline entry with no contentType in sys", () => {
    const node: any = {
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: {
            target: {
              sys: {},
              fields: { title: "No CT" },
            },
          },
          content: [],
        },
      ],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container).toHaveTextContent("No CT");
  });

  it("handles inline entry with null fields", () => {
    const node: any = {
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.EMBEDDED_ENTRY,
          data: {
            target: {
              sys: { contentType: { sys: { id: "other" } } },
            },
          },
          content: [],
        },
      ],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    const spans = container.querySelectorAll("span");
    const emptySpan = Array.from(spans).find(s => s.textContent === "");
    expect(emptySpan).toBeDefined();
  });

  it("handles hyperlink with missing data", () => {
    const node: any = {
      nodeType: BLOCKS.PARAGRAPH,
      data: {},
      content: [
        {
          nodeType: INLINES.HYPERLINK,
          data: {},
          content: [{ nodeType: "text", value: "Link", marks: [], data: {} }],
        },
      ],
    };
    const doc = makeDoc(node);
    const { result } = renderHook(() => useContentfulRichText(doc));
    const { container } = render(<>{result.current}</>);
    expect(container).toHaveTextContent("Link");
  });
});
