import { afterEach, expect, test, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { createMemoryRouter, Link, RouterProvider } from "react-router-dom";

import { UnsavedChangesDialog } from "./unsaved-changes-dialog";
import { useUnsavedChanges } from "./use-unsaved-changes";
import "../i18n";

function Editor({ active }: { active: boolean }) {
  const blocker = useUnsavedChanges(active);
  return (
    <div>
      <h1>Editor</h1>
      <Link to="/other">go elsewhere</Link>
      <UnsavedChangesDialog blocker={blocker} />
    </div>
  );
}

function renderGuard(active: boolean) {
  const router = createMemoryRouter(
    [
      { path: "/", element: <Editor active={active} /> },
      { path: "/other", element: <h1>Other page</h1> },
    ],
    { initialEntries: ["/"] },
  );

  return render(<RouterProvider router={router} />);
}

afterEach(() => {
  vi.restoreAllMocks();
});

test("dirty nav shows the dialog and Keep editing stays", async () => {
  renderGuard(true);
  await userEvent.click(screen.getByRole("link", { name: /go elsewhere/i }));

  expect(await screen.findByText("Discard unsaved changes?")).toBeInTheDocument();

  await userEvent.click(screen.getByRole("button", { name: "Keep editing" }));

  expect(screen.getByRole("heading", { name: "Editor" })).toBeInTheDocument();
  expect(screen.queryByText("Other page")).not.toBeInTheDocument();
});

test("dirty nav with Discard proceeds to the target route", async () => {
  renderGuard(true);
  await userEvent.click(screen.getByRole("link", { name: /go elsewhere/i }));

  await userEvent.click(await screen.findByRole("button", { name: "Discard" }));

  expect(await screen.findByText("Other page")).toBeInTheDocument();
});

test("clean form navigates without the dialog", async () => {
  renderGuard(false);
  await userEvent.click(screen.getByRole("link", { name: /go elsewhere/i }));

  expect(await screen.findByText("Other page")).toBeInTheDocument();
  expect(screen.queryByText("Discard unsaved changes?")).not.toBeInTheDocument();
});

test("registers a beforeunload listener only when active", () => {
  const addSpy = vi.spyOn(window, "addEventListener");
  const removeSpy = vi.spyOn(window, "removeEventListener");

  const { unmount } = renderGuard(true);

  expect(addSpy.mock.calls.some(([type]) => type === "beforeunload")).toBe(true);

  unmount();

  expect(removeSpy.mock.calls.some(([type]) => type === "beforeunload")).toBe(true);
});

test("does not register beforeunload when inactive", () => {
  const addSpy = vi.spyOn(window, "addEventListener");

  renderGuard(false);

  expect(addSpy.mock.calls.some(([type]) => type === "beforeunload")).toBe(false);
});
