/** @jsxImportSource react */
import { afterEach, expect, test } from "bun:test";
import { Window } from "happy-dom";
import { act } from "react";
import { createRoot } from "react-dom/client";
import { IslandErrorBoundary } from "../src/error-boundary.tsx";

// React error boundaries only engage during client rendering — the server
// renderers rethrow. Islands are client-only, so this is also how they run.
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;

function Boom(): never {
  throw new Error("chart exploded");
}

function mountHost() {
  const window = new Window();
  (globalThis as any).window = window;
  (globalThis as any).document = window.document;
  const container = window.document.createElement("div");
  window.document.body.appendChild(container);
  return { window, container: container as unknown as HTMLElement };
}

const silenced: Array<() => void> = [];

afterEach(() => {
  for (const restore of silenced.splice(0)) restore();
});

function silenceExpectedErrors() {
  const original = console.error;
  console.error = () => {};
  silenced.push(() => {
    console.error = original;
  });
}

test("renders children when nothing throws", async () => {
  const { container } = mountHost();
  const root = createRoot(container);

  await act(async () => {
    root.render(
      <IslandErrorBoundary name="Chart" development={false}>
        <p>ok</p>
      </IslandErrorBoundary>,
    );
  });

  expect(container.innerHTML).toBe("<p>ok</p>");
});

test("contains a thrown error and shows details in development", async () => {
  silenceExpectedErrors();
  const { container } = mountHost();
  const root = createRoot(container);

  await act(async () => {
    root.render(
      <IslandErrorBoundary name="Chart" development>
        <Boom />
      </IslandErrorBoundary>,
    );
  });

  expect(container.innerHTML).toContain("Chart");
  expect(container.innerHTML).toContain("chart exploded");
});

test("renders nothing in production when an island throws", async () => {
  silenceExpectedErrors();
  const { container } = mountHost();
  const root = createRoot(container);

  await act(async () => {
    root.render(
      <IslandErrorBoundary name="Chart" development={false}>
        <Boom />
      </IslandErrorBoundary>,
    );
  });

  expect(container.innerHTML).toBe("");
});

test("a crashed island does not remove sibling server markup", async () => {
  silenceExpectedErrors();
  const { window, container } = mountHost();
  const sibling = window.document.createElement("p");
  sibling.textContent = "server rendered";
  window.document.body.appendChild(sibling);

  const root = createRoot(container);
  await act(async () => {
    root.render(
      <IslandErrorBoundary name="Chart" development={false}>
        <Boom />
      </IslandErrorBoundary>,
    );
  });

  expect(window.document.body.textContent).toContain("server rendered");
});
