import { describe, expect, it, vi } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";

import { RootErrorBoundary, installGlobalErrorLogging } from "./RootErrorBoundary";

function ThrowingView(): JSX.Element {
  throw new Error("boom from render");
}

describe("RootErrorBoundary", () => {
  it("renders a visible fallback when the app render tree throws", () => {
    const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
    try {
      render(
        <RootErrorBoundary>
          <ThrowingView />
        </RootErrorBoundary>,
      );

      expect(screen.getByRole("alert")).toBeTruthy();
      expect(screen.getByText("IdeA a rencontré une erreur d'affichage.")).toBeTruthy();
      expect(screen.getByText(/boom from render/)).toBeTruthy();
    } finally {
      consoleError.mockRestore();
    }
  });

  it("offers a reload action from the fallback", () => {
    const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
    const reload = vi.fn();
    const originalLocation = window.location;
    try {
      Object.defineProperty(window, "location", {
        configurable: true,
        value: { ...originalLocation, reload },
      });

      render(
        <RootErrorBoundary>
          <ThrowingView />
        </RootErrorBoundary>,
      );

      fireEvent.click(screen.getByRole("button", { name: "Recharger" }));
      expect(reload).toHaveBeenCalledOnce();
    } finally {
      Object.defineProperty(window, "location", {
        configurable: true,
        value: originalLocation,
      });
      consoleError.mockRestore();
    }
  });
});

describe("installGlobalErrorLogging", () => {
  it("installs global error logging only once", () => {
    const addEventListener = vi.spyOn(window, "addEventListener");
    const previousFlag = window.__IDEA_GLOBAL_ERROR_LOGGING__;
    try {
      window.__IDEA_GLOBAL_ERROR_LOGGING__ = undefined;
      installGlobalErrorLogging();
      installGlobalErrorLogging();

      expect(
        addEventListener.mock.calls.filter(([event]) => event === "error"),
      ).toHaveLength(1);
      expect(
        addEventListener.mock.calls.filter(([event]) => event === "unhandledrejection"),
      ).toHaveLength(1);
    } finally {
      window.__IDEA_GLOBAL_ERROR_LOGGING__ = previousFlag;
      addEventListener.mockRestore();
    }
  });
});
