// @vitest-environment happy-dom
// Pure presentation component — no React Query, no async work — but
// we still set IS_REACT_ACT_ENVIRONMENT to match the project convention
// (ClaimDrawerHeader.test.tsx, KeyboardCheatsheet.test.tsx, etc.).
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;

import React, { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { describe, expect, it } from "vitest";
import { Card, CardTitle } from "./card";

function renderIntoContainer(element: React.ReactElement): {
  container: HTMLDivElement;
  unmount: () => void;
} {
  const container = document.createElement("div");
  document.body.appendChild(container);
  const root: Root = createRoot(container);
  act(() => {
    root.render(element);
  });
  return {
    container,
    unmount: () => {
      act(() => root.unmount());
      container.remove();
    },
  };
}

describe("CardTitle", () => {
  it("test_defaults_to_div_when_no_as_prop_provided", () => {
    // Backwards-compat: existing Dashboard / Upload / Providers
    // call-sites write `<CardTitle>...</CardTitle>` and the rendered
    // element must stay a <div>. Touching this contract would
    // regress every consumer in the codebase.
    const { container, unmount } = renderIntoContainer(
      <Card>
        <CardTitle>Default</CardTitle>
      </Card>
    );

    // No `as` prop → <div>.
    const title = container.querySelector("h2, h3, h4, h5, h6");
    expect(title).toBeNull();
    // The title text should still appear (now in a <div>).
    const text = container.textContent ?? "";
    expect(text).toContain("Default");

    unmount();
  });

  it("test_renders_h2_when_as_h2", () => {
    // Polymorphic `as` prop: consumers can request the semantic level
    // they need. Here we render <CardTitle as="h2"> and assert the
    // resulting element is an <h2> with the title text inside.
    const { container, unmount } = renderIntoContainer(
      <Card>
        <CardTitle as="h2">Dashboard section</CardTitle>
      </Card>
    );

    const h2 = container.querySelector("h2");
    expect(h2).not.toBeNull();
    expect(h2?.textContent).toBe("Dashboard section");

    // And no other heading level should appear inside this card —
    // confirms the polymorphic forwardRef actually swaps the element
    // type rather than rendering both.
    expect(container.querySelector("h1")).toBeNull();
    expect(container.querySelector("h3")).toBeNull();

    unmount();
  });

  it("test_renders_h3_when_as_h3", () => {
    // Verify the generic accepts heading levels other than h2 — the
    // Dashboard uses h2 for top-level cards but inner cards may want
    // h3 for proper document outline nesting.
    const { container, unmount } = renderIntoContainer(
      <Card>
        <CardTitle as="h3">Sub-section</CardTitle>
      </Card>
    );

    const h3 = container.querySelector("h3");
    expect(h3).not.toBeNull();
    expect(h3?.textContent).toBe("Sub-section");

    unmount();
  });

  it("test_preserves_className_when_using_as", () => {
    // The base className ("text-base font-semibold ...") must still
    // apply when `as` is provided — the polymorphic swap shouldn't
    // drop styling.
    const { container, unmount } = renderIntoContainer(
      <Card>
        <CardTitle as="h2" className="custom-class">
          Styled
        </CardTitle>
      </Card>
    );

    const h2 = container.querySelector("h2");
    expect(h2).not.toBeNull();
    const cls = h2?.className ?? "";
    expect(cls).toContain("font-semibold");
    expect(cls).toContain("custom-class");

    unmount();
  });

  it("test_forwards_ref_to_polymorphic_element", () => {
    // `React.forwardRef` must hand the ref to whichever element `as`
    // resolves to — otherwise consumers can't focus / measure the
    // heading after rendering. We use `createRef` (typed at the
    // call site as the polymorphic target element) rather than a
    // callback ref so TypeScript can fully resolve the generic
    // through the polymorphic cast — a callback ref here would
    // end up typed as `never` because the generic `E` parameter
    // can't unify through the call-signature cast on the component.
    const ref = React.createRef<HTMLHeadingElement>();
    const { unmount } = renderIntoContainer(
      <Card>
        <CardTitle as="h2" ref={ref}>
          With ref
        </CardTitle>
      </Card>
    );

    expect(ref.current).not.toBeNull();
    expect(ref.current?.tagName).toBe("H2");
    expect(ref.current?.textContent).toBe("With ref");

    unmount();
  });
});